PageSourceSearch

https://www.aiguesdematadepera.cat/combo?browserId=chrome&minifier…&/o/cxov-tramite-ui/js/validations.js

js aiguesdematadepera.cat collected 2026-10-06 09:32:44 UTC 40,094 bytes, 1,422 lines download raw bytes

1
2if (typeof AcDocuments === "undefined") {
3
4	function AcDocuments(step, config) {
5		var defaultConfig = {
6			rootSelector: '[data-ov-widget="file-upload"]',
7			msg: {
8			},
9			urls: {
10				addFile: null,
11				deleteFile: null
12			},
13			validate: {
14				minSizes: {
15					'image/jpeg': 300,
16					'image/png': 80,
17					'application/pdf': 5120,
18					'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 10000,
19					'application/msword': 20000
20				}
21			},
22			animate: {
23				duration: {
24					show: 400,
25					hide: 900
26				},
27				timeout: {
28					hide: 4000
29				}
30			},
31			reloadOnAuthError: false
32		};
33
34		// AcTramiteStep instance
35		this.step = step;
36		this.config = jQuery.extend({}, defaultConfig, config || {});
37		this.pendingOperations = 0;
38
39		if (this.findElements().length != 1) {
40			throw new Error('Invalid root selector: ' + this.config.rootSelector);
41		}
42	}
43
44	AcDocuments.prototype.reloadIfAuthError = function(timeout) {
45		if (this.config.reloadOnAuthError === true) {
46			// fixme: Reload should not be done on last page
47			// setTimeout(window.location.reload, timeout ? timeout : 500);
48		}
49	}
50
51	AcDocuments.prototype.add = function(id, fileName, mimeType) {
52		this.documents[id] = {
53			file: fileName,
54			mime: mimeType
55		};
56	}
57
58	AcDocuments.prototype.init = function() {
59		if (typeof this.config.init === "function") {
60			this.config.init(this);
61		}
62	}
63
64	AcDocuments.prototype.findElements = function(groupId, selector) {
65		var result = jQuery(this.config.rootSelector).first();
66
67		if (typeof groupId === "string" && groupId.trim()) {
68			result = result.find('[data-ov-file-upload-group-id="' + groupId + '"]').first();
69		}
70
71		if (typeof selector === "string" && selector.trim()) {
72			result = result.find(selector.trim());
73		}
74
75		return result;
76	}
77
78	AcDocuments.prototype.findElement = function(groupId, selector) {
79		return this.findElements(groupId, selector).first();
80	}
81
82	AcDocuments.prototype.createFileElement = function(groupId, fileId, file) {
83		var fileElem = jQuery('<span class="fileImage"></span>');
84  		fileElem.attr("data-ov-file-upload-document-id", fileId);
85  		var icon = jQuery('<span class="document file-type-icon"></span>');
86
87  		if (this.isImage(file)) {
88  			icon.addClass('file-type-image');
89  			var img = document.createElement('img');
90  			img.src = URL.createObjectURL(file);
91  			img.alt = "";
92  			img.onLoad = function() {
93  				URL.revokeObjectURL(this.src);
94  			};
95  			icon.append(img);
96  		} else if (this.isPdf(file)) {
97  			icon.addClass('file-type-pdf');
98  		} else if (file && file.name.indexOf('.') >= 0) {
99  			var ext = file.name.toLowerCase().split('.').slice(-1);
100  			icon.addClass('file-type-' + ext);
101  		} else {
102  			icon.addClass('file-type-any');
103  		}
104
105  		fileElem.append(icon);
106  		fileElem.append(jQuery('<span class="name">' + file.name + '</span>'));
107  		fileElem.append(jQuery('<span class="progress"><span class="progress-bar"></span></span>'));
108
109  		this.findElement(groupId, ".fileList").append(fileElem);
110  		this.findElement(groupId, '.placeholder').addClass('hide');
111
112  		return fileElem;
113	}
114
115	AcDocuments.prototype.addFileElementDeleteLink = function(groupId, fileId, replaceJElem) {
116		var deleteButton = jQuery('<span class="delete action-button" aria-role="button">' + this.config.msg.eliminarFichero + '</span>');
117
118		if (typeof replaceJElem === "object") {
119			replaceJElem.replaceWith(deleteButton);
120		} else {
121			var fileElem = this.findElement(groupId, '[data-ov-file-upload-document-id="' + fileId + '"]');
122
123			if (fileElem.find('.delete').length > 0) {
124				return;
125			}
126
127			fileElem.append(deleteButton);
128		}
129
130		this.attachEventHandlers(deleteButton);
131	}
132
133	AcDocuments.prototype.deleteFileElement = function(groupId, fileId) {
134		var document = this.findElement(groupId, '[data-ov-file-upload-document-id="' + fileId + '"]');
135		document.find('.delete').off('click.dnd');
136		document.remove();
137
138		if (this.findElement(groupId, '.fileList .fileImage').length == 0) {
139			this.findElement(groupId, '.placeholder').removeClass('hide');
140		}
141	}
142
143	/**
144	 * Add a file details to the list after being processed in backend
145	 *
146	 * @param groupId Group Id
147	 * @param tmpFileId Unique file Id used to create the progress
148	 * @param fileData File details obtained from the call: fileData.id, fileData.simpleType, fileData.fileName,
149	 * fileData.mimeType, fileData.base64Thumbnail
150	 *
151	 */
152	AcDocuments.prototype.updateFileInList = function(groupId, tmpFileId, fileData) {
153
154		var fileElem = this.findElement(groupId, '[data-ov-file-upload-document-id="' + tmpFileId + '"]');
155
156		if (fileElem.length == 0) {
157			fileElem = this.createFileElement(groupId, fileData.id);
158		} else {
159			fileElem.attr('data-ov-file-upload-document-id', fileData.id);
160		}
161
162  		fileElem.find('.name').text(fileData.fileName);
163
164  		var icon = fileElem.find('.document.file-type-icon').addClass('.file-type-' + fileData.simpleType)
165  			.removeClass('.file-type-any');
166
167  		if (fileData.mimeType && fileData.base64Thumbnail) {
168  			var img = icon.find('img');
169  			var dataUrl = 'data:' + fileData.mimeType + ";base64," + fileData.base64Thumbnail;
170
171  			if (img.length === 0) {
172  				img = jQuery('<img src="' + dataUrl + '" alt="">');
173  				icon.append(img);
174  			} else {
175  				img.attr('src', dataUrl).removeClass('hide');
176  			}
177  		}
178
179  		return fileElem;
180	}
181
182	AcDocuments.prototype.attachEventHandlers = function(itemRootElem) {
183		var $this = this;
184		var targetElem = itemRootElem.find('.delete');
185
186		if (itemRootElem.hasClass('delete') && itemRootElem.hasClass('action-button')) {
187			targetElem = itemRootElem.first();
188		}
189
190		targetElem.off('click.dnd').on('click.dnd', function(ev){
191			$this.handleDeleteEvent(this, ev);
192		});
193	}
194
195	AcDocuments.prototype.handleDeleteEvent = function(domElem, ev) {
196		ev.stopPropagation();
197		ev.preventDefault();
198		var fileElem = jQuery(domElem).closest('[data-ov-file-upload-document-id]');
199		var fileId = fileElem.attr('data-ov-file-upload-document-id');
200
201		if (fileId) {
202			var groupId = this.getGroupId(domElem);
203			this.ajaxDeleteFile(groupId, fileId, fileElem);
204		}
205	}
206
207	AcDocuments.prototype.ajaxDeleteFile = function(groupId, fileId, fileElem) {
208		var $this = this;
209
210		var data = {};
211		data[this.step.config.nmsp + "groupId"] = groupId;
212		data[this.step.config.nmsp + "fileId"] = fileId;
213
214		$this.onBackgroundOperationStart();
215
216		jQuery.ajax({
217			url: this.config.url.deleteFile,
218			method: 'POST',
219			data: data,
220			cache: false,
221			dataType: 'json'
222		}).done(function(result, textStatus, jqXHR){
223			$this.internalFileDelete(groupId, fileElem, result.success, result.status);
224		}).fail(function(jqXHR, textStatus, errorThrown){
225			$this.internalFileDelete(groupId, null, false, textStatus);
226		}).always(function(){
227			$this.onBackgroundOperationEnd();
228		});
229	}
230
231	AcDocuments.prototype.internalFileDelete = function (groupId, fileElem, success, statusOrMsg){
232		if (success) {
233			fileElem.remove();
234			var fileList = this.findElements(groupId, '.fileList');
235			if (fileList.children().length == 0) {
236				fileList.parent().find('.placeholder').removeClass('hide').show();
237			}
238		} else {
239			if (statusOrMsg && statusOrMsg.code === "401") {
240				// Session token expired, must reload page
241				this.reloadIfAuthError();
242			}
243
244			this.showError(groupId, statusOrMsg);
245		}
246	}
247
248	AcDocuments.prototype.normalizeToFileArray = function (files) {
249		var result = [];
250
251		if (files instanceof Array || files instanceof FileList || files instanceof DataTransferItemList) {
252			for (var index = 0; index < files.length; index++) {
253				var file = files[index];
254
255				if (file instanceof DataTransferItem) {
256					file = file.getAsFile();
257				}
258
259				if (file instanceof File) {
260					result.push(file);
261				} else {
262					result = result.concat(this.normalizeToFileArray(file));
263				}
264			}
265		} else if (files instanceof DataTransferItem) {
266			result.push(files.getAsFile());
267		} else if (files instanceof File) {
268			result.push(files);
269		} else if (file) {
270			throw new Error("Cannot normalize to File[]: " + files);
271		}
272
273		return result;
274	}
275
276	AcDocuments.prototype.ajaxAddFiles = function(groupId, fileKey, fileValues, fileAddedCallback, finalCallback) {
277		var files = this.normalizeToFileArray(fileValues);
278		var count = files.length;
279		var order = 0;
280
281		for (var index = 0; index < files.length; index++) {
282			var file = files[index];
283			order++;
284
285			this.ajaxAddFile(groupId, fileKey, file, function(finalGroupId, finalFileId){
286				count--;
287
288				if (typeof fileAddedCallback === 'function') {
289					fileAddedCallback(finalGroupId, finalFileId, order, activeUploads);
290				}
291
292				if (count == 0 && typeof finalCallback === 'function') {
293					finalCallback(finalGroupId, finalFileId);
294				}
295			})
296		}
297	}
298
299	AcDocuments.prototype.generateFileId = function(groupId, file) {
300		return (new Date()).getTime() + "" + parseInt(Math.random() * 1000000);
301	}
302
303	AcDocuments.prototype.ajaxAddFile = function(groupId, fileKey, fileValue, finalCallback) {
304		var tmpFileId = this.generateFileId(groupId, fileValue);
305
306		var fileElem = this.createFileElement(groupId, tmpFileId, fileValue);
307		this.startProgressAnimation(fileElem);
308
309		var formData = new FormData();
310		formData.append(fileKey, fileValue)
311		formData.append(this.step.config.nmsp + "groupId", groupId);
312		formData.append(this.step.config.nmsp + "tmpFileId", tmpFileId);
313
314		var $this = this;
315
316		$this.onBackgroundOperationStart();
317
318		jQuery.ajax({
319			url: this.config.url.addFile,
320			method: 'POST',
321			data: formData,
322	        cache: false,
323	        contentType: false,
324	        processData: false,
325	        dataType: 'json',
326	        timeout: 50000,
327	        beforeSend: function() {
328
329	        }
330		}).done(function(result, textStatus, jqXHR) {
331			var data = result && result.success && result.data ? result.data : null;
332			$this.handleAjaxFileUploadResult(result.success && data, result.status, fileElem, groupId, tmpFileId, data, finalCallback);
333		}).fail(function(jqXHR, textStatus, errorThrown){
334			$this.handleAjaxFileUploadResult(false, textStatus, fileElem, groupId, tmpFileId, null, finalCallback);
335		}).always(function(){
336			$this.onBackgroundOperationEnd();
337		});
338	}
339
340	AcDocuments.prototype.handleAjaxFileUploadResult = function (success, statusOrMsg, fileElem, groupId, tmpFileId, data, finalCallback){
341		var $this = this;
342
343		this.stopProgressAnimation(fileElem, function(progress){
344			if (success) {
345				$this.updateFileInList(groupId, tmpFileId, data);
346				$this.addFileElementDeleteLink(groupId, data.id, progress);
347
348				if (typeof finalCallback === "function") {
349					finalCallback(groupId, data.id);
350				}
351			} else {
352				$this.deleteFileElement(groupId, tmpFileId);
353
354				if (statusOrMsg && statusOrMsg.code === "401") {
355					// Session token expired, must reload page
356					$this.reloadIfAuthError();
357				}
358
359				$this.showError(groupId, statusOrMsg);
360
361				if (typeof finalCallback === "function") {
362					finalCallback(groupId, undefined);
363				}
364			}
365		});
366	}
367
368	AcDocuments.prototype.onBackgroundOperationStart = function() {
369		this.pendingOperations++;
370		this.step.disableUserInteraction();
371	}
372
373	AcDocuments.prototype.onBackgroundOperationEnd = function() {
374		this.pendingOperations--;
375
376		if (this.pendingOperations === 0) {
377			this.step.enableUserInteraction();
378		}
379	}
380
381	AcDocuments.prototype.filterMsgToKey = function(msg) {
382		var safeMsg = "";
383
384		if (typeof msg === "object") {
385			if ("status" in msg && typeof msg.status.message === "string") {
386				safeMsg = msg.status.message;
387			} else if (typeof msg === "object" && "message" in msg) {
388				safeMsg = msg.message;
389			}
390
391			safeMsg = safeMsg.replace(" ", "_");
392		} else if (typeof msg === "string") {
393			safeMsg = msg;
394		}
395
396		var diacriticsRegex = new RegExp("\\p{Diacritic}", "gu");
397		safeMsg = safeMsg.trim().normalize("NFD").replace(diacriticsRegex, "");
398		safeMsg = safeMsg.toLowerCase().replace(/[ \t\r\n\-]+/, "-");
399
400		return safeMsg;
401	}
402
403	AcDocuments.prototype.getMessageObj = function(msgKey, msg) {
404		var result = {
405				title: this.config.msg.genericErrorTitle,
406				description: this.config.msg.genericErrorDescription
407		};
408
409		var unhandledErrors = ["timeout", "parsererror", "abort", "error", "authorization_required"];
410
411		if (msgKey === "invalid-size" || msgKey == "error-file-upload-max-size") {
412			result.title = this.config.msg.invalidSizeTitle;
413			result.description = this.config.msg.invalidSizeDescription;
414		} else if (msgKey === "invalid-format" || msgKey == "error-file-upload-format") {
415			result.title = this.config.msg.invalidFormatTitle;
416			result.description = this.config.msg.invalidFormatDescription;
417		} else if (msgKey === "invalid-file" || msgKey == "error-file-corrupted" || msgKey == "error-file-truncated") {
418			result.title = this.config.msg.fileCorruptedTitle;
419			result.description = this.config.msg.fileCorruptedDescription;
420		} else if (unhandledErrors.indexOf(msgKey) >= 0) {
421			var errorKey = "ajaxError";
422
423			if (msgKey === "Error") {
424				errorKey += "Generic";
425			} else {
426				// convert msgKey to camelCase and append to errorKey
427				var parts = msgKey.split("-");
428				for (var index = 0; index < parts.length; index++) {
429					var part = parts[index];
430					errorKey += part[0].toUpperCase() + part.substring(1);
431				}
432			}
433
434			if (errorKey in this.config.msg) {
435				result.description = this.config.msg[errorKey];
436			}
437
438		} else if (msgKey.length > 0) {
439			result.description = msgKey.trim().replace(/[\-_]+/, " ");
440			result.description = result.description[0].toUpperCase() + result.description.substr(1);
441		}
442
443		return result;
444	}
445
446	AcDocuments.prototype.showError = function(groupId, msg) {
447		var msgKey = this.filterMsgToKey(msg);
448		var msgObj = this.getMessageObj(msgKey, msg);
449
450		var errorContainer = this.findElement(groupId, '.file-upload-error-message');
451		errorContainer.find('.title').text(msgObj.title);
452		errorContainer.find('.description').text(msgObj.description);
453		var $this = this;
454		var showTime = this.config.animate.duration.show || 'slow';
455		var hideTimeout = this.config.animate.timeout.hide || 4000;
456
457		errorContainer
458			.attr("aria-role", "alert")
459			.addClass("current-error")
460			.show(showTime, function(){
461				setTimeout(function(){
462					$this.hideError(groupId);
463				}, hideTimeout);
464			});
465	}
466
467	AcDocuments.prototype.hideError = function(groupId) {
468		var errorContainer = this.findElement(groupId, '.file-upload-error-message');
469		var hideTime = this.config.animate.duration.hide || 'fast';
470		errorContainer
471			.removeAttr("aria-role")
472			.removeClass("current-error")
473			.hide(hideTime);
474	}
475
476	AcDocuments.prototype.initDnD = function() {
477		var rootElem = this.findElement();
478		this.initValidation(rootElem);
479		this.init();
480
481		// Ensure there is a file input and that it contains all the required stuff
482		rootElem.find('[data-ov-file-upload-group-id] input[type="file"]')
483			.attr('accept', this.config.validate.accepts.join(','))
484			// Ignore file elements in built-in validation as they are handled by this pseudo-class directly
485			.attr('data-ov-validation-ignore', true)
486			.attr('data-ov-validation', 'valid')
487			.prop('multiple', true)
488			.show()
489			.addClass('ov-hidden-accesible-block');
490
491		this.handleEvents(rootElem);
492	}
493
494	AcDocuments.prototype.handleEvents = function(rootElem) {
495		var $this = this;
496		rootElem.find('[data-ov-file-upload-group-id] [data-ov-file-upload-document-id]').each(function(pos, elem){
497			var jElem = jQuery(elem);
498			$this.attachEventHandlers(jElem);
499		});
500
501		rootElem.find('[data-ov-file-upload-group-id] .drop_zone')
502			.on('dragover.dnd dragleave.dnd', function(ev) {
503				ev.stopPropagation();
504				ev.preventDefault();
505				$this.hideError($this.getGroupId(this));
506			}).on('drop.dnd', function(ev) {
507				$this.onDrop(this, ev);
508			});
509
510		rootElem.find('[data-ov-file-upload-group-id] input[type="file"]')
511			.on('change.dnd', function(ev){
512				$this.onChange(this, ev);
513			});
514	}
515
516	AcDocuments.prototype.initValidation = function(rootElem) {
517		var accepts = rootElem.attr("data-ov-upload-file-accepts");
518		accepts = accepts ? accepts.trim().toLowerCase().split(/[,\ ]+/) : [];
519
520		var maxSize = rootElem.attr("data-ov-upload-file-max-size");
521		maxSize = maxSize ? parseInt(maxSize.trim()) : 0;
522
523		if (isNaN(maxSize) || maxSize < 0) {
524			maxSize = 0;
525		}
526
527		this.config.validate.maxSize = maxSize;
528		this.config.validate.accepts = accepts;
529	}
530
531	AcDocuments.prototype.getGroupId = function(elem) {
532		var jElem = jQuery(elem);
533		var groupId = jElem.attr('data-ov-file-upload-group-id');
534
535		if (!groupId) {
536			jElem = jElem.closest('[data-ov-file-upload-group-id]');
537
538			if (jElem.length >= 0) {
539				groupId = jElem.attr('data-ov-file-upload-group-id');
540			}
541		}
542
543		return groupId ? groupId : undefined;
544	}
545
546	AcDocuments.prototype.getFileInputByElem = function(domElement) {
547		var groupId = this.getGroupId(domElement);
548
549		if (!groupId) {
550			throw new Error('No groupId found by DOM element', domElement);
551		}
552
553		var fileInput = this.findElement(groupId, 'input[type="file"]');
554
555		return fileInput && fileInput.length > 0 ? fileInput : undefined;
556	}
557
558	AcDocuments.prototype.onDrop = function(domElement, ev) {
559		ev.stopPropagation();
560		ev.preventDefault();
561
562		// Normalize to a File array.
563
564		var dataTransferItems = (ev.dataTransfer  && ev.dataTransfer.items)
565			|| (ev.originalEvent.dataTransfer && ev.originalEvent.dataTransfer.items)
566			|| [];
567		var dataFiles = ev.target.files || ev.originalEvent.target.files || [];
568
569		var files = this.normalizeToFileArray([].concat(dataTransferItems).concat(dataFiles));
570
571		var fileInput = this.getFileInputByElem(domElement);
572
573		if (!fileInput) {
574			throw new Error('No file input found by group ' + groupId);
575		}
576
577		this.handleFilesForUpload(domElement, fileInput.attr("name"), files);
578	}
579
580	AcDocuments.prototype.onChange = function(targetElem, ev) {
581		ev.preventDefault();
582		ev.stopPropagation();
583
584		var fileUploadRow = jQuery(targetElem).closest('.file-upload-row');
585
586		if (fileUploadRow.length === 0) {
587			throw new Error('Invalid target element is not inside the upload row');
588		}
589
590		var files = this.normalizeToFileArray(ev.target.files);
591		this.handleFilesForUpload(fileUploadRow[0], ev.target.name, files);
592	}
593
594	AcDocuments.prototype.handleFilesForUpload = function(targetElem, inputName, files) {
595		var groupId = this.getGroupId(targetElem);
596
597		if (!groupId) {
598			throw new Error("No group found for target element");
599		}
600
601		for (var index = 0; index < files.length; index++) {
602			var candidate = files[index];
603			var validation = this.validateFile(candidate);
604
605			if (validation !== "") {
606				console && console.error("Invalid file " + candidate.name + ". Validation: " + validation);
607				this.showError(groupId, validation);
608				return;
609			}
610		}
611
612		var $this = this;
613
614		this.ajaxAddFiles(groupId, inputName, files, null, function(finalGroupId, finalFileId){
615			var input = $this.findElement(finalGroupId, 'input[type="file"]');
616
617			if (input.length === 1) {
618				$this.clearFileInput(input);
619			} else if (input.length > 1) {
620				throw new Error("Found multiple inputs in group " + finalGroupId);
621			} else if (input.length === 0) {
622				throw new Error("No file input found in group " + finalGroupId);
623			}
624		});
625	}
626
627	AcDocuments.prototype.clearFileInput = function(input) {
628		// clear validation state
629		input.val('').attr("data-ov-validation", "valid")[0].removeAttribute("aria-invalid");
630		// Clear the file input replacing with a deep clone while keeping all event handlers
631		var newInput = input.clone(true);
632		input.replaceWith(newInput);
633		// Do validation over the new empty one
634		newInput.change();
635	}
636
637	AcDocuments.prototype.validateFileNotCorrupted = function(file) {
638		var key = (file && file.type.toLowerCase()) || "none";
639		var minSize = 0;
640
641		if (key in this.config.validate.minSizes) {
642			minSize = this.config.validate.minSizes[key];
643		}
644
645		return AcValidations.validateFileNotEmptyAndMime(file, minSize);
646	}
647
648	AcDocuments.prototype.validateFileSize = function(file) {
649		return AcValidations.validateFileSize(file, this.config.validate.maxSize);
650	}
651
652	AcDocuments.prototype.validateFileAccept = function(file) {
653		return AcValidations.validateFileAccept(file, this.config.validate.accepts);
654	}
655
656	AcDocuments.prototype.validateFile = function(file) {
657
658		if (!this.validateFileNotCorrupted(file)) {
659			return "invalid-file";
660		} else if (!this.validateFileSize(file)) {
661			return "invalid-size";
662		} else if (!this.validateFileAccept(file)) {
663			return "invalid-format";
664		}
665
666		return '';
667	}
668
669	AcDocuments.prototype.isImage = function(file) {
670		return file && file.type && file.type.startsWith('image/');
671	}
672
673	AcDocuments.prototype.isPdf = function(file) {
674		return file && file.type && file.type === 'application/pdf';
675	}
676
677	AcDocuments.prototype.startProgressAnimation = function(fileElem, onEnd) {
678		var progress = fileElem.find('.progress');
679		var domElem = progress.get(0);
680		var totalWidth = parseInt(progress.css('width'));
681		var maxTimeUnits = 120;
682		var stepTime = 400;
683
684		var fill = progress.find('.progress-bar');
685		var currentWidth = parseInt(fill.css('width'));
686
687		var stepWidth = (totalWidth - currentWidth)/maxTimeUnits;
688		var $this = this;
689
690		var updateFill = function() {
691			clearTimeout(domElem.progressTimer);
692
693			if (currentWidth >= totalWidth) {
694				$this.showError(fileElem.attr('data-ov-file-upload-group-id'));
695				$this.stopProgressAnimation(fileElem, onEnd);
696			} else {
697				currentWidth += stepWidth;
698				fill.css('width', currentWidth + 'px');
699
700				if (currentWidth > (totalWidth / 4)) {
701					stepTime = 300;
702					stepWidth = stepWidth / 2;
703				} else if (currentWidth > (totalWidth / 2)) {
704					stepTime = 200;
705					stepWidth = stepWidth / 2;
706				}
707
708				domElem.progressTimer = setTimeout(updateFill, stepTime);
709			}
710		};
711
712		updateFill();
713	}
714
715	AcDocuments.prototype.stopProgressAnimation = function(fileElem, onEnd) {
716		var progress = fileElem.find('.progress');
717
718		if (progress.length > 0) {
719			var domElem = progress.get(0);
720			clearTimeout(domElem.progressTimer);
721			progress.find('.progress-bar').css('width', '100%');
722		}
723
724		if (typeof onEnd === 'function') {
725			setTimeout(function(){
726				onEnd(progress);
727			}, 100);
728		}
729	}
730}
731
732/* Definition of class AcTramiteStep */
733
734if (typeof AcTramiteStep === 'undefined') {
735	function AcTramiteStep(config)  {
736
737		var baseSelector = ".ac-tramite.tramite-cambio-titular";
738		var sectionSelector = baseSelector + " .tramite-wrapper";
739
740		var defaultConfig = {
741			nmsp: "",
742			validation: {},
743			selectors: {
744				base: baseSelector,
745				section: sectionSelector,
746				form: sectionSelector + " form.tramite-form"
747			},
748		};
749
750		this.config = jQuery.extend({}, defaultConfig, config || {});
751
752		this.name = "tramite-step";
753		var parts = this.getCurrentStep().split("-");
754
755		for (var index = 0; index < parts.length; index++) {
756			var part = parts[index];
757			if (part.length > 0) {
758				this.name += part[0].toUpperCase() + part.substr(1);
759			}
760		}
761
762		// jQuery validate validator object
763		this.validator = null;
764		// Flag to avoid recursion between checks inside enableSubmitButton and handlers that call it
765		this.enableButtonPending = false;
766		this.debug = false;
767	}
768
769	/**
770	 * Returns the portvar namespace
771	 */
772	AcTramiteStep.prototype.getNmsp = function() {
773		return this.config.nmsp;
774	}
775
776	AcTramiteStep.prototype.getName = function () {
777		return this.name;
778	}
779
780	/**
781	 * Returns the validator object returned by jQuery.validate
782	 *
783	 * @see https://jqueryvalidation.org/documentation/
784	 * @example
785     * Validator.form() – Validates the form.
786     * Validator.element() – Validates a single element.
787     * Validator.resetForm() – Resets the controlled form.
788     * Validator.showErrors() – Show the specified messages.
789     * Validator.numberOfInvalids() – Returns the number of invalid fields.
790     * Validator.destroy() – Destroys this instance of validator.
791     *
792	 */
793	AcTramiteStep.prototype.getValidator = function() {
794		return this.validator;
795	}
796
797	/**
798	 *
799	 */
800	AcTramiteStep.prototype._getElement = function(type) {
801		var elem = null;
802
803		if (type in this.config.selectors) {
804			elem = jQuery(this.config.selectors[type]);
805
806			if (typeof elem === "undefined" || !elem.length) {
807				typeof window.console === "object"
808					&& console.error("No element found by selector type " + type + ": " + this.config.selectors[type]);
809			}
810		} else {
811			typeof window.console === "object"
812				&& console.error("Invalid selector type " + type);
813		}
814
815		return elem;
816	}
817
818	AcTramiteStep.prototype.getSectionElement = function() {
819		return this._getElement("section");
820	}
821
822	/**
823	 * Gets the tramite form jQuery object
824	 */
825	AcTramiteStep.prototype.getForm = function() {
826		return this._getElement("form");
827	}
828
829	/**
830	 * Gets the DOM element of a form input by name
831	 */
832	AcTramiteStep.prototype.getFormElement = function (elemName) {
833		if (typeof elemName !== "string" || !elemName) {
834			return null;
835		}
836
837		return this.getForm().find('[name="' + elemName + '"]');
838	}
839
840	/**
841	 * Already valid elements
842	 */
843	AcTramiteStep.prototype.getValidElements = function () {
844		return this._getValidationElements("valid");
845	}
846
847	/**
848	 * Already invalid elements
849	 */
850	AcTramiteStep.prototype.getInvalidElements = function () {
851		return this._getValidationElements("invalid");
852	}
853
854	/**
855	 * Elements marked as required but not validated
856	 */
857	AcTramiteStep.prototype.getMissingElements = function () {
858		return this._getValidationElements("missing");
859	}
860
861	/**
862	 * Elements that haven't been validated whether they are required or not
863	 */
864	AcTramiteStep.prototype.getAllElements = function () {
865		return this._getValidationElements("all");
866	}
867
868	AcTramiteStep.prototype._getValidationElements = function (validFlag) {
869
870		var filterType = "";
871
872		if (validFlag === true || validFlag === "true") {
873			// Valid if not invalid
874			filterType = "valid";
875		} else if (validFlag === false || validFlag === "false") {
876			// Invalid if not valid
877			filterType = "invalid";
878		} else if (validFlag === "missing") {
879			// Elements required but not already filled but they are also not marked as invalid
880			filterType = "missing";
881		} else if (validFlag === undefined || validFlag === "not-validated") {
882			// Elements not validated whether already invalid or not
883			filterType = "not-validated";
884		} else if (validFlag === "validated") {
885			// Elements not validated whether already invalid or not
886			filterType = "not-validated";
887		} else {
888			// all, no selector
889			filterType = "all";
890		}
891
892		var result = this.getValidator().elements();
893		var $this = this;
894
895		if (filterType !== "all") {
896			result = result.filter(function(pos, elem){
897				return $this._is(jQuery(elem), filterType);
898			});
899		}
900
901		return result;
902	}
903
904	AcTramiteStep.prototype.getElementsSummary = function() {
905		var summary = {
906				total: 0,
907				required: 0,
908				missing: 0,
909				validated: 0,
910				notValidated: 0,
911				valid: 0,
912				invalid: 0
913		};
914
915		var $this = this;
916
917		this.getValidator().elements().each(function(pos, elem){
918			var jElem = jQuery(elem);
919			summary.total++;
920			summary.required += jElem.is(':required') ? 1: 0;
921			summary.missing += $this._is(jElem, "missing") ? 1 : 0;
922			summary.validated += $this._is(jElem, "validated") ? 1 : 0;
923			summary.notValidated += $this._is(jElem, "not-validated") ? 1 : 0;
924			summary.valid += $this._is(jElem, "valid") ? 1 : 0;
925			summary.invalid += $this._is(jElem, "invalid") ? 1 : 0;
926		});
927
928		return summary;
929	}
930
931	AcTramiteStep.prototype._is = function(jElem, filterType) {
932		if (jElem.is(":hidden") || jElem.prop("readonly")) {
933			return false;
934		}
935
936		switch (filterType) {
937			case "valid":
938				return jElem.is('[data-ov-validation="valid"]');
939			case "invalid":
940				return jElem.is('[data-ov-validation="invalid"]');
941			case "missing":
942				return this.validator.isMissing(jElem[0]);
943			case "not-validated":
944				return jElem.is('[data-ov-validation="pending"]');
945			case "validated":
946				return jElem.is(':not([data-ov-validation="pending"])');
947			default:
948				return true;
949		}
950	}
951
952	/**
953	 * Gets if the current form is valid and can be submitted
954	 *
955	 * Checks if all required fields are filled and valid and that no invalid fields have been found.
956	 */
957	AcTramiteStep.prototype.isValid = function() {
958		var summary = this.getElementsSummary();
959
960		var formIsValid = summary.missing === 0 && summary.invalid === 0;
961
962		if (this.debug) {
963			typeof window.console === "object"
964				&& console.debug("isValid::" + formIsValid, summary)
965				&& console.debug(this.getForm().find(':focus').first());
966		}
967
968		return formIsValid;
969	}
970
971	/**
972	 * Gets the tramite submit button jQuery object or null
973	 */
974	AcTramiteStep.prototype.getSubmitButton = function(form) {
975		form = form || this.getForm();
976		return form.find('.btn-tramite[type="submit"]');
977	}
978
979	AcTramiteStep.prototype._delayedEnableSubmitButton = function(enable, form) {
980		var $this = this;
981
982		if (this._delayedEnableSubmitButtonTimeout !== null) {
983			clearTimeout(this._delayedEnableSubmitButtonTimeout);
984			this._delayedEnableSubmitButtonTimeout = null;
985		}
986
987		this._delayedEnableSubmitButtonTimeout = setTimeout(function(){
988			this._delayedEnableSubmitButtonTimeout = null;
989			$this.enableSubmitButton(enable, form);
990		}, 50);
991	}
992
993	AcTramiteStep.prototype.enableSubmitButton = function(enable, form) {
994		if (typeof enable === "undefined" || enable === null) {
995			enable = this.isValid();
996		}
997
998		var propValue = typeof enable === "boolean" && enable;
999		this.getSubmitButton(form).prop("disabled", !propValue);
1000	}
1001
1002	AcTramiteStep.prototype.hideErrorsBlock = function(time) {
1003		if (this.getInvalidElements().length === 0
1004				&& jQuery('#errores-formulario').is(":visible")) {
1005			// No invalids, errors shown, then clear them.
1006			jQuery('#errores-formulario').hide(time || 800);
1007		}
1008	}
1009
1010	/**
1011	 * Gets the current step key
1012	 */
1013	AcTramiteStep.prototype.getCurrentStep = function() {
1014		var section = this.getSectionElement();
1015		var data = section.attr("data-tramite-step");
1016
1017		if (typeof data === "string" && data.trim()) {
1018			return data.trim();
1019		}
1020
1021		return "";
1022	}
1023
1024	/**
1025	 * Gets the name of the current step
1026	 */
1027	AcTramiteStep.prototype.getCurrentStepName = function() {
1028		var step = this.getCurrentStep();
1029		var name = "";
1030
1031		if (step) {
1032			var parts = step.split("-");
1033			for (var index = 0; index < parts.length; index++) {
1034				if (index == 0) {
1035					name += parts[index];
1036				} else {
1037					name += parts[index][0].toUpperCase()
1038						+ parts[index].substr(1);
1039				}
1040			}
1041		}
1042
1043		return name;
1044	}
1045
1046	/**
1047	 * Returns default validation object to configure jQuery validate plugin.
1048	 */
1049	AcTramiteStep.prototype.getDefaultValidation = function() {
1050		var $this = this;
1051
1052		return {
1053			errorClass: "has-error",
1054			validClass: "is-valid",
1055			success: "is-valid",
1056			errorElement: "span",
1057			errorPlacement: function(error, element) {
1058				jQuery(element)
1059					.closest(".ov-form-field")
1060					.find(".field-error")
1061					.empty()
1062					.append(error);
1063		  	},
1064		    highlight: function(element, errorClass, validClass) {
1065		        jQuery(element)
1066		        	.closest(".ov-form-field")
1067		        	.addClass(errorClass)
1068		        	.removeClass(validClass);
1069		    },
1070		    unhighlight: function(element, errorClass, validClass) {
1071		    	jQuery(element)
1072		    		.closest(".ov-form-field")
1073		    		.removeClass(errorClass)
1074		    		.addClass(validClass);
1075		    },
1076		    invalidHandler: function(event, validator) {
1077		        // 'this' refers to the form
1078		        $this.enableSubmitButton(false);
1079		    },
1080		    afterElementValidated: function (element, valid) {
1081		    	$this._delayedEnableSubmitButton();
1082		    }
1083		}
1084	}
1085
1086	AcTramiteStep.prototype.destroyValidator = function() {
1087		if (this.validator != null) {
1088			var validator = this.validator;
1089			this.validator = null;
1090			validator.destroy();
1091		}
1092	}
1093
1094	AcTramiteStep.prototype.getOrCreateWaitLoad = function() {
1095		var section = this.getSectionElement();
1096		var wait = section.find("#wait-load");
1097
1098		if (wait && wait.length === 0) {
1099			wait = section.find(".spinner.wait-load");
1100		}
1101
1102		if (!wait || wait.length === 0) {
1103			wait = jQuery('<div class="spinner wait-load" style="display: none;"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>');
1104			section.append(wait);
1105		}
1106
1107		return wait;
1108	}
1109
1110	AcTramiteStep.prototype.disableUserInteraction = function(focus, disableAllInputs) {
1111		var wait = this.getOrCreateWaitLoad();
1112
1113		if (wait && wait.length === 1 && !wait.is(':visible')) {
1114			wait.show();
1115			if (focus) {
1116				wait.attr("tabindex", 0)[0].scrollIntoView()
1117			}
1118		} else {
1119			return;
1120		}
1121
1122		var form = this.getForm();
1123		form.attr("data-ov-disabled", "true");
1124
1125		if (disableAllInputs) {
1126			form.find("input:not(:hidden)").each(function(pos, elem){
1127				var jElem = jQuery(elem);
1128				if (!jElem.prop("disabled")) {
1129					jElem.attr("data-ov-disabled", 'true');
1130					jElem.prop("disabled", true);
1131				}
1132			});
1133		}
1134
1135		form.find('button[type="submit"]').each(function(pos, elem){
1136			var jElem = jQuery(elem);
1137			var disabledPrev = jElem.prop('disabled') === true;
1138			jElem.attr("data-ov-disabled-prev", disabledPrev);
1139			jElem.prop('disabled', true);
1140		});
1141	}
1142
1143	AcTramiteStep.prototype.enableUserInteraction = function() {
1144		var section = this.getSectionElement();
1145		var wait = section.find("#wait-load");
1146
1147		if (wait && wait.length === 0) {
1148			wait = section.find(".spinner.wait-load");
1149		}
1150
1151		if (wait && wait.length === 1 && wait.is(':visible')) {
1152			wait.hide();
1153		}
1154
1155		var form = this.getForm();
1156		form.removeAttr("data-ov-disabled");
1157
1158		form.find('[data-ov-disabled="true"]').each(function(pos, elem){
1159			jQuery(elem).removeAttr("data-ov-disabled").prop('disabled', false);
1160		});
1161
1162		form.find('button[type="submit"]').each(function(pos, elem){
1163			var jElem = jQuery(elem);
1164			var disabledPrev = jElem.attr('data-ov-disabled-prev') === 'true';
1165			jElem.removeAttr('data-ov-disabled-prev');
1166
1167			if (!disabledPrev) {
1168				jElem.prop('disabled', false);
1169			}
1170		});
1171	}
1172
1173	/**
1174	 * Initializes generic validation with jQuery validate plugin
1175	 *
1176	 * Merges the customValidation argument over the default validation and uses it
1177	 *
1178	 */
1179	AcTramiteStep.prototype.initValidation = function(customValidation) {
1180
1181		if (this.validator != null) {
1182			return;
1183		}
1184
1185		customValidation = customValidation || {};
1186		var defaultValidation = this.getDefaultValidation();
1187		var validation = jQuery.extend({}, defaultValidation, this.config.validation, customValidation);
1188
1189		var form = this.getForm();
1190		this.validator = form.validate(validation);
1191		var $this = this;
1192
1193		// Handle change on elements that toggle parts of the form and revalidate those parts
1194		form.find('input[data-ov-toggle-visibility], select[data-ov-toggle-visibility], textarea[data-ov-toggle-visibility]').change(function(ev){
1195			$this.toggleVisibility(this);
1196		});
1197
1198		// Force a change to set initial state
1199		jQuery("[data-ov-toggle-visibility]").change();
1200
1201		// After initializing validate the form if some data is present and hide/show the button
1202		if (this.validator.elementsFilled().length > 0) {
1203			var valid = form.valid();
1204
1205			this.validator.showErrors();
1206
1207			this.enableSubmitButton(valid, form);
1208		} else {
1209			this.validator.initElements();
1210		}
1211	}
1212
1213	AcTramiteStep.prototype.toggleVisibility = function(elem) {
1214		var jElem = jQuery(elem);
1215		var toggleSource = jElem.closest(".row--panel-toggle-source");
1216		var visible = jElem.is(":checked");
1217
1218		if (visible) {
1219			toggleSource.addClass("visible");
1220		} else {
1221			toggleSource.removeClass("visible");
1222		}
1223
1224		var targets = jElem.attr('data-ov-toggle-visibility').split(' ');
1225
1226		for (var index = 0; index < targets.length; index++) {
1227			var targetId = targets[index];
1228			// Search only under the form element
1229			var target = this.getForm().find('[id="' + targetId + '"]').first();
1230
1231			if (!visible) {
1232				this.hideToggleTarget(target);
1233			} else {
1234				this.showToggleTarget(target);
1235			}
1236		}
1237	}
1238
1239	AcTramiteStep.prototype.getTargetElements = function(target) {
1240		return jQuery(target)
1241			.find('input, select, textarea')
1242			.not(':submit, :reset, :image, :disabled, [type="hidden"], [readonly]')
1243			;
1244	}
1245
1246	AcTramiteStep.prototype.hideToggleTarget = function(target) {
1247		var $this = this;
1248
1249		target.hide("fast", "swing", function() {
1250			$this.validator.resetElements($this.getTargetElements(target));
1251			$this._delayedEnableSubmitButton();
1252		});
1253	}
1254
1255	AcTramiteStep.prototype.showToggleTarget = function(target) {
1256		var $this = this;
1257
1258		target.show(400, "swing", function() {
1259			// Validate shown elements only if at least one is filled
1260			var enableValidation = false;
1261
1262			var elements = $this.getTargetElements(target);
1263
1264			for (var index = 0; index < elements.length; index++) {
1265				var jElem = jQuery(elements[index]);
1266
1267				if (jElem.is(":visible") && !jElem.is(":blank")) {
1268					enableValidation = true;
1269					break;
1270				}
1271			}
1272
1273			if (enableValidation) {
1274				elements.each(function(pos, targetElement){
1275					// Validate on show only those visible inputs with content or marked as invalid
1276					$this.validator.element(targetElement);
1277				});
1278			} else {
1279				$this.validator.resetElements(elements);
1280			}
1281
1282			$this._delayedEnableSubmitButton();
1283		});
1284	}
1285}
1286
1287
1288if (typeof AcTramite === 'undefined') {
1289    function AcTramite(config) {
1290        this.steps = {};
1291        this.msg = {};
1292        this.funcs = {};
1293
1294        var defaultConfig = {
1295            nmsp: "",
1296            name: "tramite-generic",
1297            defaultStep: "default-step"
1298        };
1299
1300        this.config = jQuery.extend({}, defaultConfig, config || {});
1301    }
1302
1303    AcTramite.prototype.getCurrentStep = function() {
1304        var currentStep = jQuery('.ac-tramite.tramite-' + this.config.name +  ' .tramite-wrapper').attr('data-tramite-step');
1305
1306        if (!currentStep) {
1307            currentStep = this.config.defaultStep;
1308        }
1309
1310        return currentStep;
1311    };
1312
1313    AcTramite.prototype.createStep = function(stepConfig) {
1314        var config = stepConfig || {};
1315
1316        var defaultConfig = {
1317            nmsp: this.config.nmsp,
1318        };
1319
1320        var finalConfig = jQuery.extend(defaultConfig, config);
1321        var step = new AcTramiteStep(finalConfig);
1322
1323        return step;
1324    }
1325
1326    AcTramite.prototype.init = function () {
1327        var currentStep = this.getCurrentStep();
1328
1329        if (typeof this.funcs['before-init'] === "function") {
1330            this.funcs['before-init'](currentStep);
1331        }
1332
1333        if (typeof this.funcs['init-' + currentStep]) {
1334            this.funcs['init-' + currentStep]();
1335        }
1336
1337        if (typeof this.funcs['after-init']) {
1338            this.funcs['after-init'](currentStep);
1339        }
1340    }
1341
1342    AcTramite.prototype.initStep = function (stepName) {
1343
1344    };
1345
1346}
1347
1348
1349if (typeof AcValidations === "undefined") {
1350	function AcValidations() {
1351
1352	}
1353
1354	AcValidations.validateFileNotEmptyAndMime = function (file, minSize) {
1355		if (!file) {
1356			return true;
1357		}
1358
1359		var safeMinSize = parseInt(minSize);
1360		if (isNaN(safeMinSize) || safeMinSize < 0) {
1361			safeMinSize = 0;
1362		}
1363
1364		return file.size > safeMinSize
1365			&& file.type !== ""
1366			&& file.name.indexOf(".") > 0;
1367	}
1368
1369	AcValidations.validateFileSize = function (file, maximumSize) {
1370		var fileSizeLimit = parseInt(maximumSize);
1371
1372		if (!file || isNaN(fileSizeLimit) || fileSizeLimit <= 0) {
1373			return true;
1374		}
1375
1376		return file.size <= fileSizeLimit;
1377	}
1378
1379	AcValidations.validateFileAccept = function (file, accept) {
1380		if (!file || (typeof accept === "string" && accept.trim() === "")
1381				|| (accept instanceof Array && accept.length === 0)) {
1382			return true;
1383		}
1384
1385		var expressions = null;
1386
1387		if (typeof accept === "string") {
1388			expressions = accept.split(",");
1389		} else if (accept instanceof Array) {
1390			expressions = accept;
1391		} else {
1392			return true;
1393		}
1394
1395		var fileExt = file.name.toLowerCase().substring(file.name.lastIndexOf("."));
1396		var fileType = file.type.toLowerCase();
1397
1398		for (var index = 0; index < expressions.length; index++) {
1399			var expression = expressions[index].trim().toLowerCase();
1400
1401			if (expression.length === 0) {
1402				continue;
1403			}
1404
1405			var checkTypeEnds = false;
1406
1407			if (expression.endsWith("*")) {
1408				expression = expression.substring(0, expression.length - 1);
1409				checkTypeEnds = true;
1410			}
1411
1412			if (expression === fileExt
1413				|| (!checkTypeEnds && fileType === expression)
1414				|| (checkTypeEnds && fileType.startsWith(expression))) {
1415				return true;
1416			}
1417		}
1418
1419		return false;
1420	}
1421}
1422

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.