From d12bc6b752329fce025c2a039cc542da6d591ec4 Mon Sep 17 00:00:00 2001 From: johndoe Date: Thu, 5 Sep 2019 18:34:08 +0300 Subject: [PATCH] WIP --- code/utils_file.js | 236 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/code/utils_file.js b/code/utils_file.js index e17ffe279..a7ec14070 100644 --- a/code/utils_file.js +++ b/code/utils_file.js @@ -29,3 +29,239 @@ window.saveFile = function (data,filename,dataType) { saveAs([data], filename, dataType); } }; + + +/* + * @class FileReader + * @aka L.FileReader + * @inherits Evented + * + * Leaflet thin wrapper over [`FileReader`](https://w3c.github.io/FileAPI/#APIASynch) Web API. + * + * @example + * + * ```js + * L.fileReader(file) + * .on('load',function (e) { + * console.log(e.file.name,e.reader.result) + * }); + * ``` + */ +L.FileReader = L.Evented.extend({ + options: { + // encoding: 'utf-8' // todo + + // @option readAs: String = 'readAsText' + // Function to use for file reading. + readAs: 'readAsText' + }, + + initialize: function (file, options) { + this._setOptions(options); + var reader = new FileReader(); + this._eventTypes.forEach(function (type) { + reader.addEventListener(type,this._fire.bind(this,type)); + },this); + this.reader = reader; + this.once('loadstart',function () { + if (!this.listens('error',true)) { + this.on('error',this._onerror); + } + }); + if (file) { this.read(file); } + }, + + _setOptions: function (options) { + if (typeof options === 'string') { + options = {readAs: options}; + } + return L.Util.setOptions(this, options); + }, + + // @method read(file?, options?: Object or String): this + // Starts reading the contents of the `file` using [reader method](https://w3c.github.io/FileAPI/#reading-a-file) specified in options. + // `file` argument is optional only if already specified (in constructor, or in previous method call). + // `options` argument has the same meaning as in constructor. + read: function (file, options) { + if (options) { this._setOptions(options); } + if (file) { this.file = file; } + this.reader[this.options.readAs](this.file); + return this; + }, + + _onerror: function (e) { + console.warn('Error loading file: ',e.file.name,'\n',e.reader.error.message); // eslint-disable-line no-console + }, + + // @event [abort, error, load, loadstart, loadend, progress](https://w3c.github.io/FileAPI/#events): Event + // Event object has following additional properties: + // `reader`: raw instance of [`FileReader`](https://w3c.github.io/FileAPI/#APIASynch) interface + // `file`: raw instance of [`File`/`Blob`](https://w3c.github.io/FileAPI/#dfn-file) + // `originalEvent`: raw [event](https://w3c.github.io/FileAPI/#events) + // Note: if no handlers found for `error` then default one will be attached (`console.warn`) + _eventTypes: ['abort','error','load','loadstart','loadend','progress'], + + _fire: function (type,event) { + if (!this.listens(type,true)) { return; } + this.fire(type,Object.assign({originalEvent: event},this),true); + } +}); + +// @factory L.fileReader(file?: Blob, options?: Object or String) +// Instantiates a `FileReader` object given the [`File`/`Blob`](https://w3c.github.io/FileAPI/#dfn-file) +// and optionally an object literal with `options`. +// If `file` argument is specified, then `read` method is called immediately. +// Note: it's possible to specify `readAs` directly instead of full `options` object +L.fileReader = function (file, options) { + return new L.FileReader(file, options); +}; + +/* + * @class FileListReader + * @aka L.FileListReader + * @inherits Evented + * + * Used to handle [FileList](https://w3c.github.io/FileAPI/#filelist-section), processing it as whole. + * Each `File` will be loaded with `L.FileReader` using common event handlers, + * including propagated from underlying `L.FileReader`. + * + * @example + * + * ```js + * L.FileListReader(fileList) + * .on('load',function (e) { + * console.log(e.file.name,e.reader.result) + * }); + * .on('loaded',function (e) { + * console.log('All done!') + * }); + * .load(); + * ``` + */ +L.FileListReader = L.Evented.extend({ + options: { + // @option readAs: String = 'readAsText' + // Function to use for file reading. + readAs: 'readAsText' + }, + + initialize: function (fileList, options) { + L.Util.setOptions(this, options); + this._readers = []; + this.once('loadstart',function () { + debugger; + if (this.listens('loaded')) { + this.on('loadend',this._onloadend); + } + }); + if (fileList) { + this.load(fileList); + } + }, + + // @method load(fileList: FileList) + // Starts loading files listed in `fileList` argument. + // ??Note: all event handlers expected to be already attached **before** this method call. + load: function (fileList) { + if (!fileList) { + throw new Error('`fileList` arg not specified'); + } + this._toload = fileList.length; + Array.prototype.forEach.call(fileList,function (file) { + var options = this.options; + if (this.listens('init')) { + options = L.extend({},options); + var stop; + // @event init: Event + // Fired for each file _before_ reading. + // Event object has additional property `file`, containing processed [`File`](https://w3c.github.io/FileAPI/#dfn-file) object. + // `stop()`: do not read current file + // `setOptions(options)`: use special options for current file + // (`this.options` extended with given `options` argument. + this.fire('init',{ + file: file, + stop: function () { stop = true; }, + setOptions: function (o) { L.extend(options,o); } + }); + if (stop) { this._onloadend(); return; } + } + var reader = L.fileReader(file,options) + .addEventParent(this); + this._readers.push(reader); + },this); + return this; //???? + }, + + // @event loaded: Event + // Fired after all files are processed (either with success or with error). + _onloadend: function () { + debugger; + this._toload--; + if (this._toload === 0) { this.fire('loaded'); } + } +}); + +// @factory L.fileListReader(fileList?: FileList, options?: Object) +// Instantiates a `FileListReader` object given the [`FileList`](https://w3c.github.io/FileAPI/#filelist-section) +// and optionally an object literal with `options`. +// If `fileList` argument is specified, then `load` method is called immediately. +L.fileListReader = function (fileList, options) { + return new L.FileListReader(fileList, options); +}; + +function chooseFiles (options,callback) { + // assert callback + var input = document.createElement('input'); + input.type='file'; + if (options) { + input.accept = options.accept; + input.multiple = options.multiple; + } + input.style.display = 'none'; + input.addEventListener('change', function () { + callback(this.files); + }, false); + document.body.appendChild(input); + input.click(); + input.remove(); +} + +window.loadFiles = function (options) { // compatibility shim for IITCm + var loader; + if (window.requestFile) { + loader = new L.Evented(); + window.requestFile(function (filename,text) { + var file = { + name: filename, + size: text.length(), + type: '' + }; + var stop; + this.fire('init',{ + file: file, + stop: function () { stop = true; } + }); + if (!stop) { + this.fire('load',{ + file: file, + reader: {result: text}, + }); + } + this.fire('loaded'); + }.bind(this)); + return loader; + } + loader = L.fileListReader(); + chooseFiles(options,loader.load.bind(loader)); + return loader; +}; + +/* +var options = {accept:'.json', multiple:true}; +loadFiles(options) + .on('init',function (e) { + if (e.file.name === 'package.json') e.stop(); + }) + .on('load',console.log) + .on('loaded',console.log); +*/ \ No newline at end of file