cozy/cozy-mobile

View on GitHub
src/app/replicator/filesystem.coffee

Summary

Maintainability
Test Coverage
DOWNLOADS_FOLDER = 'cozy-downloads'

log = require('../lib/persistent_log')
    prefix: "replicator filesystem"
    date: true

module.exports = fs = {}


module.exports.getFileSystem = (callback) ->
    onSuccess = (dir) -> callback null, dir.filesystem
    onError = (err) -> callback err
    # flag for developpement in browser
    if window.isBrowserDebugging
        __chromeSafe()
        return initDebugFS onSuccess, onError
    # The cache folder isnt indexed in media (ie .nomedia file isnt required)
    # TODO: stub when externalRootDirectory is null (has in emulators).
    # CacheDirectory is private to the app, so files won't open.
    uri = cordova.file.externalCacheDirectory or cordova.file.cacheDirectory
    window.resolveLocalFileSystemURL uri, onSuccess, onError


readable = (err) ->
    for name, code of FileError when code is err.code
        msg = 'File error: ' + name.replace('_ERR', '').replace('_', ' ')
        err.message = msg
        return err

    return new Error JSON.stringify err

module.exports.initialize = (callback) ->
    fs.getFileSystem (err, filesystem) ->
        return callback readable err if err
        window.FileTransfer.fs = filesystem
        fs.getOrCreateSubFolder filesystem.root, DOWNLOADS_FOLDER, \
                (err, downloads) ->
            return callback readable err if err
            fs.getChildren downloads, (err, children) ->
                return callback readable err if err
                callback null, downloads, children


module.exports.delete = (entry, callback) ->
    onSuccess = -> callback null
    onError = (err) -> callback err
    entry.remove onSuccess, onError

module.exports.getFile = (parent, name, callback) ->
    onSuccess = (entry) -> callback null, entry
    onError = (err) -> callback err
    parent.getFile name, null, onSuccess, onError

module.exports.moveTo = (entry, directory, name, callback) ->
    onSuccess = (entry) -> callback null, entry
    onError = (err) -> callback err
    entry.moveTo directory, name, onSuccess, onError


module.exports.getDirectory = (parent, name, callback) ->
    onSuccess = (entry) -> callback null, entry
    onError = (err) -> callback err
    parent.getDirectory name, {}, onSuccess, onError

module.exports.getOrCreateSubFolder = (parent, name, callback) ->
    onSuccess = (entry) -> callback null, entry
    onError = (err) -> callback readable err
    parent.getDirectory name, {create: true}, onSuccess, (err) ->
        return callback err if err.code isnt FileError.PATH_EXISTS_ERR
        parent.getDirectory name, {}, onSuccess, (err) ->
            return callback err if err.code isnt FileError.NOT_FOUND_ERR
            # directory exists, but cant be open
            return callback new Error t 'filesystem bug error'


module.exports.getChildren = (directory, callback) ->
    # assume we are using cordova-file-plugin and call reader only once
    reader = directory.createReader()
    onSuccess = (entries) -> callback null, entries
    onError = (err) -> callback readable err
    reader.readEntries onSuccess, onError

module.exports.rmrf = (directory, callback) ->
    onError = (err) -> callback readable err
    onSuccess = -> callback null
    directory.removeRecursively onSuccess, onError

module.exports.freeSpace = (callback) ->
    onError = (err) -> callback readable err
    onSuccess = -> callback null
    cordova.exec onSuccess, onError, 'File', 'getFreeDiskSpace', []


module.exports.entryFromPath = (path, callback) ->
    onSuccess = (entry) -> callback null, entry
    onError = (err) -> callback readable err
    resolveLocalFileSystemURL 'file://' + path, onSuccess, onError

module.exports.fileFromEntry = (entry, callback) ->
    onSuccess = (file) -> callback null, file
    onError = (err) -> callback readable err
    entry.file onSuccess, onError

module.exports.contentFromFile = (file, callback) ->
    reader = new FileReader()
    reader.onerror = callback
    reader.onload = -> callback null, reader.result
    reader.readAsArrayBuffer file

module.exports.getFileAsBlob = (file, callback) ->
    reader = new FileReader()
    reader.onerror = callback
    reader.onload = ->
        blob = new Blob [reader.result], type: file.type
        callback null, blob

    reader.readAsArrayBuffer file

module.exports.getFileFromPath = (path, callback) ->
    fs.entryFromPath path, (err, entry) ->
        return callback err if err
        fs.fileFromEntry entry, callback

module.exports.metadataFromEntry = (entry, callback) ->
    onSuccess = (file) -> callback null, file
    onError = (err) -> callback readable err
    entry.getMetadata onSuccess, onError



module.exports.download = (options, progressback, callback) ->

    errors = [
        'An error happened (UNKNOWN)',
        'An error happened (NOT FOUND)',
        'An error happened (INVALID URL)',
        #'This file isnt available offline', # TODO, fix in translation.
        'An error happened (CONNEXION ERROR)',
        'ABORTED'
        'An error happened (NOT MODIFIED)'
    ]

    {url, path, auth} = options

    onSuccess = (entry) -> callback null, entry
    onError = (err) -> callback new Error errors[err.code]

    ft = new FileTransfer()
    ft.onprogress = (e) ->
        if e.lengthComputable then progressback e.loaded, e.total
        else progressback 3, 10 #@TODO, better aproximation

    ft.download url, path, onSuccess, onError, true


# various patches to debug in chrome
__chromeSafe = ->
    window.initDebugFS = (onSuccess, onError) ->
        size = 5*1024*1024
        navigator.webkitPersistentStorage.requestQuota size, (granted) ->
            window.webkitRequestFileSystem window.PERSISTENT, granted
            , ((fs) -> onSuccess filesystem: fs ), onError
        , onError

    window.ImagesBrowser = getImageList: -> []

    window.FileTransfer = class FileTransfer
        download: (url, local, onSuccess, onError, _, options) ->
            xhr = new XMLHttpRequest()
            xhr.open 'GET', url, true
            xhr.overrideMimeType 'text/plain; charset=x-user-defined'
            xhr.responseType = "arraybuffer"
            xhr.setRequestHeader key, value for key, value of options.headers
            xhr.onreadystatechange = ->
                return unless xhr.readyState == 4
                FileTransfer.fs.root.getFile local, {create: true}, (entry) ->
                    entry.createWriter (writer) ->
                        writer.onwrite = -> onSuccess entry
                        writer.onerror = (err) -> onError err
                        bb = new BlobBuilder()
                        bb.append(xhr.response)
                        writer.write(bb.getBlob(mimetype))

                    , (err) -> onError err
                , (err) -> onError err
            xhr.send(null)

    window.device =
        model: "Debug Browser"
        platform: "Browser"
        uuid: '2323-5343-2323-3232'
        serial: '2323-5343-2323-3232'
        version: '1.2'
        manufacturer: 'Debug inc.'
        isVirtual: false
        cordova: '5.3'

    $(window).on 'click', (e) -> $(e.target).trigger 'tap'