patx/rcraft_const

const contactPhotos = (() => {
  const photoInput = document.getElementById('project-photos');
  const photoList = document.getElementById('photo-list');
  const photoStatus = document.getElementById('photos-status');
  const formError = document.getElementById('form-error');
  const maxPhotos = 5;
  const maxPhotoBytes = 25 * 1024 * 1024;
  const maxTotalPhotoBytes = 90 * 1024 * 1024;
  let selectedPhotos = [];
  function clearFormError() { formError.hidden = true; formError.textContent = ''; }
      function formatPhotoStatus(files) {
        if (!files.length) return '';
        return files.length + ' of ' + maxPhotos + (files.length === 1 ? ' photo is' : ' photos are') + ' ready to send.';
      }

      function photoValidationMessage(files) {
        var totalBytes = files.reduce(function (total, file) { return total + file.size; }, 0);
        var oversizedPhoto = files.find(function (file) { return file.size > maxPhotoBytes; });
        var nonPhoto = files.find(function (file) {
          return file.type ? file.type.indexOf('image/') !== 0 : !/\.(jpe?g|png|gif|webp|heic|heif|avif|bmp|tiff?)$/i.test(file.name);
        });

        if (files.length > maxPhotos) {
          return 'Choose no more than 5 photos. Remove one before adding another.';
        }
        if (oversizedPhoto) {
          return oversizedPhoto.name + ' is larger than 25 MB and was not added.';
        }
        if (totalBytes > maxTotalPhotoBytes) {
          return 'Keep the combined photo size under 90 MB. Those photos were not added.';
        }
        if (nonPhoto) return 'Choose image files only.';
        return '';
      }

      function formatPhotoSize(bytes) {
        if (bytes < 1024 * 1024) return Math.max(1, Math.round(bytes / 1024)) + ' KB';
        return (bytes / (1024 * 1024)).toFixed(1).replace('.0', '') + ' MB';
      }

      function photoKey(file) {
        return [file.name, file.size, file.lastModified, file.type].join('|');
      }

      function renderPhotos(message) {
        photoList.textContent = '';
        selectedPhotos.forEach(function (file, index) {
          var item = document.createElement('li');
          var fileDetails = document.createElement('span');
          var fileName = document.createElement('span');
          var fileSize = document.createElement('span');
          var removeButton = document.createElement('button');

          item.className = 'photo-item';
          fileDetails.className = 'photo-file';
          fileName.className = 'photo-name';
          fileName.textContent = file.name;
          fileSize.className = 'photo-size';
          fileSize.textContent = 'Photo ' + (index + 1) + ' ยท ' + formatPhotoSize(file.size);
          removeButton.className = 'photo-remove';
          removeButton.type = 'button';
          removeButton.dataset.photoIndex = index;
          removeButton.setAttribute('aria-label', 'Remove ' + file.name);
          removeButton.textContent = 'Remove';

          fileDetails.appendChild(fileName);
          fileDetails.appendChild(fileSize);
          item.appendChild(fileDetails);
          item.appendChild(removeButton);
          photoList.appendChild(item);
        });

        photoList.hidden = selectedPhotos.length === 0;
        photoStatus.textContent = message || formatPhotoStatus(selectedPhotos);
      }

      photoInput.addEventListener('change', function () {
        if (photoInput.disabled) return;
        clearFormError();
        var incomingPhotos = Array.prototype.slice.call(photoInput.files || []);
        var selectedKeys = selectedPhotos.map(photoKey);
        var duplicateCount = 0;

        photoInput.value = '';
        incomingPhotos = incomingPhotos.filter(function (file) {
          var key = photoKey(file);
          if (selectedKeys.indexOf(key) !== -1) {
            duplicateCount += 1;
            return false;
          }
          selectedKeys.push(key);
          return true;
        });

        var candidatePhotos = selectedPhotos.concat(incomingPhotos);
        var message = photoValidationMessage(candidatePhotos);
        photoInput.setCustomValidity('');

        if (message) {
          renderPhotos(message);
          return;
        }

        selectedPhotos = candidatePhotos;
        renderPhotos(duplicateCount
          ? (duplicateCount === 1 ? 'That photo is already selected. ' : 'Those photos are already selected. ') + formatPhotoStatus(selectedPhotos)
          : '');
      });

      photoList.addEventListener('click', function (e) {
        var removeButton = e.target.closest('.photo-remove');
        if (!removeButton || photoInput.disabled) return;

        var index = Number(removeButton.dataset.photoIndex);
        if (!Number.isInteger(index) || !selectedPhotos[index]) return;

        selectedPhotos.splice(index, 1);
        photoInput.setCustomValidity('');
        clearFormError();
        renderPhotos();
        photoInput.focus();
      });


  return {
    appendTo(data) {
      data.delete(photoInput.name);
      selectedPhotos.forEach(file => data.append(photoInput.name, file, file.name));
    },
    clear() { selectedPhotos = []; photoInput.value = ''; renderPhotos(); },
    setDisabled(disabled) {
      photoInput.disabled = disabled;
      photoList.querySelectorAll('button').forEach(button => { button.disabled = disabled; });
    }
  };
})();