text: /**
   * @module nyc/LocalStorage
   */
  
  import $ from 'jquery'
  import nyc from 'nyc'
  
  /**
   * @desc Class to provide access to localStorage and filesystem
   * @public
   * @class
   */
  class LocalStorage {
    /**
     * @desc Check if download is available
     * @public
     * @method
     * @return {boolean} True if download is available
     */
    canDownload() {
      return 'download' in $('<a></a>').get(0)
    }
    /**
     * @desc Save GeoJSON data to a file prompting the user with a file dialog
     * @public
     * @method
     * @param {string} name File name
     * @param {string} data JSON data to write to file
     */
    saveGeoJson(name, data) {
      const href = `data:application/jsoncharset=utf-8,${encodeURIComponent(data)}`
      const a = $('<a class="file-dwn"><img></a>')
      $('body').append(a)
      a.attr({href: href, download: name}).find('img').trigger('click')
      a.remove()
    }
    /**
     * @desc Set data in browser's localStorage if available
     * @public
     * @method
     * @param {string} key Storage key
     * @param {string} data Data to store
     */
    setItem(key, data) {
      if ('localStorage' in window) {
        localStorage.setItem(key, data)
      }
    }
    /**
     * @desc Get data from browser's localStorage if available
     * @public
     * @method
     * @param {string} key Storage key
     * @return {string} The value of the key in local storage
     */
    getItem(key) {
      if ('localStorage' in window) {
        return localStorage.getItem(key)
      }
    }
    /**
     * @desc Remove data from browser's localStorage if available
     * @public
     * @method
     * @param {string} key Storage key
     */
    removeItem(key) {
      if ('localStorage' in window) {
        localStorage.removeItem(key)
      }
    }
    /**
     * @desc Open a text file from filesystem
     * @public
     * @method
     * @param {module:nyc/LocalStorage~LocalStorage#readTextFileCallback} callback The callback function to receive file content
     * @param {File=} file File - if not provided the user will be prompted with a file dialog
     */
    readTextFile(callback, file) {
      const reader = new FileReader()
      reader.onload = () => {
        callback(reader.result)
      }
      if (!file) {
        const input = $('<input class="file-in" type="file">')
        $('body').append(input)
        input.change(event => {
          input.remove()
          reader.readAsText(event.target.files[0])
        })
        input.trigger('click')
      } else {
        reader.readAsText(file)
      }
    }
    /**
     * @desc Open a GeoJSON file from filesystem
     * @public
     * @method
     * @param {ol.Map|L.Map} map The map in which the data will be displayed
     * @param {module:nyc/LocalStorage~LocalStorage#loadGeoJsonFileCallback} callback The callback function to receive the new layer
     * @param {File=} file File - if not provided the user will be prompted with a file dialog
     */
    loadGeoJsonFile(map, callback, file) {
      this.readTextFile(geoJson => {
        const layer = this.addToMap(map, geoJson)
        if (callback) {
          callback(layer)
        }
      }, file)
    }
    /**
     * @desc Open a shapefile from filesystem
     * @public
     * @method
     * @param {ol.Map|L.Map} map The map in which the data will be displayed
     * @param {module:nyc/LocalStorage~LocalStorage#loadShapeFileCallback} callback The callback function to receive the new layer
     * @param {FileList=} files Files (.shp, .dbf, .prj) - if not provided the user will be prompted with a file dialog
     * @see https://github.com/mbostock/shapefile
     */
    loadShapeFile(map, callback, files) {
      if (!files) {
        const me = this
        const input = $('<input class="file-in" type="file" multiple>')
        $('body').append(input)
        input.change(event => {
          me.getShpDbfPrj(map, event.target.files, callback)
          input.remove()
        })
        input.trigger('click')
      } else {
        this.getShpDbfPrj(map, files, callback)
      }
    }
    /**
     * @private
     * @method
     * @param {ol.Map|L.Map} map The map
     * @param {FileList} files List of files
     * @param {function} callback Callback function
    */
    getShpDbfPrj(map, files, callback) {
      let shp, dbf, prj
      Object.values(files).forEach(file => {
        const name = file.name
        const ext = name.substr(name.length - 4).toLowerCase()
        if (ext === '.shp') {
          shp = file
        } else if (ext === '.dbf') {
          dbf = file
        } else if (ext === '.prj') {
          prj = file
        }
      })
      if (shp) {
        this.readPrj(prj, projcs => {
          this.readShpDbf(map, shp, dbf, projcs, callback)
        })
      } else if (callback) {
        callback()
      }
    }
    /**
     * @private
     * @method
     * @param {File} prj Prj file
     * @param {function} callback Callback function
    */
    readPrj(prj, callback) {
      if (prj) {
        this.readTextFile(callback, prj)
      } else {
        callback()
      }
    }
    /**
     * @private
     * @method
     * @param {ol.Map|L.Map} map The map
     * @param {File} shp The shp file
     * @param {File} dbf The dbf file
     * @param {string} projcs Proj coordinate reference system
     * @param {function} callback Callback function
    */
    readShpDbf(map, shp, dbf, projcs, callback) {
      let shpBuffer, dbfBuffer
      const shpReader = new FileReader()
      shpReader.onload = event => {
        shpBuffer = event.target.result
        if (dbfBuffer || !dbf) {
          this.readShp(map, shpBuffer, dbfBuffer, projcs, callback)
        }
      }
      const dbfReader = new FileReader()
      dbfReader.onload = event => {
        dbfBuffer = event.target.result
        if (shpBuffer) {
          this.readShp(map, shpBuffer, dbfBuffer, projcs, callback)
        }
      }
      shpReader.readAsArrayBuffer(shp)
      if (dbf) {
        dbfReader.readAsArrayBuffer(dbf)
      }
    }
    /**
     * @private
     * @method
     * @param {ol.Map|L.Map} map The map
     * @param {string|ArrayBuffer} shp The shp file
     * @param {string|ArrayBuffer} dbf The dbf file
     * @param {string} projcs Proj coordinate reference system
     * @param {function} callback Callback function
    */
    readShp(map, shp, dbf, projcs, callback) {
      const me = this
      const features = []
      LocalStorage.shapefile.open(shp, dbf)
        .then(source => {
          source.read()
            .then(function collect(result) {
              if (result.done) {
                const layer = me.addToMap(map, features, projcs)
                if (callback) {
                  callback(layer)
                }
                return
              } else {
                features.push(result.value)
              }
              return source.read().then(collect)
            })
        }).catch(error => {
          console.error(error)
        })
    }
    /**
     * @public
     * @abstract
     * @method
     * @param {ol.Map|L.Map} map The map on which to display the new layer
     * @param {string|Array<Object>} features The features from which to create the new layer
     * @param {string=} projcs The projection
     * @return {ol.layer.Vector|L.Layer} The new layer
    */
    addToMap(map, features, projcs) {
      throw 'Not implemented'
    }
    /**
     * @desc Add a new projection to proj4 and return the code
     * @access protected
     * @method
     * @param {string} projcs The projecion as defined in a prj file
     * @param {Object} proj4 The proj4 instance
     * @return {string|undefined} The code for the new projection
     */
    customProj(projcs, proj4) {
      if (projcs) {
        const code = nyc.nextId('shp:prj')
        proj4.defs(code, projcs)
        return code
      }
    }
  }
  
  /**
   * @desc Callback for {@link module:nyc/LocalStorage~LocalStorage#readTextFile}
   * @public
   * @callback module:nyc/LocalStorage~LocalStorage#readTextFileCallback
   * @param {string} fileContents The text contained in the file
   */
  
  /**
   * @desc Callback for {@link module:nyc/LocalStorage~LocalStorage#loadGeoJsonFile}
   * @public
   * @callback module:nyc/LocalStorage~LocalStorage#loadGeoJsonFileCallback
   * @param {ol.layer.Vector|L.Layer} layer The layer created from the GeoJSON file
   */
  
  /**
   * @desc Callback for {@link module:nyc/LocalStorage~LocalStorage#loadShapeFile}
   * @public
   * @callback module:nyc/LocalStorage~LocalStorage#loadShapeFileCallback
   * @param {ol.layer.Vector|L.Layer} layer The layer created from the shapefile
   */
  
  LocalStorage.shapefile = require('shapefile')
  
  export default LocalStorage
  