(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@awesome-cordova-plugins/app-version/ngx'), require('@awesome-cordova-plugins/barcode-scanner/ngx'), require('@awesome-cordova-plugins/calendar/ngx'), require('@awesome-cordova-plugins/camera/ngx'), require('@awesome-cordova-plugins/device/ngx'), require('@awesome-cordova-plugins/media-capture/ngx'), require('@awesome-cordova-plugins/geolocation/ngx'), require('@awesome-cordova-plugins/vibration/ngx'), require('@awesome-cordova-plugins/location-accuracy/ngx'), require('@awesome-cordova-plugins/diagnostic/ngx'), require('@wm/core'), require('@wm/mobile/core'), require('@wm/mobile/offline'), require('@wm/security'), require('@wm/variables'), require('lodash-es'), require('@wm/mobile/components/basic')) :
    typeof define === 'function' && define.amd ? define(['exports', '@angular/core', '@awesome-cordova-plugins/app-version/ngx', '@awesome-cordova-plugins/barcode-scanner/ngx', '@awesome-cordova-plugins/calendar/ngx', '@awesome-cordova-plugins/camera/ngx', '@awesome-cordova-plugins/device/ngx', '@awesome-cordova-plugins/media-capture/ngx', '@awesome-cordova-plugins/geolocation/ngx', '@awesome-cordova-plugins/vibration/ngx', '@awesome-cordova-plugins/location-accuracy/ngx', '@awesome-cordova-plugins/diagnostic/ngx', '@wm/core', '@wm/mobile/core', '@wm/mobile/offline', '@wm/security', '@wm/variables', 'lodash-es', '@wm/mobile/components/basic'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.wm = global.wm || {}, global.wm.mobile = global.wm.mobile || {}, global.wm.mobile.variables = {}), global.ng.core, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.awesomeCordova.plugins, global.wm.core, global.wm.mobile.core, global.wm.mobile.offline, global.wm.security, global.wm.variables, global._, global.wm.mobile.components.basic));
})(this, (function (exports, i0, i2, i3, i5, i6, i9, i11, i10, i13, i14, i15, i1, i7, i4, i12, variables, lodashEs, i8) { '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__namespace = /*#__PURE__*/_interopNamespaceDefault(i2);
    var i3__namespace = /*#__PURE__*/_interopNamespaceDefault(i3);
    var i5__namespace = /*#__PURE__*/_interopNamespaceDefault(i5);
    var i6__namespace = /*#__PURE__*/_interopNamespaceDefault(i6);
    var i9__namespace = /*#__PURE__*/_interopNamespaceDefault(i9);
    var i11__namespace = /*#__PURE__*/_interopNamespaceDefault(i11);
    var i10__namespace = /*#__PURE__*/_interopNamespaceDefault(i10);
    var i13__namespace = /*#__PURE__*/_interopNamespaceDefault(i13);
    var i14__namespace = /*#__PURE__*/_interopNamespaceDefault(i14);
    var i15__namespace = /*#__PURE__*/_interopNamespaceDefault(i15);
    var i1__namespace = /*#__PURE__*/_interopNamespaceDefault(i1);
    var i7__namespace = /*#__PURE__*/_interopNamespaceDefault(i7);
    var i4__namespace = /*#__PURE__*/_interopNamespaceDefault(i4);
    var i12__namespace = /*#__PURE__*/_interopNamespaceDefault(i12);
    var i8__namespace = /*#__PURE__*/_interopNamespaceDefault(i8);

    const DEFAULT_TIME = new Date().getTime();
    /*3 months timestamp value*/
    const DELTA_VALUE_DATE = (3 * 30 * 24 * 60 * 60 * 1000);
    const DEFAULT_START_DATE = (DEFAULT_TIME - DELTA_VALUE_DATE);
    const DEFAULT_END_DATE = (DEFAULT_TIME + DELTA_VALUE_DATE);
    const EVENT_META = {
        title: '',
        message: '',
        location: '',
        startDate: new Date(),
        endDate: new Date()
    };
    /**
     * this file contains all calendar operations under 'calendar' service.
     */
    class CalendarService extends variables.DeviceVariableService {
        constructor(calendar) {
            super();
            this.name = 'calendar';
            this.operations = [];
            this.operations.push(new CreateEventOperation(calendar), new DeleteEventOperation(calendar), new GetEventsOperation(calendar));
        }
    }
    class CreateEventOperation {
        constructor(calendar) {
            this.calendar = calendar;
            this.name = 'createEvent';
            this.properties = [
                { target: 'eventTitle', type: 'string', dataBinding: true },
                { target: 'eventNotes', type: 'string', dataBinding: true },
                { target: 'eventLocation', type: 'string', dataBinding: true },
                { target: 'eventStart', type: 'datetime', dataBinding: true },
                { target: 'eventEnd', type: 'datetime', dataBinding: true }
            ];
            this.requiredCordovaPlugins = ['CALENDAR'];
        }
        invoke(variable, options, eventInfo) {
            return this.calendar.createEvent(eventInfo.get('eventTitle'), eventInfo.get('eventLocation'), eventInfo.get('eventNotes'), new Date(eventInfo.get('eventStart') || 0), new Date(eventInfo.get('eventEnd') || 0));
        }
    }
    class DeleteEventOperation {
        constructor(calendar) {
            this.calendar = calendar;
            this.name = 'deleteEvent';
            this.properties = [
                { target: 'eventTitle', type: 'string', dataBinding: true },
                { target: 'eventNotes', type: 'string', dataBinding: true },
                { target: 'eventLocation', type: 'string', dataBinding: true },
                { target: 'eventStart', type: 'datetime', dataBinding: true },
                { target: 'eventEnd', type: 'datetime', dataBinding: true }
            ];
            this.requiredCordovaPlugins = ['CALENDAR'];
        }
        invoke(variable, options, eventInfo) {
            return this.calendar.deleteEvent(eventInfo.get('eventTitle'), eventInfo.get('eventLocation'), eventInfo.get('eventNotes'), new Date(eventInfo.get('eventStart') || DEFAULT_START_DATE), new Date(eventInfo.get('eventEnd') || DEFAULT_END_DATE));
        }
    }
    class GetEventsOperation {
        constructor(calendar) {
            this.calendar = calendar;
            this.name = 'getEvents';
            this.model = [EVENT_META];
            this.properties = [
                { target: 'eventTitle', type: 'string', dataBinding: true },
                { target: 'eventNotes', type: 'string', dataBinding: true },
                { target: 'eventLocation', type: 'string', dataBinding: true },
                { target: 'eventStart', type: 'datetime', dataBinding: true },
                { target: 'eventEnd', type: 'datetime', dataBinding: true }
            ];
            this.requiredCordovaPlugins = ['CALENDAR'];
        }
        invoke(variable, options, eventInfo) {
            return this.calendar.findEvent(eventInfo.get('eventTitle'), eventInfo.get('eventLocation'), eventInfo.get('eventNotes'), new Date(eventInfo.get('eventStart') || DEFAULT_START_DATE), new Date(eventInfo.get('eventEnd') || DEFAULT_END_DATE));
        }
    }

    class CameraService extends variables.DeviceVariableService {
        constructor(camera, mediaCapture) {
            super();
            this.name = 'camera';
            this.operations = [];
            this.operations.push(new CaptureImageOperation(camera), new CaptureVideoOperation(mediaCapture));
        }
    }
    class CaptureImageOperation {
        constructor(camera) {
            this.camera = camera;
            this.name = 'captureImage';
            this.model = {
                imagePath: 'resources/images/imagelists/default-image.png'
            };
            this.properties = [
                { target: 'allowImageEdit', type: 'boolean', value: false, dataBinding: true },
                { target: 'imageQuality', type: 'number', value: 80, dataBinding: true },
                { target: 'imageEncodingType', type: 'list', options: { '0': 'JPEG', '1': 'PNG' }, value: '0', dataBinding: true },
                { target: 'correctOrientation', type: 'boolean', value: true, dataBinding: true },
                { target: 'imageTargetWidth', type: 'number', dataBinding: true },
                { target: 'imageTargetHeight', type: 'number', dataBinding: true }
            ];
            this.requiredCordovaPlugins = ['CAMERA', 'CAPTURE'];
        }
        invoke(variable, options, dataBindings) {
            const imageTargetWidth = dataBindings.get('imageTargetWidth'), imageTargetHeight = dataBindings.get('imageTargetHeight');
            let imageEncodingType = parseInt(dataBindings.get('imageEncodingType'), 10), cameraOptions;
            if (isNaN(imageEncodingType)) {
                imageEncodingType = (dataBindings.get('imageEncodingType') === 'JPEG' ? 0 : 1);
            }
            cameraOptions = {
                quality: dataBindings.get('imageQuality'),
                destinationType: 1, // only file url
                sourceType: 1, // camera
                allowEdit: dataBindings.get('allowImageEdit'),
                encodingType: imageEncodingType,
                mediaType: 0, // always picture
                correctOrientation: dataBindings.get('correctOrientation'),
                targetWidth: lodashEs.isNumber(imageTargetWidth) ? imageTargetWidth : undefined,
                targetHeight: lodashEs.isNumber(imageTargetHeight) ? imageTargetHeight : undefined,
            };
            return this.camera.getPicture(cameraOptions).then(data => {
                return { imagePath: data };
            });
        }
    }
    class CaptureVideoOperation {
        constructor(mediaCapture) {
            this.mediaCapture = mediaCapture;
            this.name = 'captureVideo';
            this.model = {
                videoPath: ''
            };
            this.properties = [];
            this.requiredCordovaPlugins = ['CAMERA', 'CAPTURE'];
        }
        invoke(variable, options) {
            return this.mediaCapture.captureVideo({
                limit: 1
            }).then(data => {
                let path = data[0].fullPath;
                path = (path.startsWith('file://') ? '' : 'file://') + path;
                return { videoPath: path };
            });
        }
    }

    class FileService extends variables.DeviceVariableService {
        constructor(fileOpener, fileUploader) {
            super();
            this.name = 'file';
            this.operations = [];
            this.operations.push(new OpenFileOperation(fileOpener), new UploadFileOperation(fileUploader));
        }
    }
    class OpenFileOperation {
        constructor(fileOpener) {
            this.fileOpener = fileOpener;
            this._defaultFileTypesToOpen = {
                'doc': { 'label': 'Microsoft Office Word Document', 'mimeType': 'application/msword', 'extension': 'doc' },
                'pdf': { 'label': 'PDF Document', 'mimeType': 'application/pdf', 'extension': 'pdf' },
                'ppt': { 'label': 'Microsoft Office Powerpoint', 'mimeType': 'application/vnd.ms-powerpoint', 'extension': 'ppt' },
                'xls': { 'label': 'Microsoft Office Excel', 'mimeType': 'application/vnd.ms-excel', 'extension': 'xls' }
            };
            this.name = 'openFile';
            this.model = {};
            this.properties = [
                { target: 'filePath', type: 'string', value: '', dataBinding: true },
                {
                    target: 'fileType',
                    type: 'list',
                    options: lodashEs.mapValues(this._defaultFileTypesToOpen, 'label'),
                    value: 'pdf',
                    dataBinding: true,
                    hide: true
                },
                { target: 'spinnerContext', hide: false },
                { target: 'spinnerMessage', hide: false }
            ];
            this.requiredCordovaPlugins = [];
        }
        invoke(variable, options, dataBindings) {
            const fileType = this._defaultFileTypesToOpen[dataBindings.get('fileType')];
            let filePath = dataBindings.get('filePath');
            // if relative path is given, then append url with deployedUrl, to access files in resources.
            if (!i1.isValidWebURL(filePath)) {
                filePath = variables.$rootScope.project.deployedUrl + filePath;
            }
            return this.fileOpener.openRemoteFile(filePath, fileType.extension);
        }
    }
    class UploadFileOperation {
        constructor(fileUploader) {
            this.fileUploader = fileUploader;
            this.name = 'upload';
            this.model = {
                fileName: '',
                path: '',
                length: 0,
                success: false,
                inlinePath: '',
                errorMessage: '',
                inProgress: false,
                loaded: 0
            };
            this.properties = [
                { target: 'localFile', type: 'string', value: '', dataBinding: true },
                { target: 'remoteFolder', type: 'string', value: '', dataBinding: true },
                { target: 'onProgress', hide: false },
                { target: 'spinnerContext', hide: false },
                { target: 'spinnerMessage', hide: false }
            ];
            this.requiredCordovaPlugins = [];
        }
        invoke(variable, options, dataBindings) {
            const serverUrl = variables.$rootScope.project.deployedUrl + 'services/file/uploadFile?relativePath=' + (dataBindings.get('remoteFolder') || ''), filePath = dataBindings.get('localFile'), fileName = filePath.split('/').pop(), data = {
                fileName: fileName,
                fileSize: 0,
                inProgress: true,
                length: 0,
                loaded: 0
            };
            return this.fileUploader.upload(serverUrl, 'files', filePath, fileName)
                .then(uploadResponse => {
                lodashEs.assignIn(data, JSON.parse(uploadResponse.text)[0]);
                data.loaded = data.length;
                return data;
            });
        }
    }

    const APP_IS_OFFLINE = 'App is offline.';
    const OFFLINE_PLUGIN_NOT_FOUND = 'Offline DB Plugin is required, but missing.';
    const ON_BEFORE_BLOCKED = 'onBefore callback returned false.';
    const REQUIRED_PLUGINS = ['OFFLINE_DB', 'NETWORK'];
    class DatasyncService extends variables.DeviceVariableService {
        constructor(app, changeLogService, fileSelectorService, localDBManagementService, localDBDataPullService, processManagementService, securityService, networkService) {
            super();
            this.name = 'datasync';
            this.operations = [];
            this.operations.push(new ExportDBOperation(localDBManagementService));
            this.operations.push(new GetOfflineChangesOperation(changeLogService));
            this.operations.push(new ImportDBOperation(fileSelectorService, localDBManagementService));
            this.operations.push(new LastPullInfoOperation(localDBDataPullService));
            this.operations.push(new LastPushInfoOperation(changeLogService));
            this.operations.push(new PullOperation(app, processManagementService, networkService, securityService, localDBDataPullService));
            this.operations.push(new PushOperation(app, changeLogService, processManagementService, networkService, securityService));
        }
    }
    class ExportDBOperation {
        constructor(localDBManagementService) {
            this.localDBManagementService = localDBManagementService;
            this.name = 'exportDB';
            this.model = { path: '' };
            this.properties = [
                { target: 'spinnerContext', hide: false },
                { target: 'spinnerMessage', hide: false }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            if (window['SQLitePlugin']) {
                return this.localDBManagementService.exportDB();
            }
            return Promise.reject(OFFLINE_PLUGIN_NOT_FOUND);
        }
    }
    class GetOfflineChangesOperation {
        static { this.DATA_CHANGE_TEMPLATE = {
            service: 'DatabaseService',
            operation: 'operation',
            params: {
                data: {},
                dataModelName: 'dataModelName',
                entityName: 'entityName'
            },
            hasError: 0,
            errorMessage: ''
        }; }
        static { this.CHANGE_LOG_SET = {
            total: 0,
            database: {
                create: [GetOfflineChangesOperation.DATA_CHANGE_TEMPLATE],
                update: [GetOfflineChangesOperation.DATA_CHANGE_TEMPLATE],
                delete: [GetOfflineChangesOperation.DATA_CHANGE_TEMPLATE]
            },
            uploads: [{
                    service: 'OfflineFileUploadService',
                    operation: 'uploadToServer',
                    params: {
                        file: 'localFilePath',
                        serverUrl: 'serverUrl',
                        ftOptions: {}
                    },
                    hasError: 0,
                    errorMessage: ''
                }]
        }; }
        constructor(changeLogService) {
            this.changeLogService = changeLogService;
            this.name = 'getOfflineChanges';
            this.model = {
                total: 0,
                pendingToSync: GetOfflineChangesOperation.CHANGE_LOG_SET,
                failedToSync: GetOfflineChangesOperation.CHANGE_LOG_SET
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean', value: true, hide: true },
                { target: 'autoUpdate', type: 'boolean', value: true, hide: true }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            if (window['SQLitePlugin']) {
                return getOfflineChanges(this.changeLogService);
            }
            return Promise.reject(OFFLINE_PLUGIN_NOT_FOUND);
        }
    }
    class LastPullInfoOperation {
        constructor(localDBDataPullService) {
            this.localDBDataPullService = localDBDataPullService;
            this.name = 'lastPullInfo';
            this.model = {
                databases: [{
                        name: 'datbaseName',
                        entities: [{
                                entityName: 'entityName',
                                pulledRecordCount: 0
                            }],
                        pulledRecordCount: 0
                    }],
                totalPulledRecordCount: 0,
                startTime: new Date().toJSON(),
                endTime: new Date().toJSON()
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean', value: true, hide: true },
                { target: 'spinnerContext', hide: false },
                { target: 'spinnerMessage', hide: false }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            if (window['SQLitePlugin']) {
                return this.localDBDataPullService.getLastPullInfo();
            }
            return Promise.reject(OFFLINE_PLUGIN_NOT_FOUND);
        }
    }
    class LastPushInfoOperation {
        constructor(changeLogService) {
            this.changeLogService = changeLogService;
            this.name = 'lastPushInfo';
            this.model = {
                successfulTaskCount: 0,
                failedTaskCount: 0,
                completedTaskCount: 0,
                totalTaskCount: 0,
                startTime: new Date().toJSON(),
                endTime: new Date().toJSON()
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean', value: true, hide: true },
                { target: 'spinnerContext', hide: false },
                { target: 'spinnerMessage', hide: false }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            if (window['SQLitePlugin']) {
                return this.changeLogService.getLastPushInfo();
            }
            return Promise.reject(OFFLINE_PLUGIN_NOT_FOUND);
        }
    }
    class ImportDBOperation {
        constructor(fileSelectorService, localDBManagementService) {
            this.fileSelectorService = fileSelectorService;
            this.localDBManagementService = localDBManagementService;
            this.name = 'importDB';
            this.model = {};
            this.properties = [
                { target: 'spinnerContext', hide: false },
                { target: 'spinnerMessage', hide: false }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            if (window['SQLitePlugin']) {
                return this.fileSelectorService.selectFiles(false, 'zip')
                    .then(files => {
                    if (files && files.length) {
                        return this.localDBManagementService.importDB(files[0].path, true);
                    }
                });
            }
            return Promise.reject(OFFLINE_PLUGIN_NOT_FOUND);
        }
    }
    class PullOperation {
        constructor(app, processManagementService, networkService, securityService, localDBDataPullService) {
            this.app = app;
            this.processManagementService = processManagementService;
            this.networkService = networkService;
            this.securityService = securityService;
            this.localDBDataPullService = localDBDataPullService;
            this.name = 'pull';
            this.model = {
                totalTaskCount: 0,
                completedTaskCount: 0,
                inProgress: false
            };
            this.properties = [
                { target: 'clearData', type: 'boolean', widgettype: 'boolean-inputfirst', value: true, group: 'properties', subGroup: 'behavior' },
                { target: 'startUpdate', type: 'boolean', hide: false },
                { target: 'onBefore', hide: false },
                { target: 'onProgress', hide: false },
                { target: 'showProgress', hide: false }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            let progressInstance;
            return canExecute(variable, this.networkService, this.securityService)
                .then(() => {
                if (variable.showProgress) {
                    return this.processManagementService.createInstance(this.app.appLocale.LABEL_DATA_PULL_PROGRESS, 0, 0);
                }
                return null;
            }).then((instance) => {
                const progressObserver = {
                    next: (pullInfo) => {
                        // variable.dataSet = progress; Todo: progress
                        variables.initiateCallback('onProgress', variable, pullInfo);
                        if (progressInstance) {
                            progressInstance.set('max', pullInfo.totalRecordsToPull);
                            progressInstance.set('value', pullInfo.totalPulledRecordCount);
                        }
                    }, error: i1.noop, complete: i1.noop
                };
                const clearData = variable.clearData === 'true' || variable.clearData === true, pullPromise = this.localDBDataPullService.pullAllDbData(clearData, progressObserver);
                if (instance) {
                    progressInstance = instance;
                    progressInstance.set('stopButtonLabel', this.app.appLocale.LABEL_DATA_PULL_PROGRESS_STOP_BTN);
                    progressInstance.set('onStop', () => {
                        this.localDBDataPullService.cancel(pullPromise);
                    });
                }
                return pullPromise;
            }).catch(pullInfo => pullInfo)
                .then(pullInfo => {
                if (progressInstance) {
                    progressInstance.destroy();
                }
                return pullInfo;
            });
        }
    }
    class PushOperation {
        constructor(app, changeLogService, processManagementService, networkService, securityService) {
            this.app = app;
            this.changeLogService = changeLogService;
            this.processManagementService = processManagementService;
            this.networkService = networkService;
            this.securityService = securityService;
            this.name = 'push';
            this.model = {
                successfulTaskCount: 0,
                failedTaskCount: 0,
                completedTaskCount: 0,
                totalTaskCount: 0,
                inProgress: false
            };
            this.properties = [
                { target: 'onBefore', hide: false },
                { target: 'onProgress', hide: false },
                { target: 'showProgress', 'hide': false, 'value': true }
            ];
            this.requiredCordovaPlugins = REQUIRED_PLUGINS;
        }
        invoke(variable, options, dataBindings) {
            let progressInstance;
            if (this.changeLogService.isFlushInProgress()) {
                return Promise.resolve();
            }
            return canExecute(variable, this.networkService, this.securityService)
                .then(() => getOfflineChanges(this.changeLogService))
                .then(changes => {
                if (changes.pendingToSync.total <= 0) {
                    return Promise.reject(lodashEs.clone(this.model));
                }
            }).then(() => {
                if (variable.showProgress) {
                    return this.processManagementService.createInstance(this.app.appLocale.LABEL_DATA_PUSH_PROGRESS, 0, 0);
                }
                return null;
            }).then((instance) => {
                const progressObserver = {
                    next: (pushInfo) => {
                        pushInfo = addOldPropertiesForPushData(pushInfo);
                        variables.initiateCallback('onProgress', variable, pushInfo);
                        if (progressInstance) {
                            progressInstance.set('max', pushInfo.totalTaskCount);
                            progressInstance.set('value', pushInfo.completedTaskCount);
                        }
                    }, error: i1.noop, complete: i1.noop
                };
                const pushPromise = this.changeLogService.flush(progressObserver);
                if (instance) {
                    progressInstance = instance;
                    progressInstance.set('stopButtonLabel', this.app.appLocale.LABEL_DATA_PUSH_PROGRESS_STOP_BTN);
                    progressInstance.set('onStop', () => this.changeLogService.stop());
                }
                return pushPromise;
            })
                .catch(pushInfo => pushInfo)
                .then(pushInfo => {
                if (progressInstance) {
                    progressInstance.destroy();
                }
                if (pushInfo && pushInfo.totalTaskCount !== undefined) {
                    pushInfo = addOldPropertiesForPushData(pushInfo);
                    if (pushInfo.failedTaskCount !== 0) {
                        return Promise.reject(pushInfo);
                    }
                    return pushInfo;
                }
                return Promise.reject(pushInfo);
            });
        }
    }
    /**
     * This function adds the old properties to the push dataSet to support old projects.
     * @param data
     * @returns {*}
     */
    const addOldPropertiesForPushData = data => {
        const result = lodashEs.clone(data);
        result.success = data.successfulTaskCount;
        result.error = data.failedTaskCount;
        result.completed = data.completedTaskCount;
        result.total = data.totalTaskCount;
        return result;
    };
    const canExecute = (variable, networkService, securityService) => {
        if (!window['SQLitePlugin']) {
            return Promise.reject(OFFLINE_PLUGIN_NOT_FOUND);
        }
        if (!networkService.isConnected()) {
            return Promise.reject(APP_IS_OFFLINE);
        }
        return i1.toPromise(variables.initiateCallback(variables.VARIABLE_CONSTANTS.EVENT.BEFORE, variable, null))
            .then(proceed => {
            if (proceed === false) {
                return Promise.reject(ON_BEFORE_BLOCKED);
            }
            // If user is authenticated and online, then start the data pull process.
            return securityService.onUserLogin();
        });
    };
    const generateChangeSet = (changes) => {
        const createChanges = lodashEs.filter(changes, c => {
            return c.service === 'DatabaseService' &&
                (c.operation === 'insertTableData'
                    || c.operation === 'insertMultiPartTableData');
        }), updateChanges = lodashEs.filter(changes, c => {
            return c.service === 'DatabaseService' &&
                (c.operation === 'updateTableData'
                    || c.operation === 'updateMultiPartTableData');
        });
        return {
            total: changes ? changes.length : 0,
            database: {
                create: createChanges,
                update: updateChanges,
                delete: lodashEs.filter(changes, { service: 'DatabaseService', operation: 'deleteTableData' })
            },
            uploads: lodashEs.filter(changes, { service: 'OfflineFileUploadService', operation: 'uploadToServer' })
        };
    };
    const getOfflineChanges = (changeLogService) => {
        return changeLogService.getChanges().then(changes => {
            return {
                'total': changes ? changes.length : 0,
                'pendingToSync': generateChangeSet(lodashEs.filter(changes, { 'hasError': 0 })),
                'failedToSync': generateChangeSet(lodashEs.filter(changes, { 'hasError': 1 }))
            };
        });
    };

    const PERMISSION_DENIED_ONCE = "DENIED_ONCE";
    /**
     * This class handles 'getCurrentGeoPosition' device operation.
     */
    class CurrentGeoPositionOperation {
        constructor(app, geoLocation, locationAccuracyService, diagnosticService) {
            this.app = app;
            this.geoLocation = geoLocation;
            this.locationAccuracyService = locationAccuracyService;
            this.diagnosticService = diagnosticService;
            this.name = 'getCurrentGeoPosition';
            this.model = {
                coords: {
                    latitude: 0,
                    longitude: 0,
                    altitude: 0,
                    accuracy: 0,
                    altitudeAccuracy: 0,
                    heading: 0,
                    speed: 0
                },
                timestamp: 0
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean', value: true, hide: true },
                { target: 'autoUpdate', type: 'boolean', value: true, hide: true },
                { target: 'geolocationHighAccuracy', type: 'boolean', value: true, dataBinding: true },
                { target: 'geolocationMaximumAge', type: 'number', value: 3, dataBinding: true },
                { target: 'geolocationTimeout', type: 'number', value: 5, dataBinding: true },
            ];
            this.requiredCordovaPlugins = ['GEOLOCATION'];
            this.waitingQueue = [];
            this.locationRequestedAlready = false;
            this.options = {
                maximumAge: 3000,
                timeout: (2 * 60) * 1000,
                enableHighAccuracy: true
            };
        }
        watchPosition() {
            if (this.watchId) {
                navigator.geolocation.clearWatch(this.watchId);
                this.watchId = null;
            }
            const options = window['WM_GEO_LOCATION_OPTIONS'] || this.options;
            this.watchId = navigator.geolocation.watchPosition(position => {
                this.lastKnownPosition = {
                    coords: {
                        latitude: position.coords.latitude,
                        longitude: position.coords.longitude,
                        altitude: position.coords.altitude,
                        accuracy: position.coords.accuracy,
                        altitudeAccuracy: position.coords.altitudeAccuracy,
                        heading: position.coords.heading,
                        speed: position.coords.speed
                    },
                    timestamp: position.timestamp
                };
                if (this.waitingQueue.length > 0) {
                    this.waitingQueue.forEach(fn => fn(this.lastKnownPosition));
                    this.waitingQueue.length = 0;
                }
                $(document).off('touchend.usergesture');
            }, () => {
                this.watchId = null;
            }, options);
        }
        geoLocationService(variable, options, dataBindings) {
            if (!this.watchId || !this.lastKnownPosition) {
                this.watchPosition();
                $(document).on('touchend.usergesture', () => this.watchPosition());
            }
            ({
                maximumAge: dataBindings.get('geolocationMaximumAge') * 1000,
                timeout: dataBindings.get('geolocationTimeout') * 1000,
                enableHighAccuracy: dataBindings.get('geolocationHighAccuracy')
            });
            if (this.lastKnownPosition) {
                return Promise.resolve(this.lastKnownPosition);
            }
            return new Promise(resolve => {
                const c = position => {
                    resolve(position);
                };
                setTimeout(() => {
                    const index = this.waitingQueue.indexOf(c);
                    if (index > -1) {
                        this.waitingQueue.splice(index, 1);
                        resolve(this.model);
                    }
                }, this.options.timeout);
                this.waitingQueue.push(c);
            });
        }
        requestLocationService(variable, options, dataBindings) {
            return this.locationAccuracyService.canRequest()
                .then((canRequest) => {
                if (canRequest && !this.locationRequestedAlready) {
                    //In iOS, Only once location service request can be made, Repeated access will be blocked by OS 
                    //https://github.com/dpa99c/cordova-plugin-request-location-accuracy#ios-cancel-button-caveat
                    this.locationRequestedAlready = i1.isIos();
                    return this.locationAccuracyService.request(this.locationAccuracyService.REQUEST_PRIORITY_HIGH_ACCURACY);
                }
                else {
                    if (i1.isIos() && confirm(this.app.appLocale.MESSAGE_REQUEST_LOCATION_PERMISSION)) {
                        this.diagnosticService.switchToSettings();
                        return Promise.reject();
                    }
                    return Promise.resolve();
                }
            })
                .then(() => {
                return this.geoLocationService(variable, options, dataBindings);
            })
                .catch((e) => {
                if (e && e.code
                    && e.code !== this.locationAccuracyService.ERROR_USER_DISAGREED
                    && e.code !== this.locationAccuracyService.ERROR_CANNOT_CHANGE_ACCURACY) {
                    if (confirm(this.app.appLocale.MESSAGE_REQUEST_LOCATION_PERMISSION)) {
                        this.diagnosticService.switchToSettings();
                    }
                }
                return Promise.resolve(this.lastKnownPosition ? this.lastKnownPosition : this.model);
            });
        }
        handleLocationAuthorizationStatus(variable, options, dataBindings, permissionStatus) {
            this.previousPermissionStatus = this.currentPermissionStatus;
            this.currentPermissionStatus = permissionStatus;
            switch (permissionStatus) {
                case this.diagnosticService.permissionStatus.GRANTED_WHEN_IN_USE:
                case this.diagnosticService.permissionStatus.GRANTED: {
                    return this.onLocationGranted(variable, options, dataBindings);
                }
                case PERMISSION_DENIED_ONCE: {
                    return this.requestLocationService(variable, options, dataBindings);
                }
                case this.diagnosticService.permissionStatus.DENIED_ALWAYS: {
                    if (i1.isIos()) {
                        //iOS Location Service Turned Off returns DENIED_ALWAYS
                        return this.requestLocationService(variable, options, dataBindings);
                    }
                    else {
                        if (confirm(this.app.appLocale.MESSAGE_REQUEST_LOCATION_PERMISSION)) {
                            this.diagnosticService.switchToSettings();
                        }
                        return Promise.resolve(this.model);
                    }
                }
                case this.diagnosticService.permissionStatus.NOT_REQUESTED: {
                    return this.requestLocationAuthorization(variable, options, dataBindings);
                }
            }
        }
        requestLocationAuthorization(variable, options, dataBindings) {
            return this.diagnosticService.requestLocationAuthorization()
                .then((permissionStatus) => {
                return this.handleLocationAuthorizationStatus(variable, options, dataBindings, permissionStatus);
            });
        }
        requestLocationAccuracy(variable, options, dataBindings) {
            return this.diagnosticService.getLocationAuthorizationStatus()
                .then((permissionStatus) => {
                return this.handleLocationAuthorizationStatus(variable, options, dataBindings, permissionStatus);
            })
                .catch((e) => {
                //In case of device older than Android 6, Due to absence of Permission Check API.
                return this.geoLocationService(variable, options, dataBindings);
            });
        }
        onLocationGranted(variable, options, dataBindings) {
            //iOS updates 'Granted' Only if Location service is turned On
            //Android updates 'Granted' even if Location service is turned Off
            if (i1.isIos()) {
                return this.geoLocationService(variable, options, dataBindings);
            }
            else if (i1.isAndroid()
                && parseInt(i1.getAndroidVersion(), 10) <= 10
                && this.previousPermissionStatus === PERMISSION_DENIED_ONCE
                && this.currentPermissionStatus === this.diagnosticService.permissionStatus.GRANTED_WHEN_IN_USE
                && !this.lastKnownPosition) {
                //Below Android 11, Chrome webview of Cordova is not updating the permission status from "denied" to "allowed" and throwing the below message
                //"application does not have sufficient geolocation permissions" For navigator.geolocation.watchPosition method
                //Hence we are reloading the webview manually
                location.reload();
            }
            else {
                return this.requestLocationService(variable, options, dataBindings);
            }
        }
        invoke(variable, options, dataBindings) {
            if (cordova['plugins'] && cordova['plugins']['locationAccuracy']) {
                return this.requestLocationAccuracy(variable, options, dataBindings);
            }
            else {
                return this.geoLocationService(variable, options, dataBindings);
            }
        }
    }

    /**
     * this file contains all device operations under 'device' service.
     */
    class DeviceService extends variables.DeviceVariableService {
        constructor(app, appVersion, device, geoLocation, networkService, vibrateService, locationAccuracyService, diagnosticService) {
            super();
            this.name = 'device';
            this.operations = [];
            this.operations.push(new AppInfoOperation(device, appVersion), new CurrentGeoPositionOperation(app, geoLocation, locationAccuracyService, diagnosticService), new DeviceInfoOperation(device), new GetNetworkInfoOperation(app, networkService), new GoOfflineOperation(networkService), new GoOnlineOperation(networkService), new VibrateOperation(vibrateService));
            app.subscribe('onNetworkStateChange', data => {
                app.networkStatus = data;
                i1.$appDigest();
            });
            app.networkStatus = {
                isConnecting: false,
                isConnected: true,
                isNetworkAvailable: true,
                isServiceAvailable: true
            };
        }
    }
    /**
     * This class handles 'getAppInfo' device operation.
     */
    class AppInfoOperation {
        constructor(device, appVersion) {
            this.device = device;
            this.appVersion = appVersion;
            this.name = 'getAppInfo';
            this.model = {
                appversion: 'X.X.X',
                cordovaversion: 'X.X.X'
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean', value: true, hide: true }
            ];
        }
        invoke(variable, options) {
            const cordovaVersion = this.device.cordova;
            return this.appVersion.getVersionNumber().then(appVersion => {
                return {
                    appversion: appVersion,
                    cordovaversion: cordovaVersion
                };
            });
        }
    }
    /**
     * This class handles 'getDeviceInfo' device operation.
     */
    class DeviceInfoOperation {
        constructor(device) {
            this.device = device;
            this.name = 'getDeviceInfo';
            this.model = {
                deviceModel: 'DEVICEMODEL',
                os: 'DEVICEOS',
                osVersion: 'X.X.X',
                deviceUUID: 'DEVICEUUID'
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean', value: true, hide: true }
            ];
        }
        invoke(variable, options) {
            const response = {
                'deviceModel': this.device.model,
                'os': this.device.platform,
                'osVersion': this.device.version,
                'deviceUUID': this.device.uuid
            };
            return Promise.resolve(response);
        }
    }
    class GetNetworkInfoOperation {
        constructor(app, networkService) {
            this.app = app;
            this.networkService = networkService;
            this.name = 'getNetworkInfo';
            this.model = {
                connectionType: 'NONE',
                isConnecting: false,
                isNetworkAvailable: true,
                isOnline: true,
                isOffline: false
            };
            this.properties = [
                { target: 'autoUpdate', type: 'boolean', value: true, hide: true },
                { target: 'startUpdate', type: 'boolean', value: true, hide: true },
                { target: 'networkStatus', type: 'object', value: 'bind:App.networkStatus', dataBinding: true, hide: true },
                { target: 'onOnline', hide: false },
                { target: 'onOffline', hide: false }
            ];
            this.requiredCordovaPlugins = ['NETWORK'];
        }
        invoke(variable, options, dataBindings) {
            const data = {
                connectionType: navigator.connection.type,
                isConnecting: this.app.networkStatus.isConnecting,
                isNetworkAvailable: this.app.networkStatus.isNetworkAvailable,
                isOnline: this.app.networkStatus.isConnected,
                isOffline: !this.app.networkStatus.isConnected
            };
            if (this.networkService.isConnected()) {
                variables.initiateCallback('onOnline', variable, data);
            }
            else {
                variables.initiateCallback('onOffline', variable, data);
            }
            return Promise.resolve(data);
        }
    }
    class GoOfflineOperation {
        constructor(networkService) {
            this.networkService = networkService;
            this.name = 'goOffline';
            this.model = {};
            this.properties = [];
            this.requiredCordovaPlugins = ['NETWORK'];
        }
        invoke(variable, options, dataBindings) {
            return this.networkService.disconnect();
        }
    }
    class GoOnlineOperation {
        constructor(networkService) {
            this.networkService = networkService;
            this.name = 'goOnline';
            this.model = {};
            this.properties = [];
            this.requiredCordovaPlugins = ['NETWORK'];
        }
        invoke(variable, options, dataBindings) {
            return this.networkService.connect();
        }
    }
    /**
     * This class handles 'vibrate' device operation.
     */
    class VibrateOperation {
        constructor(vibrationService) {
            this.vibrationService = vibrationService;
            this.name = 'vibrate';
            this.model = {
                appversion: 'X.X.X',
                cordovaversion: 'X.X.X'
            };
            this.properties = [
                { target: 'vibrationtime', type: 'number', value: 2, dataBinding: true }
            ];
            this.requiredCordovaPlugins = ['VIBRATE'];
        }
        invoke(variable, options, dataBindings) {
            this.vibrationService.vibrate(dataBindings.get('vibrationtime') * 1000);
            return Promise.resolve();
        }
    }

    class ContactsService extends variables.DeviceVariableService {
        constructor() {
            super();
            this.name = 'contacts';
            this.operations = [];
            this.operations.push(new GetContactsOperation());
        }
    }
    class GetContactsOperation {
        constructor() {
            this.name = 'getContacts';
            this.model = {
                id: '',
                displayName: '',
                phoneNumbers: [{ value: '' }]
            };
            this.properties = [
                { target: 'startUpdate', type: 'boolean' },
                { target: 'autoUpdate', type: 'boolean' },
                { target: 'contactFilter', type: 'string', value: '', dataBinding: true }
            ];
            this.requiredCordovaPlugins = ['CONTACTS'];
            this.waitingCalls = [];
        }
        extractDisplayName(c) {
            const name = c.displayName;
            // In IOS, displayName is undefined, so using the formatted name.
            if (!name || name === '') {
                if (c.name.formatted) {
                    return c.name.formatted;
                }
            }
            return name;
        }
        processNextCall() {
            if (this.waitingCalls.length > 0) {
                this.waitingCalls[0]();
            }
        }
        findContacts(requiredFields, findOptions) {
            return new Promise((resolve, reject) => {
                // Contacts plugin is not processing two simultaneous calls. It is anwsering to only call.
                this.waitingCalls.push(() => {
                    new Promise((resolve, reject) => {
                        navigator.contacts.find(requiredFields, resolve, reject, findOptions);
                    }).then((data) => {
                        if (data != null) {
                            const contacts = data.filter(c => {
                                c.displayName = this.extractDisplayName(c);
                                return c.phoneNumbers && c.phoneNumbers.length > 0;
                            });
                            resolve(contacts);
                        }
                    }, reject).then(() => {
                        this.waitingCalls.shift();
                        this.processNextCall();
                    });
                });
                if (this.waitingCalls.length === 1) {
                    this.processNextCall();
                }
            });
        }
        invoke(variable, options, dataBindings) {
            const requiredFields = ['displayName', 'name'];
            const findOptions = {
                filter: dataBindings.get('contactFilter'),
                multiple: true
            };
            return this.findContacts(requiredFields, findOptions);
        }
    }

    const barcodeFormatOptions = {
        'ALL': 'ALL',
        'CODABAR': 'CODABAR (not supported in iOS)',
        'CODE_39': 'CODE_39',
        'CODE_93': 'CODE_93 (not supported in iOS)',
        'CODE_128': 'CODE_128',
        'DATA_MATRIX': 'DATA_MATRIX',
        'EAN_8': 'EAN_8',
        'EAN_13': 'EAN_13',
        'ITF': 'ITF',
        'PDF_417': 'PDF_417 (not supported in iOS)',
        'QR_CODE': 'QR_CODE',
        'RSS14': 'RSS14 (not supported in iOS)',
        'RSS_EXPANDED': 'RSS_EXPANDED (not supported in iOS)',
        'UPC_E': 'UPC_E',
        'UPC_A': 'UPC_A'
    };
    class ScanService extends variables.DeviceVariableService {
        constructor(barcodeScanner) {
            super();
            this.name = 'scan';
            this.operations = [];
            this.operations.push(new ScanOperation(barcodeScanner));
        }
    }
    class ScanOperation {
        constructor(barcodeScanner) {
            this.barcodeScanner = barcodeScanner;
            this.name = 'scanBarCode';
            this.model = {
                text: 'BAR CODE',
                format: 'TEXT',
                cancelled: false
            };
            this.properties = [
                {
                    target: 'barcodeFormat',
                    type: 'list',
                    options: barcodeFormatOptions,
                    value: 'ALL',
                    group: 'properties',
                    subGroup: 'behavior',
                    hide: false
                }
            ];
            this.requiredCordovaPlugins = ['BARCODE_SCANNER'];
        }
        invoke(variable, options) {
            let scanOptions = {
                showFlipCameraButton: true,
                showTorchButton: true
            };
            if (variable.barcodeFormat && variable.barcodeFormat !== 'ALL') {
                scanOptions.formats = variable.barcodeFormat;
            }
            return this.barcodeScanner.scan(scanOptions);
        }
    }

    class VariablesModule {
        static { this.initialized = false; }
        // Device variable services have to be added only once in the app life-cycle.
        static initialize(app, appVersion, barcodeScanner, changeLogService, calendar, camera, fileOpener, fileSelectorService, fileUploader, device, geoLocation, localDBDataPullService, localDBManagementService, mediaCapture, processManagementService, securityService, networkService, vibrateService, locationAccuracyService, diagnosticService) {
            if (this.initialized) {
                return;
            }
            this.initialized = true;
            const deviceVariableManager = variables.VariableManagerFactory.get(variables.VARIABLE_CONSTANTS.CATEGORY.DEVICE);
            deviceVariableManager.registerService(new CameraService(camera, mediaCapture));
            deviceVariableManager.registerService(new CalendarService(calendar));
            deviceVariableManager.registerService(new FileService(fileOpener, fileUploader));
            deviceVariableManager.registerService(new ContactsService());
            deviceVariableManager.registerService(new DatasyncService(app, changeLogService, fileSelectorService, localDBManagementService, localDBDataPullService, processManagementService, securityService, networkService));
            deviceVariableManager.registerService(new DeviceService(app, appVersion, device, geoLocation, networkService, vibrateService, locationAccuracyService, diagnosticService));
            deviceVariableManager.registerService(new ScanService(barcodeScanner));
        }
        constructor(app, appVersion, barcodeScanner, changeLogService, calendar, camera, fileOpener, fileSelectorService, fileUploader, device, geoLocation, localDBDataPullService, localDBManagementService, mediaCapture, processManagementService, securityService, networkService, vibrateService, locationAccuracyService, diagnosticService) {
            VariablesModule.initialize(app, appVersion, barcodeScanner, changeLogService, calendar, camera, fileOpener, fileSelectorService, fileUploader, device, geoLocation, localDBDataPullService, localDBManagementService, mediaCapture, processManagementService, securityService, networkService, vibrateService, locationAccuracyService, diagnosticService);
        }
        static { this.ɵfac = function VariablesModule_Factory(t) { return new (t || VariablesModule)(i0__namespace.ɵɵinject(i1__namespace.App), i0__namespace.ɵɵinject(i2__namespace.AppVersion), i0__namespace.ɵɵinject(i3__namespace.BarcodeScanner), i0__namespace.ɵɵinject(i4__namespace.ChangeLogService), i0__namespace.ɵɵinject(i5__namespace.Calendar), i0__namespace.ɵɵinject(i6__namespace.Camera), i0__namespace.ɵɵinject(i7__namespace.DeviceFileOpenerService), i0__namespace.ɵɵinject(i8__namespace.FileSelectorService), i0__namespace.ɵɵinject(i7__namespace.DeviceFileUploadService), i0__namespace.ɵɵinject(i9__namespace.Device), i0__namespace.ɵɵinject(i10__namespace.Geolocation), i0__namespace.ɵɵinject(i4__namespace.LocalDBDataPullService), i0__namespace.ɵɵinject(i4__namespace.LocalDBManagementService), i0__namespace.ɵɵinject(i11__namespace.MediaCapture), i0__namespace.ɵɵinject(i8__namespace.ProcessManagementService), i0__namespace.ɵɵinject(i12__namespace.SecurityService), i0__namespace.ɵɵinject(i7__namespace.NetworkService), i0__namespace.ɵɵinject(i13__namespace.Vibration), i0__namespace.ɵɵinject(i14__namespace.LocationAccuracy), i0__namespace.ɵɵinject(i15__namespace.Diagnostic)); }; }
        static { this.ɵmod = /*@__PURE__*/ i0__namespace.ɵɵdefineNgModule({ type: VariablesModule }); }
        static { this.ɵinj = /*@__PURE__*/ i0__namespace.ɵɵdefineInjector({ imports: [i4.OfflineModule] }); }
    }
    (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(VariablesModule, [{
            type: i0.NgModule,
            args: [{
                    imports: [
                        i4.OfflineModule
                    ],
                    declarations: [],
                    providers: [
                    // add providers to mobile-runtime module.
                    ]
                }]
        }], () => [{ type: i1__namespace.App }, { type: i2__namespace.AppVersion }, { type: i3__namespace.BarcodeScanner }, { type: i4__namespace.ChangeLogService }, { type: i5__namespace.Calendar }, { type: i6__namespace.Camera }, { type: i7__namespace.DeviceFileOpenerService }, { type: i8__namespace.FileSelectorService }, { type: i7__namespace.DeviceFileUploadService }, { type: i9__namespace.Device }, { type: i10__namespace.Geolocation }, { type: i4__namespace.LocalDBDataPullService }, { type: i4__namespace.LocalDBManagementService }, { type: i11__namespace.MediaCapture }, { type: i8__namespace.ProcessManagementService }, { type: i12__namespace.SecurityService }, { type: i7__namespace.NetworkService }, { type: i13__namespace.Vibration }, { type: i14__namespace.LocationAccuracy }, { type: i15__namespace.Diagnostic }], null); })();
    (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0__namespace.ɵɵsetNgModuleScope(VariablesModule, { imports: [i4.OfflineModule] }); })();

    exports.CalendarService = CalendarService;
    exports.CameraService = CameraService;
    exports.ContactsService = ContactsService;
    exports.CurrentGeoPositionOperation = CurrentGeoPositionOperation;
    exports.DatasyncService = DatasyncService;
    exports.DeviceService = DeviceService;
    exports.FileService = FileService;
    exports.ScanService = ScanService;
    exports.VariablesModule = VariablesModule;

}));
