UNPKG

samagra-x-enketo-core

Version:

Extensible Enketo form engine

190 lines (171 loc) 5.82 kB
/** * @module fileManager * * @description Simple file manager with cross-browser support. That uses the FileReader * to create previews. Can be replaced with a more advanced version that * obtains files from storage. * * The replacement should support the same public methods and return the same * types. */ import $ from 'jquery'; import { getFilename, dataUriToBlobSync } from './utils'; const fileManager = {}; import { t } from 'enketo/translator'; const URL_RE = /[a-zA-Z0-9+-.]+?:\/\//; /** * @static * @function init * * @description Initialize the file manager. * * @return {Promise|boolean|Error} promise boolean or rejection with Error */ fileManager.init = () => { return Promise.resolve( true ); }; /** * @static * @function isWaitingForPermissions * * @description Whether the filemanager is waiting for user permissions * * @return {boolean} [description] */ fileManager.isWaitingForPermissions = () => { return false; }; /** * @static * @function getFileUrl * * @description Obtains a URL that can be used to show a preview of the file when used * as a src attribute. * * It is meant for media previews and media downloads. * * @param {?string|object} subject - File or filename in local storage * @return {Promise|string|Error} promise url string or rejection with Error */ fileManager.getFileUrl = subject => { return new Promise( ( resolve, reject ) => { let error; if ( !subject ) { resolve( null ); } else if ( typeof subject === 'string' ) { // TODO obtain from storage as http URL or objectURL // or from model for default binary files // Very crude URL checker which is fine for now, // because at this point we don't expect anything other than jr:// if ( URL_RE.test( subject ) ) { resolve( subject ); } else { reject( 'no!' ); } } else if ( typeof subject === 'object' ) { if ( fileManager.isTooLarge( subject ) ) { error = new Error( t( 'filepicker.toolargeerror', { maxSize: fileManager.getMaxSizeReadable() } ) ); reject( error ); } else { resolve( URL.createObjectURL( subject ) ); } } else { reject( new Error( 'Unknown error occurred' ) ); } } ); }; /** * @static * @function getObjectUrl * * @description Similar to getFileURL, except that this one is guaranteed to return an objectURL * * It is meant for loading images into a canvas. * * @param {?string|object} subject - File or filename in local storage * @return {Promise|string|Error} promise url string or rejection with Error */ fileManager.getObjectUrl = subject => fileManager.getFileUrl( subject ) .then( url => { if ( /https?:\/\//.test( url ) ) { return fileManager.urlToBlob( url ).then( URL.createObjectURL ); } return url; } ); /** * @static * @function urlToBlob * * @param {string} url - url to get * @return {Promise} promise of XMLHttpRequesting given url */ fileManager.urlToBlob = url => { const xhr = new XMLHttpRequest(); return new Promise( resolve => { xhr.open( 'GET', url ); xhr.responseType = 'blob'; xhr.onload = () => { resolve( xhr.response ); }; xhr.send(); } ); }; /** * @static * @function getCurrentFiles * * @description Obtain files currently stored in file input elements of open record * * @return {Array.File} array of files */ fileManager.getCurrentFiles = () => { const files = []; // Get any files inside file input elements or text input elements for drawings. $( 'form.or' ).find( 'input[type="file"]:not(.ignore), input[type="text"][data-drawing="true"]' ).each( function() { let newFilename; let file = null; let canvas = null; if ( this.type === 'file' ) { file = this.files[ 0 ]; // Why doesn't this fail for empty file inputs? } else if ( this.value ) { canvas = $( this ).closest( '.question' )[ 0 ].querySelector( '.draw-widget canvas' ); if ( canvas && !URL_RE.test( this.value ) ) { // TODO: In the future, we could simply do canvas.toBlob() instead file = dataUriToBlobSync( canvas.toDataURL() ); file.name = this.value; } } if ( file && file.name ) { // Correct file names by adding a unique-ish postfix // First create a clone, because the name property is immutable // TODO: in the future, when browser support increase we can invoke // the File constructor to do this. newFilename = getFilename( file, this.dataset.filenamePostfix ); // If file is resized, get Blob representation of data URI if ( this.dataset.resized && this.dataset.resizedDataURI ) { file = dataUriToBlobSync( this.dataset.resizedDataURI ); } file = new Blob( [ file ], { type: file.type } ); file.name = newFilename; files.push( file ); } } ); return files; }; /** * @static * @function isTooLarge * * @description Placeholder function to check if file size is acceptable. * * @return {boolean} whether file is too large */ fileManager.isTooLarge = () => { return false; }; /** * @static * @function getMaxSizeReadable * * @description Replace with function that determines max size published in OpenRosa server response header. * * @return {string} human radable maximiym size */ fileManager.getMaxSizeReadable = () => { return `${5}MB`; }; export default fileManager;