{"version":3,"sources":["webpack:///./app/javascript/packs/validateFileInputs.js"],"names":["MAX_FILE_SIZE_MB","Object","freeze","image","video","PERMITTED_FILE_TYPES","addErrorMessage","fileInput","msg","fileInputField","error","document","createElement","style","color","innerHTML","classList","add","parentNode","append","validateFileSize","file","fileType","maxFileSizeMb","dataset","fileSizeErrorHandler","fileSizeMb","size","toFixed","isValidFileSize","Number","value","errorMessage","handleFileSizeError","validateFileType","permittedFileTypes","JSON","parse","fileTypeErrorHandler","isValidFileType","includes","join","handleFileTypeError","validateFileNameLength","maxFileNameLength","fileNameLengthErrorHandler","isValidFileNameLength","name","length","handleFileNameLengthError","validateFileInput","isValidFileInput","querySelector","remove","removeErrorMessage","files","Array","from","i","type","split","validateFileInputs","validFileInputs","fileInputs","querySelectorAll","forEach","addEventListener"],"mappings":"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","file":"js/validateFileInputs-0b36624e58281af6ace2.chunk.js","sourcesContent":["/**\n * @file Manages logic to validate file uploads client-side. In general, the\n * validations work by looping over input form fields with a type of file and\n * checking the size and format of the files upload by the user.\n */\n\n/**\n * An object containing the top level MIME type as the key and the max file\n * size in MB for the value. To use a different value than these defaults,\n * simply add a data-max-file-mb attribute to the input form field with the\n * max file size in MB. If that attribute is found, it takes priority over these\n * defaults.\n *\n * @constant {Object.}\n */\nconst MAX_FILE_SIZE_MB = Object.freeze({\n image: 25,\n video: 50,\n});\n\n/**\n * Permitted file types using the top level MIME type (i.e. image for\n * image/png). To specify permitted file types, simply add a\n * data-permitted-file-types attribute to the input form field as an Array of\n * strings specifying the top level MIME types that are permitted.\n *\n * @constant {string[]}\n */\nconst PERMITTED_FILE_TYPES = ['image'];\n\n/**\n * The maximum length of the file name to prevent errors on the backend when a\n * file name is too long.\n *\n * @constant {number}\n */\nconst MAX_FILE_NAME_LENGTH = 250;\n\n/**\n * Removes any pre-existing error messages from the DOM related to file\n * validation.\n *\n * @param {HTMLElement} fileInput - An input form field with type of file\n */\nfunction removeErrorMessage(fileInput) {\n const errorMessage = fileInput.parentNode.querySelector(\n 'div.file-upload-error',\n );\n\n if (errorMessage) {\n errorMessage.remove();\n }\n}\n\n/**\n * Adds error messages in the form of a div with red text.\n *\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {string} msg - The error message to be displayed to the user\n *\n * @returns {HTMLElement} The error element that was added to the DOM\n */\nfunction addErrorMessage(fileInput, msg) {\n const fileInputField = fileInput;\n const error = document.createElement('div');\n error.style.color = 'red';\n error.innerHTML = msg;\n error.classList.add('file-upload-error');\n\n fileInputField.parentNode.append(error);\n}\n\n/**\n * Handles errors for files that are too large.\n *\n * @param {object} fileSizeErrorHandler - A custom function to be ran after the default error handling\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {number} fileSizeMb - The size of the file in MB\n * @param {?number} maxFileSizeMb - The max file size limit in MB\n */\nfunction handleFileSizeError(\n fileSizeErrorHandler,\n fileInput,\n fileSizeMb,\n maxFileSizeMb,\n) {\n const fileInputField = fileInput;\n fileInputField.value = null;\n\n if (fileSizeErrorHandler) {\n fileSizeErrorHandler();\n } else {\n let errorMessage = `File size too large (${fileSizeMb} MB).`;\n\n // If a user uploads a file type that we haven't defined a max size limit for then maxFileSizeMb\n // could be NaN\n if (maxFileSizeMb >= 0) {\n errorMessage += ` The limit is ${maxFileSizeMb} MB.`;\n }\n\n addErrorMessage(fileInput, errorMessage);\n }\n}\n\n/**\n * Handles errors for files that are not a valid format.\n *\n * @param {object} fileSizeErrorHandler - A custom function to be ran after the default error handling\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {string} fileType - The top level file type (i.e. image for image/png)\n * @param {string[]} permittedFileTypes - The top level file types (i.e. image for image/png) that are permitted\n */\nfunction handleFileTypeError(\n fileTypeErrorHandler,\n fileInput,\n fileType,\n permittedFileTypes,\n) {\n const fileInputField = fileInput;\n fileInputField.value = null;\n\n if (fileTypeErrorHandler) {\n fileTypeErrorHandler();\n } else {\n const errorMessage = `Invalid file format (${fileType}). Only ${permittedFileTypes.join(\n ', ',\n )} files are permitted.`;\n addErrorMessage(fileInput, errorMessage);\n }\n}\n\n/**\n * Handles errors for files with names that are too long.\n *\n * @param {object} fileNameLengthErrorHandler - A custom function to be ran after the default error handling\n * @param {HTMLElement} fileInput - An input form field with type of file\n * @param {number} maxFileNameLength - The max number of characters permitted for a file name\n */\nfunction handleFileNameLengthError(\n fileNameLengthErrorHandler,\n fileInput,\n maxFileNameLength,\n) {\n const fileInputField = fileInput;\n fileInputField.value = null;\n\n if (fileNameLengthErrorHandler) {\n fileNameLengthErrorHandler();\n } else {\n const errorMessage = `File name is too long. It can't be longer than ${maxFileNameLength} characters.`;\n addErrorMessage(fileInput, errorMessage);\n }\n}\n\n/**\n * Validates the file size and handles the error if it's invalid.\n *\n * @external File\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File File}\n *\n * @param {File} file - The file attached by the user\n * @param {string} fileType - The top level file type (i.e. image for image/png)\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if the file is too big. Otherwise, returns true.\n */\nfunction validateFileSize(file, fileType, fileInput) {\n let { maxFileSizeMb } = fileInput.dataset;\n\n const { fileSizeErrorHandler } = fileInput.dataset;\n\n const fileSizeMb = (file.size / (1024 * 1024)).toFixed(2);\n maxFileSizeMb = Number(maxFileSizeMb || MAX_FILE_SIZE_MB[fileType]);\n\n const isValidFileSize = fileSizeMb <= maxFileSizeMb;\n\n if (!isValidFileSize) {\n handleFileSizeError(\n fileSizeErrorHandler,\n fileInput,\n fileSizeMb,\n maxFileSizeMb,\n );\n }\n\n return isValidFileSize;\n}\n\n/**\n * Validates the file type and handles the error if it's invalid.\n *\n * @external File\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File File}\n *\n * @param {File} file - The file attached by the user\n * @param {string} fileType - The top level file type (i.e. image for image/png)\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if the files is an invalid format. Otherwise, returns true.\n */\nfunction validateFileType(file, fileType, fileInput) {\n let { permittedFileTypes } = fileInput.dataset;\n\n if (permittedFileTypes) {\n permittedFileTypes = JSON.parse(permittedFileTypes);\n }\n\n permittedFileTypes = permittedFileTypes || PERMITTED_FILE_TYPES;\n\n const { fileTypeErrorHandler } = fileInput.dataset;\n\n const isValidFileType = permittedFileTypes.includes(fileType);\n\n if (!isValidFileType) {\n handleFileTypeError(\n fileTypeErrorHandler,\n fileInput,\n fileType,\n permittedFileTypes,\n );\n }\n\n return isValidFileType;\n}\n\n/**\n * Validates the length of the file name and handles the error if it's invalid.\n *\n * @external File\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/File File}\n *\n * @param {File} file - The file attached by the user\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if the file name is too long. Otherwise, returns true.\n */\nfunction validateFileNameLength(file, fileInput) {\n let { maxFileNameLength } = fileInput.dataset;\n\n maxFileNameLength = Number(maxFileNameLength || MAX_FILE_NAME_LENGTH);\n\n const { fileNameLengthErrorHandler } = fileInput.dataset;\n\n const isValidFileNameLength = file.name.length <= maxFileNameLength;\n\n if (!isValidFileNameLength) {\n handleFileNameLengthError(\n fileNameLengthErrorHandler,\n fileInput,\n maxFileNameLength,\n );\n }\n\n return isValidFileNameLength;\n}\n\n/**\n * This is the core function to handle validations of uploaded files. It loops\n * through all the uploaded files for the given fileInput and checks the file\n * size, file format, and file name length. If a file fails a validation, the\n * error is handled.\n *\n * @param {HTMLElement} fileInput - An input form field with type of file\n *\n * @returns {Boolean} Returns false if any files failed validations. Otherwise, returns true.\n */\nfunction validateFileInput(fileInput) {\n let isValidFileInput = true;\n\n removeErrorMessage(fileInput);\n const files = Array.from(fileInput.files);\n\n for (let i = 0; i < files.length; i += 1) {\n const file = files[i];\n const fileType = file.type.split('/')[0];\n\n const isValidFileSize = validateFileSize(file, fileType, fileInput);\n\n if (!isValidFileSize) {\n isValidFileInput = false;\n break;\n }\n\n const isValidFileType = validateFileType(file, fileType, fileInput);\n\n if (!isValidFileType) {\n isValidFileInput = false;\n break;\n }\n\n const isValidFileNameLength = validateFileNameLength(file, fileInput);\n\n if (!isValidFileNameLength) {\n isValidFileInput = false;\n break;\n }\n }\n\n return isValidFileInput;\n}\n\n/**\n * This function is designed to be exported in areas where we are doing more\n * custom implementations of file uploading using Preact. It can then be used\n * in Preact event handlers. It loops through all file input fields on the DOM\n * and validates any attached files.\n *\n * @returns {Boolean} Returns false if any files failed validations. Otherwise, returns true.\n */\nexport function validateFileInputs() {\n let validFileInputs = true;\n const fileInputs = document.querySelectorAll('input[type=\"file\"]');\n\n for (let i = 0; i < fileInputs.length; i += 1) {\n const fileInput = fileInputs[i];\n const validFileInput = validateFileInput(fileInput);\n\n if (!validFileInput) {\n validFileInputs = false;\n break;\n }\n }\n\n return validFileInputs;\n}\n\n// This is written so that it works automagically by just including this pack\n// in a view.\nconst fileInputs = document.querySelectorAll('input[type=\"file\"]');\n\nfileInputs.forEach((fileInput) => {\n fileInput.addEventListener('change', () => {\n validateFileInput(fileInput);\n });\n});\n"],"sourceRoot":""}