(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@wm/core'), require('@awesome-cordova-plugins/file/ngx'), require('@awesome-cordova-plugins/app-version/ngx'), require('lodash-es'), require('@angular/common/http'), require('rxjs/operators'), require('@wm/components/base'), require('@awesome-cordova-plugins/file-opener/ngx'), require('@awesome-cordova-plugins/network/ngx')) :
    typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@wm/core', '@awesome-cordova-plugins/file/ngx', '@awesome-cordova-plugins/app-version/ngx', 'lodash-es', '@angular/common/http', 'rxjs/operators', '@wm/components/base', '@awesome-cordova-plugins/file-opener/ngx', '@awesome-cordova-plugins/network/ngx'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.wm = global.wm || {}, global.wm.mobile = global.wm.mobile || {}, global.wm.mobile.core = {}), global.ng.core, global.wm.core, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global._, global.ng.common.http, global.rxjs.operators, global.wm.components.base, global.awesomeCordova.plugins, global.awesomeCordova.plugins));
})(this, (function (exports, i0, i2$2, i1$1, i1, lodashEs, i2, operators, i4, i2$1, i3) { 'use strict';

    function _interopNamespaceDefault(e) {
        var n = Object.create(null);
        if (e) {
            Object.keys(e).forEach(function (k) {
                if (k !== 'default') {
                    var d = Object.getOwnPropertyDescriptor(e, k);
                    Object.defineProperty(n, k, d.get ? d : {
                        enumerable: true,
                        get: function () { return e[k]; }
                    });
                }
            });
        }
        n.default = e;
        return Object.freeze(n);
    }

    var i0__namespace = /*#__PURE__*/_interopNamespaceDefault(i0);
    var i2$2__namespace = /*#__PURE__*/_interopNamespaceDefault(i2$2);
    var i1$1__namespace = /*#__PURE__*/_interopNamespaceDefault(i1$1);
    var i1__namespace = /*#__PURE__*/_interopNamespaceDefault(i1);
    var i2__namespace = /*#__PURE__*/_interopNamespaceDefault(i2);
    var i4__namespace = /*#__PURE__*/_interopNamespaceDefault(i4);
    var i2$1__namespace = /*#__PURE__*/_interopNamespaceDefault(i2$1);
    var i3__namespace = /*#__PURE__*/_interopNamespaceDefault(i3);

    exports.FileType = void 0;
    (function (FileType) {
        FileType["AUDIO"] = "AUDIO";
        FileType["DOCUMENT"] = "DOCUMENT";
        FileType["IMAGE"] = "IMAGE";
        FileType["VIDEO"] = "VIDEO";
    })(exports.FileType || (exports.FileType = {}));
    const IMAGE_EXTENSIONS = ['gif', 'jpg', 'png', 'svg', 'webp', 'jpeg', 'jif', 'jfif', 'jfi'], VIDEO_EXTENSIONS = ['mp4', 'mpg', 'avi', 'wma', 'mp2', '3gp', '3g2', 'm4p', 'm4v', 'mpg', 'fiv'], AUDIO_EXTENSIONS = ['mp3', 'm4p', 'aiff', 'aa', 'aax', 'wma'];
    const APP_FOLDER_STRUCTURE = [{
            name: '{APP_NAME}',
            children: [{
                    name: 'Media',
                    children: [
                        {
                            name: '{APP_NAME} Images',
                            fileType: exports.FileType.IMAGE
                        },
                        {
                            name: '{APP_NAME} Audio',
                            fileType: exports.FileType.AUDIO
                        },
                        {
                            name: '{APP_NAME} Vedios',
                            fileType: exports.FileType.VIDEO
                        },
                        {
                            name: '{APP_NAME} Documents',
                            fileType: exports.FileType.DOCUMENT
                        }
                    ]
                }]
        }];
    class DeviceFileService {
        static { this.SERVICE_NAME = 'DeviceFileService'; }
        constructor(cordovaAppVersion, cordovaFile) {
            this.cordovaAppVersion = cordovaAppVersion;
            this.cordovaFile = cordovaFile;
            this._fileTypeVsPathMap = {
                'temporary': {},
                'persistent': {}
            };
        }
        addMediaToGallery(filePath) {
            if (i2$2.isAndroid() && this.isPersistentType(filePath)) {
                return new Promise((resolve, reject) => {
                    cordova.plugins.MediaScannerPlugin.scanFile(filePath, resolve, reject);
                });
            }
            return Promise.resolve();
        }
        appendToFileName(fileName, attachment) {
            let splits;
            attachment = attachment || '_' + lodashEs.now();
            fileName = fileName || 'noname';
            splits = fileName.split('.');
            if (splits.length > 1) {
                splits[splits.length - 2] = splits[splits.length - 2] + attachment;
                return splits.join('.');
            }
            return fileName + attachment;
        }
        clearTemporaryStorage() {
            return this.cordovaFile.removeRecursively(this.getTemporaryRootPath() + this._appName + '/', 'Media');
        }
        copy(persistent, sourceFilePath) {
            const sourceFilename = sourceFilePath.split('/').pop(), destFolder = this.findFolderPath(persistent, sourceFilename), sourceFolder = sourceFilePath.substring(0, sourceFilePath.lastIndexOf('/'));
            return this.newFileName(destFolder, sourceFilename)
                .then(destFilename => this.cordovaFile.copyFile(sourceFolder, sourceFilename, destFolder, destFilename)
                .then(() => destFolder + destFilename));
        }
        findFolderPath(persistent, fileName) {
            const typeMap = persistent ? this._fileTypeVsPathMap.persistent : this._fileTypeVsPathMap.temporary, fileType = this.findFileType(fileName);
            return typeMap[fileType] || typeMap[exports.FileType.DOCUMENT];
        }
        getPersistentRootPath() {
            return cordova.file.dataDirectory;
        }
        getTemporaryRootPath() {
            return cordova.file.cacheDirectory;
        }
        getUploadDirectory() {
            return this._uploadDir;
        }
        isPersistentType(filePath) {
            return filePath.startsWith(this.getPersistentRootPath());
        }
        isValidPath(filePath) {
            let folder, fileName;
            if (!filePath) {
                return Promise.reject('File path is required');
            }
            folder = filePath.substring(0, filePath.lastIndexOf('/') + 1);
            fileName = filePath.split('/').pop();
            return this.cordovaFile.checkFile(folder, fileName)
                .then(() => filePath);
        }
        listFiles(folder, search) {
            return new Promise((resolve, reject) => {
                resolveLocalFileSystemURL(folder, directory => {
                    if (!directory.files) {
                        directory.createReader().readEntries(entries => {
                            if (search) {
                                entries = entries.filter(e => e.name.match(search));
                            }
                            entries = entries.map(e => {
                                return {
                                    name: e.name,
                                    isDirectory: e.isDirectory,
                                    path: e.nativeURL
                                };
                            });
                            resolve(entries);
                        }, reject);
                    }
                    else {
                        resolve([]);
                    }
                }, reject);
            });
        }
        newFileName(folder, fileName) {
            return this.cordovaFile.checkFile(folder, fileName)
                .then(() => {
                const extIndex = fileName.lastIndexOf('.');
                if (extIndex > 0) {
                    fileName = fileName.substring(0, extIndex) + '_' + lodashEs.now() + '.' + fileName.substring(extIndex + 1);
                }
                else {
                    fileName = fileName + '_' + lodashEs.now();
                }
                return this.newFileName(folder, fileName);
            }, () => fileName);
        }
        removeFile(filePath) {
            const i = filePath.lastIndexOf('/'), dir = filePath.substring(0, i), file = filePath.substring(i + 1);
            return this.cordovaFile.removeFile(dir, file);
        }
        /**
         * removes the directory at the specified location.
         *
         * @param dirPath absolute path of directory
         */
        removeDir(dirPath) {
            const i = dirPath.lastIndexOf('/'), parentdir = dirPath.substring(0, i + 1), dir = dirPath.substring(i + 1), movedDir = dir + lodashEs.now();
            return this.cordovaFile.checkDir(parentdir, dir)
                .then(() => {
                /**
                 * If folder is remove directly without moving, then INVALID_MODIFICATION_ERR is thrown in android
                 * when a copy operation is done with the same directory name. To avoid this, directory will be moved
                 * first and removed.
                 */
                return this.cordovaFile.moveDir(parentdir, dir, parentdir, movedDir)
                    .then(() => this.cordovaFile.removeDir(parentdir, movedDir));
            }).catch(i2$2.noop);
        }
        start() {
            /**
             * Default READ_CHUNK_SIZE is 256 Kb. But with that setting readJson method is failing. This is an issue
             * with cordova file plugin. So, increasing it to 512 Kb to read large database schema files (>256 Kb).
             */
            FileReader.READ_CHUNK_SIZE = 512 * 1024;
            return Promise.resolve().then(() => {
                return this.cordovaAppVersion.getAppName();
            }).then(appName => {
                const promises = [];
                this._appName = appName;
                promises.push(this.createFolderIfNotExists(this.getTemporaryRootPath(), APP_FOLDER_STRUCTURE, this._fileTypeVsPathMap.temporary));
                promises.push(this.createFolderIfNotExists(this.getPersistentRootPath(), APP_FOLDER_STRUCTURE, this._fileTypeVsPathMap.persistent));
                promises.push(this.setupUploadDirectory());
                return Promise.all(promises);
            }).then(() => {
                if (i2$2.isAndroid()) {
                    // this is necessary to prevent multiple file permission popup.
                    return this.cordovaFile.readAsText(cordova.file.externalRootDirectory, 'random-file-for-permission').catch(i2$2.noop);
                }
            });
        }
        getServiceName() {
            return DeviceFileService.SERVICE_NAME;
        }
        createFolderIfNotExists(parent, folders, fileTypeLocationMap) {
            const childPromises = [];
            if (folders) {
                folders.forEach(folder => {
                    let folderPath;
                    folder.name = folder.name.replace('{APP_NAME}', this._appName);
                    folderPath = parent + folder.name + '/';
                    if (folder.fileType && !fileTypeLocationMap[folder.fileType]) {
                        fileTypeLocationMap[folder.fileType] = folderPath;
                    }
                    const p = this.cordovaFile.createDir(parent, folder.name, false)
                        .then(() => this.createFolderIfNotExists(folderPath, folder.children, fileTypeLocationMap), () => this.createFolderIfNotExists(folderPath, folder.children, fileTypeLocationMap));
                    childPromises.push(p);
                });
            }
            if (childPromises.length > 0) {
                return Promise.all(childPromises);
            }
        }
        findFileType(fileName) {
            let extension;
            if (fileName.indexOf('.') > 0) {
                extension = fileName.split('.').pop().toLowerCase();
                if (IMAGE_EXTENSIONS.some(a => a === extension)) {
                    return exports.FileType.IMAGE;
                }
                if (VIDEO_EXTENSIONS.some(a => a === extension)) {
                    return exports.FileType.VIDEO;
                }
                if (AUDIO_EXTENSIONS.some(a => a === extension)) {
                    return exports.FileType.AUDIO;
                }
            }
            return exports.FileType.DOCUMENT;
        }
        setupUploadDirectory() {
            const uploadsDirName = 'uploads', appDir = cordova.file.dataDirectory;
            return this.cordovaFile.checkDir(appDir, uploadsDirName)
                .then(() => this._uploadDir = appDir + uploadsDirName, () => this.cordovaFile.createDir(appDir, uploadsDirName, true)
                .then(() => this._uploadDir = appDir + uploadsDirName));
        }
        static { this.ɵfac = function DeviceFileService_Factory(t) { return new (t || DeviceFileService)(i0__namespace.ɵɵinject(i1__namespace.AppVersion), i0__namespace.ɵɵinject(i1$1__namespace.File)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: DeviceFileService, factory: DeviceFileService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(DeviceFileService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i1__namespace.AppVersion }, { type: i1$1__namespace.File }], null); })();

    const MAX_CONCURRENT_DOWNLOADS = 2;
    class DeviceFileDownloadService {
        static { this.SERVICE_NAME = 'DeviceFileDownloadService'; }
        constructor(cordovaFile, http, deviceFileService, fileExtensionFromMimePipe) {
            this.cordovaFile = cordovaFile;
            this.http = http;
            this.deviceFileService = deviceFileService;
            this.fileExtensionFromMimePipe = fileExtensionFromMimePipe;
            this._downloadQueue = [];
            this._concurrentDownloads = 0;
        }
        download(url, isPersistent, destFolder, destFile, progressObserver, headers) {
            return this.addToDownloadQueue(url, isPersistent, destFolder, destFile, progressObserver, headers);
        }
        // Adds to download request queue
        addToDownloadQueue(url, isPersistent, destFolder, destFile, progressObserver, headers) {
            return new Promise((resolve, reject) => {
                this._downloadQueue.push({
                    url: url,
                    headers: headers,
                    isPersistent: isPersistent,
                    destFolder: destFolder,
                    destFile: destFile,
                    resolve: resolve,
                    reject: reject,
                    progressObserver: progressObserver
                });
                if (this._concurrentDownloads < MAX_CONCURRENT_DOWNLOADS) {
                    this.downloadNext();
                }
            });
        }
        downloadNext() {
            if (this._downloadQueue.length > 0) {
                const req = this._downloadQueue.shift();
                this.downloadFile(req).then(filePath => {
                    req.resolve(filePath);
                    this.downloadNext();
                }, () => {
                    req.reject();
                    this.downloadNext();
                });
            }
        }
        // Start processing a download request
        downloadFile(req) {
            let filePath, blob;
            this._concurrentDownloads++;
            return this.sendHttpRequest(req.url, req.progressObserver, req.headers).then((e) => {
                blob = e.body;
                return this.getFileName(e, req, blob.type);
            }).then((fileName) => {
                if (!req.destFolder) {
                    req.destFolder = this.deviceFileService.findFolderPath(req.isPersistent, fileName);
                }
                filePath = req.destFolder + fileName;
                return this.cordovaFile.writeFile(req.destFolder, fileName, blob);
            }).then(() => {
                this._concurrentDownloads--;
                return filePath;
            }, (response) => {
                this._concurrentDownloads--;
                this.cordovaFile.removeFile(req.destFolder, req.destFile);
                return Promise.reject(`Failed to downloaded  ${req.url} with error ${JSON.stringify(response)}`);
            });
        }
        /**
         * Returns the filename
         * 1. if filename exists just return
         * 2. retrieve the filename from response headers i.e. content-disposition
         * 3. pick the filename from the end of the url
         * If filename doesnt contain the extension then extract using mimeType.
         * Generates newFileName if filename already exists.
         * @param response, download file response
         * @param req, download request params
         * @param mimeType mime type of file
         * @returns {Promise<string>}
         */
        getFileName(response, req, mimeType) {
            const disposition = response.headers.get('Content-Disposition');
            let filename = req.destFile;
            if (!filename && disposition && disposition.indexOf('attachment') !== -1) {
                const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                const matches = filenameRegex.exec(disposition);
                if (matches !== null && matches[1]) {
                    filename = matches[1].replace(/['"]/g, '');
                }
            }
            if (!filename) {
                filename = req.url.split('?')[0];
                filename = filename.split('/').pop();
            }
            let fileExtension;
            if (mimeType) {
                fileExtension = this.fileExtensionFromMimePipe.transform(mimeType);
            }
            let hasFileExtension;
            // one or more file extensions can have same mimeType then loop over the file extensions.
            if (lodashEs.isArray(fileExtension) && !lodashEs.isEmpty(fileExtension)) {
                hasFileExtension = lodashEs.find(fileExtension, extension => lodashEs.endsWith(filename, extension));
                //File extension will be array for some file formats
                fileExtension = fileExtension[0];
            }
            if (!hasFileExtension && !lodashEs.endsWith(filename, fileExtension)) {
                filename = filename + fileExtension;
            }
            const folder = req.destFolder || this.deviceFileService.findFolderPath(req.isPersistent, filename);
            return this.deviceFileService.newFileName(folder, filename);
        }
        sendHttpRequest(url, progressObserver, headers) {
            let reqHeaders = new i2.HttpHeaders();
            // headers
            if (headers) {
                Object.entries(headers).forEach(([k, v]) => reqHeaders = reqHeaders.append(k, v));
            }
            const req = new i2.HttpRequest('GET', url, {
                headers: reqHeaders,
                responseType: 'blob',
                reportProgress: progressObserver != null
            });
            return this.http.request(req)
                .pipe(operators.map(e => {
                if (progressObserver && progressObserver.next && e.type === i2.HttpEventType.DownloadProgress) {
                    progressObserver.next(e);
                }
                return e;
            }), operators.filter(e => e.type === i2.HttpEventType.Response), operators.map(e => {
                if (progressObserver && progressObserver.complete) {
                    progressObserver.complete();
                }
                return e;
            }))
                .toPromise();
        }
        static { this.ɵfac = function DeviceFileDownloadService_Factory(t) { return new (t || DeviceFileDownloadService)(i0__namespace.ɵɵinject(i1$1__namespace.File), i0__namespace.ɵɵinject(i2__namespace.HttpClient), i0__namespace.ɵɵinject(DeviceFileService), i0__namespace.ɵɵinject(i4__namespace.FileExtensionFromMimePipe)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: DeviceFileDownloadService, factory: DeviceFileDownloadService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(DeviceFileDownloadService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i1$1__namespace.File }, { type: i2__namespace.HttpClient }, { type: DeviceFileService }, { type: i4__namespace.FileExtensionFromMimePipe }], null); })();

    const CACHE_FILE_INDEX_NAME = 'appCache.json';
    class DeviceFileCacheService {
        static { this.SERVICE_NAME = 'DeviceFileCacheService'; }
        constructor(cordovaFile, fileService, downloadService) {
            this.cordovaFile = cordovaFile;
            this.fileService = fileService;
            this.downloadService = downloadService;
            this._cacheIndex = {};
        }
        addEntry(url, filepath) {
            this._cacheIndex[url] = filepath;
            this.writeCacheIndexToFile();
        }
        getLocalPath(url, downloadIfNotExists, isPersistent, forceRefresh = false) {
            if (forceRefresh) {
                return this.download(url, isPersistent);
            }
            const filePath = this._cacheIndex[url];
            return this.fileService.isValidPath(filePath)
                .catch(() => {
                delete this._cacheIndex[url];
                if (downloadIfNotExists) {
                    return this.download(url, isPersistent);
                }
                else {
                    return Promise.reject('No cache entry for ' + url);
                }
            });
        }
        invalidateCache() {
            this._cacheIndex = {};
            this.writeCacheIndexToFile();
            this.fileService.clearTemporaryStorage();
        }
        start() {
            return this.cordovaFile.readAsText(cordova.file.dataDirectory, CACHE_FILE_INDEX_NAME)
                .then(content => {
                this._cacheIndex = JSON.parse(content);
            }, i2$2.noop);
        }
        getServiceName() {
            return DeviceFileCacheService.SERVICE_NAME;
        }
        download(url, isPersistent) {
            return this.downloadService.download(url, isPersistent)
                .then(filepath => {
                this._cacheIndex[url] = filepath;
                this.writeCacheIndexToFile();
                return filepath;
            });
        }
        writeCacheIndexToFile() {
            if (!this._writing) {
                this._writing = true;
                this.cordovaFile.writeFile(cordova.file.dataDirectory, CACHE_FILE_INDEX_NAME, JSON.stringify(this._cacheIndex), {
                    replace: true
                })
                    .catch(i2$2.noop)
                    .then(() => {
                    if (this._saveCache) {
                        setTimeout(() => {
                            this._writing = false;
                            this._saveCache = false;
                            this.writeCacheIndexToFile();
                        }, 5000);
                    }
                    else {
                        this._writing = false;
                    }
                });
            }
            else {
                this._saveCache = true;
            }
        }
        static { this.ɵfac = function DeviceFileCacheService_Factory(t) { return new (t || DeviceFileCacheService)(i0__namespace.ɵɵinject(i1$1__namespace.File), i0__namespace.ɵɵinject(DeviceFileService), i0__namespace.ɵɵinject(DeviceFileDownloadService)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: DeviceFileCacheService, factory: DeviceFileCacheService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(DeviceFileCacheService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i1$1__namespace.File }, { type: DeviceFileService }, { type: DeviceFileDownloadService }], null); })();

    class DeviceFileOpenerService {
        static { this.SERVICE_NAME = 'DeviceFileOpenerService'; }
        constructor(cordovaFile, cordovaFileOpener, fileService, cacheService, downloadService) {
            this.cordovaFile = cordovaFile;
            this.cordovaFileOpener = cordovaFileOpener;
            this.fileService = fileService;
            this.cacheService = cacheService;
            this.downloadService = downloadService;
        }
        // this method returns the mime type of file from the filePath.
        getFileMimeType(filePath) {
            return new Promise((resolve) => {
                // Read the file entry from the file URL
                resolveLocalFileSystemURL(filePath, fileEntry => {
                    fileEntry.file(metadata => {
                        resolve(metadata.type);
                    });
                });
            });
        }
        openRemoteFile(url, extension, fileName, headers) {
            return this.getLocalPath(url, extension, fileName, headers)
                .then(filePath => {
                return this.getFileMimeType(filePath).then(type => {
                    return this.cordovaFileOpener.open(filePath, type);
                });
            });
        }
        start() {
            let downloadsParent;
            if (i2$2.isAndroid()) {
                downloadsParent = cordova.file.externalCacheDirectory;
            }
            else if (i2$2.isIos()) {
                downloadsParent = cordova.file.documentsDirectory + 'NoCloud/';
            }
            else {
                downloadsParent = cordova.file.dataDirectory;
            }
            return this.cordovaFile.createDir(downloadsParent, 'downloads', false)
                .catch(i2$2.noop)
                .then(() => {
                this._downloadsFolder = downloadsParent + 'downloads/';
            });
        }
        getServiceName() {
            return DeviceFileCacheService.SERVICE_NAME;
        }
        generateFileName(url, extension) {
            let fileName = url.split('?')[0];
            fileName = fileName.split('/').pop();
            fileName = this.fileService.appendToFileName(fileName, '' + lodashEs.now());
            if (extension) {
                return fileName.split('.')[0] + '.' + extension;
            }
            return fileName;
        }
        getLocalPath(url, extension, filename, headers) {
            return new Promise((resolve, reject) => {
                return this.cacheService.getLocalPath(url, false, false)
                    .then(filePath => {
                    let fileName, i, j, fromDir, fromFile;
                    // Is it part of downloaded folder.
                    if (filePath.startsWith(this._downloadsFolder)) {
                        resolve(filePath);
                    }
                    else {
                        //Extract extension from file downloaded earlier.
                        j = filePath.lastIndexOf('.');
                        extension = filePath.substring(j + 1);
                        fileName = filename || this.generateFileName(url, extension);
                        i = filePath.lastIndexOf('/');
                        fromDir = filePath.substring(0, i);
                        fromFile = filePath.substring(i + 1);
                        this.cordovaFile.copyFile(fromDir, fromFile, this._downloadsFolder, fileName)
                            .then(() => {
                            const newFilePath = this._downloadsFolder + fileName;
                            this.cacheService.addEntry(url, newFilePath);
                            resolve(newFilePath);
                        });
                    }
                }).catch(() => {
                    this.downloadService.download(url, false, this._downloadsFolder, filename, undefined, headers)
                        .then(filePath => {
                        this.cacheService.addEntry(url, filePath);
                        resolve(filePath);
                    }, reject);
                });
            });
        }
        static { this.ɵfac = function DeviceFileOpenerService_Factory(t) { return new (t || DeviceFileOpenerService)(i0__namespace.ɵɵinject(i1$1__namespace.File), i0__namespace.ɵɵinject(i2$1__namespace.FileOpener), i0__namespace.ɵɵinject(DeviceFileService), i0__namespace.ɵɵinject(DeviceFileCacheService), i0__namespace.ɵɵinject(DeviceFileDownloadService)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: DeviceFileOpenerService, factory: DeviceFileOpenerService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(DeviceFileOpenerService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i1$1__namespace.File }, { type: i2$1__namespace.FileOpener }, { type: DeviceFileService }, { type: DeviceFileCacheService }, { type: DeviceFileDownloadService }], null); })();

    const REGISTRY_FILE_NAME = 'registry.info';
    class DeviceService {
        static { this.SERVICE_NAME = 'DeviceService'; }
        constructor(file) {
            this.file = file;
            this._registry = {};
            this._isReady = false;
            this._whenReadyPromises = [];
            this._backBtnTapListeners = [];
            this._startUpServices = [];
            this._config = {};
            const maxWaitTime = 10;
            setTimeout(() => {
                if (!this._isReady) {
                    console.warn(`Device is not ready even after ${maxWaitTime} seconds`);
                    console.warn('Waiting For %O', this._startUpServices.map(i => i.getServiceName()));
                }
            }, maxWaitTime * 1000);
            document.addEventListener('backbutton', this.executeBackTapListeners.bind(this));
            if (i2$2.hasCordova()) {
                const port = location.port ? ':' + location.port : '';
                const configUrl = `${location.protocol}//${location.hostname}${port}${i2$2.isIos() ? '' : '/_www'}/config.json`;
                i2$2.fetchContent('json', configUrl, true, (response => {
                    if (!response.error && response.baseUrl) {
                        this._config = response;
                    }
                }));
            }
        }
        executeBackTapListeners($event) {
            lodashEs.forEach(this._backBtnTapListeners, fn => {
                return fn($event) !== false;
            });
            // explicitly applying the digest cycle as the backbutton listener is not rendering the page content.
            // This is because zone is not run (there is no change detection)
            // https://weblogs.thinktecture.com/thomas/2017/02/cordova-vs-zonejs-or-why-is-angulars-document-event-listener-not-in-a-zone.html
            i2$2.$appDigest();
        }
        addStartUpService(service) {
            this._startUpServices.push(service);
        }
        onBackButtonTap(fn) {
            this._backBtnTapListeners.unshift(fn);
            return () => {
                const i = this._backBtnTapListeners.indexOf(fn);
                if (i >= 0) {
                    this._backBtnTapListeners.splice(i, 1);
                }
            };
        }
        start() {
            if (this._isReady || this._startUpServices.length === 0) {
                this._isReady = true;
                return Promise.resolve();
            }
            else {
                return new Promise((resolve) => {
                    if (i2$2.hasCordova()) {
                        document.addEventListener('deviceready', () => resolve(), false);
                    }
                    else {
                        resolve();
                    }
                }).then(() => {
                    if (window['cordova']) {
                        return this.file.readAsText(cordova.file.dataDirectory, REGISTRY_FILE_NAME)
                            .then(content => this._registry = JSON.parse(content), i2$2.noop);
                    }
                }).then(() => {
                    return Promise.all(this._startUpServices.map(s => {
                        return s.start().catch((error) => {
                            console.error('%s failed to start due to: %O', s.getServiceName(), error);
                            return Promise.reject(error);
                        });
                    }));
                }).then(() => {
                    window['wmDeviceReady'] = true;
                    document.dispatchEvent(new CustomEvent('wmDeviceReady'));
                    this._startUpServices.length = 0;
                    this._whenReadyPromises.forEach(fn => fn());
                    this._isReady = true;
                });
            }
        }
        getServiceName() {
            return DeviceService.SERVICE_NAME;
        }
        whenReady() {
            if (this._isReady) {
                return Promise.resolve();
            }
            else {
                return new Promise((resolve) => {
                    this._whenReadyPromises.push(resolve);
                });
            }
        }
        getBaseUrl() {
            return this._config.baseUrl === 'http://NOSERVERREQUIRED.com' ? 'NONE' : this._config.baseUrl;
        }
        isAppConnectedToPreview() {
            return this._config.customUrl !== this._config.baseUrl && this.getBaseUrl() !== 'NONE';
        }
        getConfig() {
            return this._config;
        }
        useNativeXHR() {
            return (i2$2.isIos() || this._config.useNativeXHR === true)
                && i2$2.hasCordova()
                && cordova.plugin
                && cordova.plugin.http;
        }
        /**
         * @returns {Promise<number>} promise resolved with the app build time
         */
        getAppBuildTime() {
            return Promise.resolve(this._config.buildTime);
        }
        /**
         * Stores an entry that survives app restarts and updates.
         *
         * @param {string} key
         * @param {Object} value
         * @returns {Promise<any>}
         */
        storeEntry(key, value) {
            this._registry[key] = value;
            return this.file.writeFile(cordova.file.dataDirectory, REGISTRY_FILE_NAME, JSON.stringify(this._registry), { replace: true });
        }
        /**
         * @param {string} key
         * @returns {any} entry corresponding to the key
         */
        getEntry(key) {
            return this._registry[key];
        }
        static { this.ɵfac = function DeviceService_Factory(t) { return new (t || DeviceService)(i0__namespace.ɵɵinject(i1$1__namespace.File)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: DeviceService, factory: DeviceService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(DeviceService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i1$1__namespace.File }], null); })();

    const _XMLHttpRequest = window['XMLHttpRequest'];
    const cordova$1 = window['cordova'];
    class Internals {
        constructor(nXhr) {
            this.nXhr = nXhr;
            this.xhr = new _XMLHttpRequest();
            this.method = 'GET';
            this.requestHeaders = {};
            this.responseHeaders = {};
            this.responseHeaderText = '';
            this.url = '';
            this.async = true;
            this.listeners = {};
        }
        triggerListeners(eventName, data) {
            const listeners = this.listeners[eventName];
            if (listeners) {
                listeners.forEach(function (l) {
                    l.apply(window, data);
                });
            }
        }
        copyNativeResponse(res) {
            this.nXhr.status = res.status;
            this.nXhr.statusText = res.statusText || res.status;
            this.nXhr.response = res.data;
            this.nXhr.responseType = (res.headers && res.headers['content-type']) || 'plain/text';
            this.nXhr.responseURL = res.responseURL;
            this.nXhr.responseXML = res.responseXML;
            this.nXhr.responseText = (res.data && res.data.toString()) || res.error;
            this.responseHeaderText = (() => {
                const headers = [];
                for (let k in res.headers) {
                    headers.push(k + ': ' + res.headers[k]);
                }
                return headers.join('\r\n');
            })();
        }
        copyXMLHttpResponse(req) {
            this.nXhr.status = req.status;
            this.nXhr.statusText = req.statusText;
            this.nXhr.response = req.response;
            this.nXhr.responseType = req.responseType;
            this.nXhr.responseURL = req.responseURL;
            this.nXhr.responseXML = req.responseXML;
            this.nXhr.responseText = req.responseText;
            this.responseHeaderText = req.getAllResponseHeaders();
            this.nXhr.readyState = req.readyState;
            this.nXhr.getResponseHeader = header => req.getResponseHeader(header);
        }
        sendViaXhr(body) {
            const self = this;
            this.xhr.onreadystatechange = () => {
                self.copyXMLHttpResponse(self.xhr);
                this.nXhr.onreadystatechange && this.nXhr.onreadystatechange();
            };
            this.xhr.onload = function () {
                self.copyXMLHttpResponse(self.xhr);
                if (self.nXhr.onload) {
                    self.nXhr.onload.apply(self.nXhr, arguments);
                }
            };
            this.xhr.onerror = this.nXhr.onerror;
            if (!this.async) {
                this.xhr.ontimeout = this.nXhr.ontimeout;
                this.xhr.timeout = this.nXhr.timeout;
            }
            this.xhr.withCredentials = this.nXhr.withCredentials;
            for (let k in this.listeners) {
                this.xhr.addEventListener(k, e => {
                    this.copyXMLHttpResponse(this.xhr);
                    this.triggerListeners(e.type);
                });
            }
            this.xhr.send(body);
        }
        close() {
            for (let k in this.listeners) {
                this.listeners[k].length = 0;
            }
        }
    }
    class NativeXMLHttpRequest {
        constructor() {
            this.onreadystatechange = null;
            this.readyState = 0;
            this.response = null;
            this.responseType = null;
            this.responseText = null;
            this.responseURL = null;
            this.responseXML = null;
            this.status = 0;
            this.statusText = '';
            this.ontimeout = null;
            this.onload = null;
            this.onloadend = null;
            this.onerror = null;
            this.timeout = 0;
            this.withCredentials = false;
            this.progress = {
                lengthComputable: false,
                loaded: 0,
                total: 0,
                close: (success = false) => { }
            };
            this.upload = {
                addEventListener: this.addEventListener.bind(this)
            };
            this._internal = new Internals(this);
        }
        abort() {
            this.status = -1;
            this._internal.triggerListeners("error" /* EVENT.ERROR */);
            this.onerror && this.onerror();
            this._internal.xhr.abort();
            this.close();
        }
        getResponseHeader(name) {
            return this.response.headers[name];
        }
        getAllResponseHeaders() {
            return this._internal.responseHeaderText;
        }
        setRequestHeader(header, value) {
            this._internal.requestHeaders[header] = value;
            this._internal.xhr.setRequestHeader(header, value);
        }
        overrideMimeType(mime) {
            this._internal.xhr.overrideMimeType(mime);
        }
        open(method, url, async = true, user, password) {
            this._internal.method = method;
            this._internal.url = url;
            this._internal.async = async;
            this._internal.xhr.open(method, url, async, user, password);
        }
        send(body, useBrowserXHR = false) {
            if (useBrowserXHR
                || this._internal.url.startsWith('http://localhost')
                || !this._internal.url.startsWith('http')) {
                this._internal.sendViaXhr(body);
            }
            else if (this.responseType === 'blob') {
                this.sendBlobViaNativePlugin(body);
            }
            else {
                this.sendViaNativePlugin(body);
            }
        }
        ;
        addEventListener(eventName, handler) {
            this._internal.listeners[eventName] = this._internal.listeners[eventName] || [];
            this._internal.listeners[eventName].push(handler);
        }
        removeEventListener(eventName, handler) {
            if (this._internal.listeners[eventName]) {
                const i = this._internal.listeners[eventName].indexOf(handler);
                this._internal.listeners[eventName].splice(i, 1);
            }
        }
        prepareOptions(body) {
            body = body || {};
            const options = {
                headers: {},
                data: body,
                method: this._internal.method,
                serializer: 'raw'
            };
            if (body != null && body != undefined) {
                if (body.constructor && body.constructor.toString().indexOf('FormData()') >= 0) {
                    options.serializer = 'multipart';
                    this.startProgress();
                }
                else if (typeof body === 'string') {
                    options.serializer = 'utf8';
                }
                else {
                    options.serializer = 'urlencoded';
                }
            }
            for (let key in this._internal.requestHeaders) {
                options.headers[key] = this._internal.requestHeaders[key];
            }
            return options;
        }
        /*
         * Native plugin didn't provide any API to track upload progress.
         * This method generates a synthetic event which does not co-relates with the actual upload progress.
         */
        startProgress(total) {
            const _1mb = 1024 * 1024 * 8;
            total = total || _1mb;
            const interval = Math.ceil(total / _1mb) * 200;
            const slice = Math.ceil(total / _1mb) * (_1mb / 10);
            this.progress = {
                lengthComputable: true,
                loaded: 0,
                total: total,
                close: null
            };
            const triggerEvent = () => {
                this._internal.triggerListeners("progress" /* EVENT.PROGRESS */, [{
                        lengthComputable: true,
                        total: this.progress.total,
                        loaded: this.progress.loaded
                    }]);
            };
            const intervalId = setInterval(() => {
                if (this.progress.loaded + slice < total) {
                    this.progress.loaded += slice;
                }
                triggerEvent();
            }, interval);
            this.progress.close = (success = false) => {
                if (success) {
                    this.progress.loaded = this.progress.total;
                }
                triggerEvent();
                clearInterval(intervalId);
            };
        }
        sendBlobViaNativePlugin(body) {
            const options = this.prepareOptions(body);
            const tempfile = (i2$2.isIos() ? cordova$1.file.tempDirectory : cordova$1.file.cacheDirectory) + Date.now();
            const timerId = setTimeout(() => {
                if (this.timeout > 0) {
                    console.error('network call with request %O failed with timeout', this);
                    this._internal.triggerListeners("load" /* EVENT.LOAD */);
                    this._internal.triggerListeners("error" /* EVENT.ERROR */);
                    this.onerror && this.onerror();
                    this.ontimeout && this.ontimeout();
                    this.close();
                }
            }, this.timeout > 0 ? this.timeout : 1);
            const onSuccess = (file, response) => {
                const res = {
                    headers: (response && response.headers) || {},
                    status: 200,
                    data: file
                };
                let contentType = (response && response.headers && response.headers['content-type']);
                if (contentType) {
                    res.data.type = contentType.split(';')[0];
                }
                console.log('network call with request %O successed with response : %O', this, res);
                this._internal.copyNativeResponse(res);
                this._internal.triggerListeners("load" /* EVENT.LOAD */);
                this.onload && this.onload();
                this.onloadend && this.onloadend();
                clearTimeout(timerId);
                this.readyState = 4;
                this.onreadystatechange && this.onreadystatechange();
            };
            const onError = (response) => {
                console.error('network call with request %O failed with response : %O', this, response);
                this._internal.triggerListeners("load" /* EVENT.LOAD */, [response]);
                this._internal.triggerListeners("error" /* EVENT.ERROR */, [response]);
                this.onerror && this.onerror();
                this.onloadend && this.onloadend();
                clearTimeout(timerId);
                this.readyState = 4;
                this.onreadystatechange && this.onreadystatechange();
            };
            cordova$1.plugin.http.downloadFile(new URL(this._internal.url).href, {}, options.headers, tempfile, (entry, response) => {
                entry.file(f => onSuccess(f, response), onError);
            }, onError);
            this.readyState = 1;
            this.onreadystatechange && this.onreadystatechange();
        }
        sendViaNativePlugin(body) {
            const options = this.prepareOptions(body);
            const timerId = setTimeout(() => {
                if (this.timeout > 0) {
                    console.error('network call with request %O failed with timeout', this);
                    this._internal.triggerListeners("load" /* EVENT.LOAD */);
                    this._internal.triggerListeners("error" /* EVENT.ERROR */);
                    this.ontimeout && this.ontimeout();
                    this.close();
                }
            }, this.timeout > 0 ? this.timeout : 1);
            new Promise((resolve, reject) => {
                cordova$1.plugin.http.sendRequest(new URL(this._internal.url).href, options, resolve, reject);
                this.readyState = 1;
                this.onreadystatechange && this.onreadystatechange();
            }).then(response => {
                console.log('network call with request %O successed with response : %O', this, response);
                this.progress.close(true);
                this._internal.copyNativeResponse(response);
                this._internal.triggerListeners("load" /* EVENT.LOAD */);
                this.onload && this.onload();
                this.onloadend && this.onloadend();
            }, response => {
                this.progress.close();
                this._internal.copyNativeResponse(response);
                console.error('network call with request %O failed with response : %O', this, response);
                this._internal.triggerListeners("load" /* EVENT.LOAD */, [response]);
                this._internal.triggerListeners("error" /* EVENT.ERROR */, [response]);
                this.onerror && this.onerror();
                this.onloadend && this.onloadend();
            }).finally(() => {
                this.readyState = 4;
                this.onreadystatechange && this.onreadystatechange();
                clearTimeout(timerId);
            });
        }
        close() {
            this.ontimeout = null;
            this.onload = null;
            this.onerror = null;
            this.onreadystatechange = null;
            this._internal.close();
        }
    }

    const AUTO_CONNECT_KEY = 'WM.NetworkService._autoConnect', IS_CONNECTED_KEY = 'WM.NetworkService.isConnected', excludedList = [new RegExp('/wmProperties.js')], networkState = {
        isConnecting: false,
        isConnected: true,
        isNetworkAvailable: true,
        isServiceAvailable: true,
        noServiceRequired: false
    };
    let XML_HTTP_REQUEST = null;
    /**
     * If server is not connected and url does not match the unblock list of regular expressions,
     * then this function return true. Otherwise, return false.
     * @param url
     * @returns {boolean}
     */
    const blockUrl = url => {
        let block = !networkState.isConnected && lodashEs.startsWith(url, 'http');
        if (block) {
            block = !lodashEs.find(excludedList, regExp => regExp.test(url));
        }
        return block;
    };
    const getXMLHttpRequestToUse = ((useNativeXHR) => {
        const clazz = useNativeXHR ? NativeXMLHttpRequest : XMLHttpRequest;
        const orig = clazz.prototype.open;
        // Intercept all XHR calls
        clazz.prototype.open = function (method, url, async = true, user, password) {
            if (!networkState.noServiceRequired && blockUrl(url)) {
                const urlSplits = url.split('://');
                const pathIndex = urlSplits[1].indexOf('/');
                urlSplits[1] = 'localhost' + (pathIndex > 0 ? urlSplits[1].substr(pathIndex) : '/');
                url = urlSplits.join('://');
            }
            return orig.apply(this, [method, url, async, user, password]);
        };
        return clazz;
    });
    class NetworkService {
        static { this.SERVICE_NAME = 'NetworkService'; }
        constructor(httpClient, app, network, deviceService) {
            this.httpClient = httpClient;
            this.app = app;
            this.network = network;
            this.deviceService = deviceService;
            this._autoConnect = localStorage.getItem(AUTO_CONNECT_KEY) !== 'false';
            this._isCheckingServer = false;
            /**
             * When the auto connect is enabled, then app is automatically connected  whenever server is available.
             * Every time when app goes offline, auto connect is enabled. Before app coming to online, one can disable
             * the auto connection flow using this method.
             */
            this.disableAutoConnect = () => this.setAutoConnect(false);
            /**
             * Returns true, if app is connected to server. Otherwise, returns false.
             *
             * @returns {boolean} Returns true, if app is connected to server. Otherwise, returns false.
             */
            this.isConnected = () => {
                // checking for connection type.
                if (lodashEs.get(navigator, 'connection') && navigator.connection.type) {
                    networkState.isConnected = networkState.isConnected && (navigator.connection.type !== 'none');
                }
                this.checkForNetworkStateChange();
                return networkState.isConnected;
            };
            /**
             * Returns true if app is trying to connect to server. Otherwise, returns false.
             *
             * @returns {boolean} Returns true if app is trying to connect to server. Otherwise, returns false.
             */
            this.isConnecting = () => networkState.isConnecting;
            networkState.isConnected = localStorage.getItem(IS_CONNECTED_KEY) === 'true';
            XML_HTTP_REQUEST = XML_HTTP_REQUEST || getXMLHttpRequestToUse(deviceService.useNativeXHR());
        }
        /**
         * This method attempts to connect app to the server and returns a promise that will be resolved with
         * a boolean value based on the operation result.
         *
         * @returns {object} promise
         */
        connect() {
            this.setAutoConnect(true);
            return this.tryToConnect();
        }
        /**
         * This method disconnects the app from the server and returns a promise that will be resolved with
         * a boolean value based on the operation result. Use connect method to reconnect.
         *
         * @returns {object} promise
         */
        disconnect() {
            const p = this.tryToDisconnect();
            this.disableAutoConnect();
            return p;
        }
        /**
         * If pingServer is true, then it returns a promise that will be resolved with boolean based on service availability
         * check.If pingServer is false, returns a boolean value based on the last known service availability.
         *
         * @returns {boolean} if pingServer is true, then a promise is returned. Otherwise, a boolean value.
         */
        isAvailable(pingServer = false) {
            if (pingServer) {
                return this.isServiceAvailable().then(() => {
                    this.checkForNetworkStateChange();
                    return networkState.isServiceAvailable;
                });
            }
            return networkState.isServiceAvailable;
        }
        /**
         * This method returns a promise that is resolved when connection is established with server.
         *
         * @returns {object} promise a promise that is resolved with the returned object of fn
         */
        onConnect() {
            let defer, cancelSubscription;
            if (this.isConnected()) {
                return Promise.resolve();
            }
            defer = i2$2.getAbortableDefer();
            cancelSubscription = this.app.subscribe('onNetworkStateChange', () => {
                if (this.isConnected()) {
                    defer.resolve(true);
                    cancelSubscription();
                }
            });
            defer.promise.catch(function () {
                cancelSubscription();
            });
            return defer.promise;
        }
        /**
         * This is a util method. If fn cannot execute successfully and network lost connection, then the fn will
         * be invoked when network is back. The returned can also be aborted.
         *
         * @param {function()} fn method to invoke.
         * @returns {object} promise a promise that is resolved with the returned object of fn
         */
        retryIfNetworkFails(fn) {
            const defer = i2$2.getAbortableDefer();
            i2$2.retryIfFails(fn, 0, 0, () => {
                let onConnectPromise;
                if (!this.isConnected()) {
                    onConnectPromise = this.onConnect();
                    defer.promise.catch(function () {
                        onConnectPromise.abort();
                    });
                    return onConnectPromise;
                }
                return false;
            }).then(defer.resolve, defer.reject);
            return defer.promise;
        }
        start() {
            if (window['cordova']) {
                // Connection constant will be available only when network plugin is included.
                if (window['Connection'] && navigator.connection) {
                    networkState.isNetworkAvailable = navigator.connection.type !== 'none';
                    networkState.isConnected = networkState.isNetworkAvailable && networkState.isConnected;
                    /*
                     * When the device comes online, check is the service is available. If the service is available and auto
                     * connect flag is true, then app is automatically connected to remote server.
                     */
                    this.network.onConnect().subscribe(() => {
                        networkState.isNetworkAvailable = true;
                        this.tryToConnect().catch(i2$2.noop);
                    });
                    /*
                     *When device goes offline, then change the network state and emit that notifies about network state change.
                     */
                    this.network.onDisconnect().subscribe(() => {
                        networkState.isNetworkAvailable = false;
                        networkState.isServiceAvailable = false;
                        this.tryToDisconnect();
                    });
                    this.app.subscribe('onNetworkStateChange', (data) => {
                        /**
                         * If network is available and server is not available,then
                         * try to connect when server is available.
                         */
                        if (data.isNetworkAvailable && !data.isServiceAvailable && !this._isCheckingServer && !data.noServiceRequired) {
                            this._isCheckingServer = true;
                            this.checkForServiceAvailiblity().then(() => {
                                this._isCheckingServer = false;
                                this.connect();
                            }, () => {
                                this._isCheckingServer = false;
                            });
                        }
                    });
                }
            }
            // to set the default n/w connection values.
            return this.tryToConnect(true).catch(i2$2.noop);
        }
        getServiceName() {
            return NetworkService.SERVICE_NAME;
        }
        /**
         * This function adds the given regular expression to the unblockList. Even app is in offline mode,
         * the urls matching with the given regular expression are not blocked by NetworkService.
         *
         * @param {string} urlRegex regular expression
         */
        unblock(urlRegex) {
            excludedList.push(new RegExp(urlRegex));
        }
        getState() {
            return lodashEs.clone(networkState);
        }
        checkForNetworkStateChange() {
            if (!lodashEs.isEqual(this._lastKnownNetworkState, networkState)) {
                this._lastKnownNetworkState = lodashEs.clone(networkState);
                this.app.notify('onNetworkStateChange', this._lastKnownNetworkState);
            }
        }
        /**
         * Returns a promise that is resolved when server is available.
         * @returns {*}
         */
        checkForServiceAvailiblity() {
            const maxTimeout = 4500;
            return new Promise(resolve => {
                const intervalId = setInterval(() => {
                    if (networkState.isNetworkAvailable) {
                        this.isServiceAvailable(maxTimeout).then(available => {
                            if (available) {
                                clearInterval(intervalId);
                                resolve();
                            }
                        });
                    }
                }, 5000);
            });
        }
        /**
         * Pings server to check whether server is available. Based on ping response network state is modified.
         * @returns {*} a promise that resolved with true, if server responds with valid status.
         * Otherwise, the promise is resolved with false.
         */
        isServiceAvailable(maxTimeout) {
            if (this.app.deployedUrl === 'NONE') {
                networkState.isServiceAvailable = false;
                networkState.noServiceRequired = true;
                return Promise.resolve(false);
            }
            return this.pingServer(maxTimeout).then(response => {
                networkState.isServiceAvailable = response;
                if (!networkState.isServiceAvailable) {
                    networkState.isConnecting = false;
                    networkState.isConnected = false;
                }
                return response;
            });
        }
        /**
         * Pings server
         * @returns {*} a promise that resolved with true, if server responds with valid status.
         * Otherwise, the promise is resolved with false.
         * default timeout value is 1min.
         */
        pingServer(maxTimeout = 60000) {
            return new Promise(resolve => {
                const oReq = new XML_HTTP_REQUEST();
                let baseURL = this.app.deployedUrl;
                if (baseURL && !lodashEs.endsWith(baseURL, '/')) {
                    baseURL += '/';
                }
                else {
                    baseURL = baseURL || '';
                }
                const timer = setTimeout(() => {
                    oReq.abort(); // abort request
                    resolve(false);
                }, maxTimeout);
                oReq.addEventListener('load', () => {
                    if (oReq.status === 200) {
                        resolve(true);
                    }
                    else {
                        resolve(false);
                    }
                    if (timer) {
                        clearTimeout(timer);
                    }
                });
                oReq.addEventListener('error', () => resolve(false));
                oReq.open('GET', baseURL + 'services/application/wmProperties.js?t=' + Date.now());
                oReq.send();
            });
        }
        setAutoConnect(flag) {
            this._autoConnect = flag;
            localStorage.setItem(AUTO_CONNECT_KEY, '' + flag);
        }
        /**
         * Tries to connect to remote server. Network State will be changed based on the success of connection
         * operation and emits an event notifying the network state change.
         *
         * @param silentMode {boolean} if true and connection is successful, then no event is emitted. Otherwise,
         * events are emitted for network status change.
         * @returns {*} a promise
         */
        tryToConnect(silentMode = false) {
            return new Promise((resolve, reject) => {
                this.isServiceAvailable(5000).then(() => {
                    if (networkState.isServiceAvailable && this._autoConnect) {
                        networkState.isConnecting = true;
                        if (!silentMode) {
                            this.checkForNetworkStateChange();
                        }
                        setTimeout(() => {
                            networkState.isConnecting = false;
                            networkState.isConnected = true;
                            localStorage.setItem(IS_CONNECTED_KEY, '' + true);
                            if (!silentMode) {
                                this.checkForNetworkStateChange();
                            }
                            resolve(true);
                        }, silentMode ? 0 : 5000);
                    }
                    else {
                        networkState.isConnecting = false;
                        networkState.isConnected = false;
                        localStorage.setItem(IS_CONNECTED_KEY, '' + false);
                        reject();
                        this.checkForNetworkStateChange();
                    }
                });
            });
        }
        tryToDisconnect() {
            networkState.isConnected = false;
            networkState.isConnecting = false;
            this.checkForNetworkStateChange();
            localStorage.setItem(IS_CONNECTED_KEY, '' + networkState.isConnected);
            return Promise.resolve(networkState.isConnected);
        }
        static { this.ɵfac = function NetworkService_Factory(t) { return new (t || NetworkService)(i0__namespace.ɵɵinject(i2__namespace.HttpClient), i0__namespace.ɵɵinject(i2$2__namespace.App), i0__namespace.ɵɵinject(i3__namespace.Network), i0__namespace.ɵɵinject(DeviceService)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: NetworkService, factory: NetworkService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(NetworkService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i2__namespace.HttpClient }, { type: i2$2__namespace.App }, { type: i3__namespace.Network }, { type: DeviceService }], null); })();

    class MobileCoreModule {
        static { this.initialized = false; }
        // Startup services have to be added only once in the app life-cycle.
        static addStartupServices(deviceService, deviceFileService, fileCacheService, fileOpener, networkService) {
            if (this.initialized) {
                return;
            }
            deviceService.addStartUpService(networkService);
            if (i2$2.hasCordova()) {
                deviceService.addStartUpService(deviceFileService);
                deviceService.addStartUpService(fileCacheService);
                deviceService.addStartUpService(fileOpener);
            }
            this.initialized = true;
        }
        constructor(deviceService, deviceFileService, fileCacheService, fileOpener, networkService) {
            MobileCoreModule.addStartupServices(deviceService, deviceFileService, fileCacheService, fileOpener, networkService);
            const isSSLPinningEnabled = deviceService.getConfig().enableSSLPinning;
            if (i2$2.hasCordova()) {
                document.addEventListener('wmDeviceReady', () => {
                    if (deviceService.useNativeXHR()) {
                        // ### ANGULAR9TODO###
                        // Adding type as any to avoid error TS2739:
                        window['XMLHttpRequest'] = NativeXMLHttpRequest;
                        if (isSSLPinningEnabled) {
                            cordova.plugin.http.setServerTrustMode('pinned', () => {
                                console.log('SSL Pinning enabled.!');
                            }, e => {
                                console.log('Failed to enable SSL Pinning. Due to: ' + e);
                            });
                        }
                        this.overrideResolveLocalFileSystemURL();
                    }
                }, false);
            }
        }
        overrideResolveLocalFileSystemURL() {
            const or = window['resolveLocalFileSystemURL'];
            window['resolveLocalFileSystemURL'] = (path, onSuccess, onError) => {
                if (path && path.startsWith('http://localhost')) {
                    path = new URL(path).pathname;
                    const lf = '/local-filesystem';
                    if (path.startsWith(lf)) {
                        path = 'file://' + path.substring(lf.length);
                    }
                }
                or.call(window, path, onSuccess, onError);
            };
        }
        static { this.ɵfac = function MobileCoreModule_Factory(t) { return new (t || MobileCoreModule)(i0__namespace.ɵɵinject(DeviceService), i0__namespace.ɵɵinject(DeviceFileService), i0__namespace.ɵɵinject(DeviceFileCacheService), i0__namespace.ɵɵinject(DeviceFileOpenerService), i0__namespace.ɵɵinject(NetworkService)); }; }
        static { this.ɵmod = /*@__PURE__*/ i0__namespace.ɵɵdefineNgModule({ type: MobileCoreModule }); }
        static { this.ɵinj = /*@__PURE__*/ i0__namespace.ɵɵdefineInjector({}); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(MobileCoreModule, [{
            type: i0.NgModule,
            args: [{
                    declarations: [],
                    imports: [],
                    providers: [
                    // add providers to mobile-runtime module.
                    ],
                    bootstrap: []
                }]
        }], () => [{ type: DeviceService }, { type: DeviceFileService }, { type: DeviceFileCacheService }, { type: DeviceFileOpenerService }, { type: NetworkService }], null); })();

    class UploadRequest {
        constructor(url, cordovaFile) {
            this.url = url;
            this.cordovaFile = cordovaFile;
            this._files = [];
            this._params = [];
            this._headers = [];
        }
        addFile(name, path, filename) {
            this._files.push({
                name: name,
                path: path,
                fileName: filename
            });
            return this;
        }
        addHeader(name, value) {
            this._headers.push({
                name: name,
                value: value
            });
            return this;
        }
        addParam(name, value) {
            this._params.push({
                name: name,
                value: value
            });
            return this;
        }
        post() {
            const formData = new FormData();
            this._params.forEach(e => formData.append(e.name, e.value));
            return Promise.all(this._files.map(e => {
                if (e.path) {
                    return i2$2.convertToBlob(e.path)
                        .then(result => {
                        return {
                            name: e.name,
                            fileName: e.fileName,
                            blob: result.blob
                        };
                    });
                }
                return e;
            })).then(params => {
                params.forEach(e => formData.append(e.name, e.blob || e.path, e.fileName));
                return new Promise((resolve, reject) => {
                    const request = new XMLHttpRequest();
                    request.open('POST', this.url);
                    this._headers.forEach(e => request.setRequestHeader(e.name, e.value));
                    request.onload = () => {
                        resolve({
                            headers: (name) => request.getResponseHeader(name),
                            response: request.response,
                            text: request.responseText
                        });
                    };
                    request.onerror = reject;
                    request.onabort = reject;
                    request.send(formData);
                });
            });
        }
    }
    class DeviceFileUploadService {
        static { this.SERVICE_NAME = 'DeviceFileUploadService'; }
        constructor(cordovaFile) {
            this.cordovaFile = cordovaFile;
        }
        upload(url, fileParamName, path, fileName, params, headers) {
            const req = new UploadRequest(url, this.cordovaFile)
                .addFile(fileParamName, path, fileName);
            lodashEs.forEach(params, (k, v) => req.addParam(k, v));
            lodashEs.forEach(headers, (k, v) => req.addHeader(k, v));
            return req.post();
        }
        static { this.ɵfac = function DeviceFileUploadService_Factory(t) { return new (t || DeviceFileUploadService)(i0__namespace.ɵɵinject(i1$1__namespace.File)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: DeviceFileUploadService, factory: DeviceFileUploadService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(DeviceFileUploadService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i1$1__namespace.File }], null); })();

    class ExtAppMessageService {
        static { this.SERVICE_NAME = 'ExtAppMessageService'; }
        constructor(app) {
            this.app = app;
            this.handlers = [];
            document.addEventListener('externalAppMessageReceived', e => {
                const message = (e['detail'].message);
                this.handlers.forEach(handler => {
                    const matches = handler && message.address.match(handler.pattern);
                    if (matches && matches.length > 0) {
                        handler.callBack(message);
                    }
                });
            });
        }
        /**
         * adds a listener for a message whose address matches with the given regex pattern.
         *
         * @param {string} messageAddressPattern a regex pattern that is used to target messages to listen.
         * @param {Function} listener function to invoke when message that matches regex is received.
         *                  message received will be sent as first argument.
         * @returns {Function} a function which removes the listener when invoked.
         */
        subscribe(messageAddressPattern, listener) {
            const handler = {
                pattern: new RegExp(messageAddressPattern),
                callBack: listener
            };
            this.handlers.push(handler);
            return () => lodashEs.remove(this.handlers, handler);
        }
        static { this.ɵfac = function ExtAppMessageService_Factory(t) { return new (t || ExtAppMessageService)(i0__namespace.ɵɵinject(i2$2__namespace.App)); }; }
        static { this.ɵprov = /*@__PURE__*/ i0__namespace.ɵɵdefineInjectable({ token: ExtAppMessageService, factory: ExtAppMessageService.ɵfac, providedIn: 'root' }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(ExtAppMessageService, [{
            type: i0.Injectable,
            args: [{ providedIn: 'root' }]
        }], () => [{ type: i2$2__namespace.App }], null); })();
    (function (window, document) {
        // listen app-to-app communication via url schemes
        function subString(str, begin, end) {
            end = end < 0 ? undefined : end;
            return (str && begin >= 0 && str.length > begin && str.substring(begin, end)) || undefined;
        }
        function indexOf(str, pattern) {
            return str && str.indexOf(pattern);
        }
        /**
         * returns if oAuth flow is implicit or pkce using state object in redirect_uri response.
         *
         * @param {url} string which contains access token and state information.
         * @returns {Boolean}
         */
        function getFlow(url) {
            if (!url) {
                return false;
            }
            const stateObj = url.match(/\{([^)]+)\}/);
            if (stateObj) {
                const parsedObj = i2$2.getValidJSON('{' + stateObj[1] + '}');
                if (parsedObj && parsedObj.flow) {
                    return parsedObj.flow;
                }
            }
            return false;
        }
        function extractData(url) {
            let str = subString(url, indexOf(url, '?') + 1, indexOf(url, '#'));
            const data = {}, flow = getFlow(url);
            // access token comes as a hash for implicit flow
            if (flow === 'implicit') {
                str = url.match(/access_token=([^&#]*)/)[0];
            }
            else if (flow === 'pkce') {
                str = url.match(/code=([^&#]*)/)[0];
            }
            lodashEs.forEach(lodashEs.split(str, '&'), entry => {
                const esplits = entry.split('=');
                data[esplits[0]] = esplits[1];
            });
            return data;
        }
        function extractAddress(url) {
            // access token comes as a hash for implicit flow
            const addressKey = getFlow(url) === 'implicit' ? '#' : '?';
            return subString(url, indexOf(url, '://') + 3, indexOf(url, addressKey));
        }
        function createMessage(url) {
            return {
                'address': extractAddress(url),
                'data': extractData(url)
            };
        }
        window['handleOpenURL'] = function (url) {
            const handleOpenURL = window['handleOpenURL'];
            if (handleOpenURL.isReady && !lodashEs.startsWith(url, 'http')) {
                const message = createMessage(url);
                const e = new window['CustomEvent']('externalAppMessageReceived', {
                    'detail': {
                        'message': message
                    }
                });
                document.dispatchEvent(e);
            }
            handleOpenURL.lastURL = url;
        };
    }(window, document));

    exports.DeviceFileCacheService = DeviceFileCacheService;
    exports.DeviceFileDownloadService = DeviceFileDownloadService;
    exports.DeviceFileOpenerService = DeviceFileOpenerService;
    exports.DeviceFileService = DeviceFileService;
    exports.DeviceFileUploadService = DeviceFileUploadService;
    exports.DeviceService = DeviceService;
    exports.ExtAppMessageService = ExtAppMessageService;
    exports.MobileCoreModule = MobileCoreModule;
    exports.NetworkService = NetworkService;
    exports.UploadRequest = UploadRequest;

}));
