PageSourceSearch

https://www.perlesandco.com/templates/template2017/js/plugins/contactPictures.js?v=2.0.2.107

js perlesandco.com collected 2026-10-03 22:03:47 UTC 7,777 bytes, 223 lines download raw bytes

1/**
2 * Dépôt des pièces jointes du formulaire de contact.
3 *
4 * Volontairement distinct de multipleFileForm.js, que chargent aussi les avis produits et les
5 * contenus : les plafonds posés ici ne doivent contraindre que ce formulaire.
6 *
7 * Plafonds et messages sont lus sur les data-* de l'élément, alimentés par faq_contactForm :
8 * une seule source, sinon le garde-fou navigateur et le contrôle serveur divergent et le client
9 * se voit refuser un envoi que le formulaire lui présentait comme valide.
10 *
11 * Requiert imageResize.js (resizeInputFile, formatFileSize), à charger avant.
12 */
13(function($)
14{
15	$.fn.contactPictures = function()
16	{
17		return this.each(function()
18		{
19			var zone		= $(this),
20				settings	= {
21								accept		: (zone.data('accept') || '').split(',').map(function(t){ return $.trim(t).toLowerCase(); }),
22								maxSize		: parseInt(zone.data('max-size'),10)	|| 0,
23								maxFiles	: parseInt(zone.data('max-files'),10)	|| 0,
24								maxTotal	: parseInt(zone.data('max-total'),10)	|| 0,
25								msgMaxFiles	: zone.data('msg-max-files')	|| '',
26								msgMaxSize	: zone.data('msg-max-size')		|| '',
27								msgMaxTotal	: zone.data('msg-max-total')	|| '',
28								msgAccept	: zone.data('msg-accept')		|| '',
29								units		: (zone.data('units') || 'Ko,Mo').split(','),
30							};
31
32			// Zone de message : le client doit voir pourquoi son fichier est refusé sans quitter le formulaire
33				var message = $('<div class="fileFormMessage" role="alert" aria-live="polite"></div>').hide();
34				zone.after(message);
35
36			zone.css({'display': 'flex','flex-wrap': 'wrap'});
37			addSlot(zone,settings);
38
39			zone.on('change','.imageUploader input',function()
40			{
41				var input	= $(this),
42					label	= input.closest('.imageUploader').find('label');
43
44				if (!input[0].files[0])
45					return;
46
47				// Réduction avant contrôle : une photo de téléphone trop lourde passe une fois réduite
48					resizeInputFile(input[0]).then(function(files)
49					{
50						var file	= files[0],
51							error	= checkFile(zone,settings,file);
52
53						if (error)
54						{
55							// L'espace est remis à zéro : sans ça le client croit son fichier joint alors qu'il est écarté
56								input.val('');
57								resetLabel(label);
58								message.text(error).show();
59								return;
60						}
61
62						message.text('').hide();
63						showPreview(zone,settings,label,file);
64					});
65			});
66
67			zone.on('click','.removeFile',function(ev)
68			{
69				ev.preventDefault();
70
71				var label = $(this).closest('label');
72
73				$('#'+label.attr('for')).val('');
74				resetLabel(label);
75				message.text('').hide();
76				compactSlots(zone,settings);
77			});
78		});
79	};
80
81	/**
82	 * Confronte un fichier aux plafonds du formulaire.
83	 * Rend le message à afficher, ou une chaîne vide si le fichier passe.
84	 */
85	function checkFile(zone,settings,file)
86	{
87		// Le type réel, pas celui qu'annonce l'attribut accept : un glisser-déposer le contourne
88			if (settings.accept.length && !isAcceptedType(file.type,settings.accept))
89				return settings.msgAccept;
90
91		if (settings.maxSize > 0 && file.size > settings.maxSize)
92			return settings.msgMaxSize;
93
94		var attached = countFiles(zone);
95
96		if (settings.maxFiles > 0 && attached.count > settings.maxFiles)
97			return settings.msgMaxFiles;
98
99		// Poids cumulé : c'est lui qui fait refuser l'envoi par le routeur de mails, pas le poids unitaire
100			if (settings.maxTotal > 0 && attached.size > settings.maxTotal)
101				return settings.msgMaxTotal;
102
103		return '';
104	}
105
106	function isAcceptedType(type,formats)
107	{
108		type = (type || '').toLowerCase();
109
110		for (var i=0; i<formats.length; i++)
111			if (formats[i] === type || (formats[i].slice(-2) === '/*' && type.indexOf(formats[i].slice(0,-1)) === 0))
112				return true;
113
114		return false;
115	}
116
117	/** Nombre de fichiers joints et poids cumulé. */
118	function countFiles(zone)
119	{
120		var attached = { count: 0, size: 0 };
121
122		zone.find('.imageUploader input').each(function(){
123			if (!this.files)
124				return;
125
126			attached.count += this.files.length;
127
128			for (var i=0; i<this.files.length; i++)
129				attached.size += this.files[i].size;
130		});
131
132		return attached;
133	}
134
135	/** Affiche la vignette d'un fichier accepté, puis ouvre l'espace suivant s'il reste de la place. */
136	function showPreview(zone,settings,label,file)
137	{
138		var reader	= new FileReader(),
139			pdfIcon	= '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M384 160L224 0V128c0 17.7 14.3 32 32 32H384zM176 352c-8.8 0-16 7.2-16 16v128c0 8.8 7.2 16 16 16s16-
1397.2 16-16V464h16c30.9 0 56-25.1 56-56s-25.1-56-56-56H176zm32 80H192V384h16c13.3 0 24 10.7 24 24s-10.7 24-24 24zm96-80c-8.8 0-16 7.2-16 16V496c0 8.8 7.2 16 16 16h32c26.5 0 48-21.5 48-48V400c0-26.5-21.5-48-48-48H304zm32 128H320V384h16c8.8 0 16 7.2 16 16v64c0 8.8-7.2 16-16 16zm80-112v128c0 8.8 7.2 16 16 16s16-7.2 16-16V448h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H448V384h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H432c-8.8 0-16 7.2-16 16z"/><path opacity=".4" d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V304H176c-35.3 0-64 28.7-64 64V512H64c-35.3 0-64-28.7-64-64V64z"/></svg>';
140
141		reader.onload = function(ev)
142		{
143			if (/^image/.test(file.type))
144				label.css({'background-image': 'url("'+ev.target.result+'")'});
145			else
146				label.css({'background-image': "url('data:image/svg+xml;charset=utf-8,"+encodeURIComponent(pdfIcon)+"')"});
147
148			label.html('<i class="far fa-pen-circle"></i><i class="far fa-trash-circle removeFile"></i>');
149			label.find('i').css({'font-size':'200%'});
150			label.append($('<span class="fileSize"></span>').text(formatFileSize(file.size,settings.units)));
151
152			addSlot(zone,settings);
153
154			// Le plafond se signale au moment où il est atteint : une fois le dernier espace rempli,
155			// il n'y a plus de bouton d'ajout, donc plus rien qui puisse expliquer le blocage.
156				if (settings.maxFiles > 0 && countFiles(zone).count >= settings.maxFiles)
157					zone.next('.fileFormMessage').text(settings.msgMaxFiles).show();
158		};
159
160		reader.readAsDataURL(file);
161	}
162
163	function addSlot(zone,settings)
164	{
165		// Plus d'espace vierge une fois le plafond atteint : le client ne peut plus dépasser sans le savoir
166			if (settings.maxFiles > 0 && countFiles(zone).count >= settings.maxFiles)
167			{
168				applyStyles(zone);
169				return;
170			}
171
172		var rank	= zone.find('.imageUploader').length + 1,
173			slot	= $('<div class="imageUploader"></div>');
174
175		slot.append('<input type="file" id="pjs_'+rank+'" name="pjs[]" class="inputfile" accept="'+zone.data('accept')+'">');
176		slot.append('<label for="pjs_'+rank+'" class="file-label"><i class="fad fa-plus-circle"></i></label>');
177
178		zone.append(slot);
179		applyStyles(zone);
180	}
181
182	function compactSlots(zone,settings)
183	{
184		if (zone.find('.imageUploader').length > 1)
185			zone.find('.imageUploader input').each(function(){
186				if ($(this).val() === '')
187					$(this).closest('.imageUploader').remove();
188			});
189
190		addSlot(zone,settings);
191	}
192
193	function resetLabel(label)
194	{
195		label.css({'background-image': ''});
196		label.html('<i class="fad fa-plus-circle"></i>');
197		label.find('i').css({'font-size':'200%'});
198	}
199
200	function applyStyles(zone)
201	{
202		zone.find('.imageUploader input').hide();
203		zone.find('.imageUploader label').css({
204			'border-radius'		: '5px',
205			'margin'			: '2px',
206			'color'				: '#232323',
207			'border'			: '2px dashed #9b9b9b',
208			'position'			: 'relative',
209			'display'			: 'flex',
210			'justify-content'	: 'space-around',
211			'align-items'		: 'center',
212			'width'				: '120px',
213			'height'			: '120px',
214			'cursor'			: 'pointer',
215			'background-repeat'	: 'no-repeat',
216			'transition'		: 'border-color 150ms linear,color 150ms linear',
217			'background-size'	: 'contain',
218			'background-position': 'center center',
219		});
220		zone.find('.imageUploader').css({'margin':'0'});
221		zone.find('.imageUploader label i').css({'font-size':'200%'});
222	}
223})(jQuery);

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.