/// module openflow { // "use strict"; interface IHashTable { [key: string]: T; } declare var cordova: any; declare var device: any; declare var diagnostic: any; type QueuedMessageCallback = (msg: any) => any; export class QueuedMessage { constructor(message: any, cb: QueuedMessageCallback) { this.id = message.id; this.message = message; this.cb = cb; } public cb: QueuedMessageCallback; public id: string; public message: any; } export class WebSocketClient { private _socketObject: ReconnectingWebSocket = null; public domain: string = null; public allow_personal_nodered: boolean = false; public allow_user_registration: boolean = false; public namespace: string = null; public nodered_domain_schema: string = null; private _url: string = null; private static instance: WebSocketClient = null; private _receiveQueue: SocketMessage[] = []; private _sendQueue: SocketMessage[] = []; public user: TokenUser = null; public jwt: string = null; public device: any = null; public usingCordova: boolean = false; public scanCount: number = 0; public oneSignalId: string = null; public location: any; static $inject = ["$rootScope", "$location", "$window"]; public messageQueue: IHashTable = {}; constructor(public $rootScope: ng.IRootScopeService, public $location, public $window: any) { try { var path = this.$location.absUrl().split('#')[0]; this.usingCordova = (path.indexOf("/android/") > -1 || path.indexOf("/ios/") > -1); } catch (error) { } this.init(); } onbackbutton() { console.log("Handle the onbackbutton event"); } onPause() { console.log("Handle the pause event"); } onResume() { console.log("Handle the resume event"); } onMenuKeyDown() { console.log("Handle the menubutton event"); } getids(oneSignal: any): Promise { return new Promise(async (resolve, reject) => { oneSignal.getIds(function (ids) { console.log("oneSignal.getIds: " + ids.userId); resolve(ids.userId); }); }); } getLocation(): Promise { return new Promise(async (resolve, reject) => { var onSuccess = function (position) { var result = { 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.coords.timestamp } console.log('Latitude: ' + result.latitude + '\n' + 'Longitude: ' + result.longitude + '\n' + 'Altitude: ' + result.altitude + '\n' + 'Accuracy: ' + result.accuracy + '\n' + 'Altitude Accuracy: ' + result.altitudeAccuracy + '\n' + 'Heading: ' + result.heading + '\n' + 'Speed: ' + result.speed + '\n' + 'Timestamp: ' + result.timestamp + '\n'); resolve(result); }; // onError Callback receives a PositionError object // function onError(error) { reject(error); console.error('code: ' + error.code + '\n' + 'message: ' + error.message + '\n'); } var options = { enableHighAccuracy: true, timeout: 2000, maximumAge: 0 }; try { if (navigator && navigator.geolocation) { console.log("getCurrentPosition"); navigator.geolocation.getCurrentPosition(onSuccess, onError, options); } else { console.log("geolocation not installed"); reject(new Error("geolocation not installed!")); } } catch (error) { reject(error); } }); } isLocationAvailable(): Promise { return new Promise(async (resolve, reject) => { cordova.plugins.diagnostic.isLocationAvailable((isAvailable) => { resolve(isAvailable); }, (error) => { reject(error); }); }); } isLocationAuthorized(): Promise { return new Promise(async (resolve, reject) => { cordova.plugins.diagnostic.isLocationAuthorized((authorized) => { resolve(authorized); }, (error) => { reject(error); }); }); } requestLocationAuthorization(): Promise { return new Promise(async (resolve, reject) => { cordova.plugins.diagnostic.requestLocationAuthorization(() => { resolve(); }, (error) => { reject(error); }); }); } scanForCordova() { if (this.usingCordova == true) return; try { if (cordova !== undefined) { console.log("Found cordova"); this.usingCordova = true; document.addEventListener("deviceready", async () => { console.log("deviceready"); if ((window as any).plugins) { var oneSignal = (window as any).plugins.OneSignal; if (oneSignal) { try { console.debug("window.plugins.OneSignal exists"); const sender_id = "906036108091"; // google_project_number const oneSignalAppId = "cfdefd08-d4ad-4593-8173-4ba4ebf4d67a"; // onesignal_app_id var iosSettings = {}; iosSettings["kOSSettingsKeyAutoPrompt"] = true; iosSettings["kOSSettingsKeyInAppLaunchURL"] = true; console.debug("oneSignal.startInit"); oneSignal.startInit(oneSignalAppId, sender_id). iOSSettings(iosSettings). inFocusDisplaying(oneSignal.OSInFocusDisplayOption.Notification). handleNotificationOpened(this.notificationOpenedCallback). handleNotificationReceived(this.notificationReceivedCallback). endInit(); this.oneSignalId = await this.getids(oneSignal); console.log("oneSignalId: " + this.oneSignalId); } catch (error) { console.error(error); } } else { console.log("Missing oneSignal plugin"); } } try { if (device) { console.debug("device exists"); this.device = device; } else { console.debug("device does NOT exists"); } } catch (error) { console.error(error); } document.addEventListener("pause", this.onPause, false); document.addEventListener("resume", this.onResume, false); document.addEventListener("menubutton", this.onMenuKeyDown, false); document.addEventListener("backbutton", this.onbackbutton, false); if (cordova.plugins && cordova.plugins.diagnostic) { console.debug("Check if authorized for location"); var isAuthorized = await this.isLocationAuthorized(); if (!isAuthorized) { console.debug("Not authorized for location is not , request authorization"); await this.requestLocationAuthorization(); } var isAvailable = await this.isLocationAvailable(); if (!isAvailable) { console.debug("Location is not available"); } else { console.debug("Location is available, get current location"); this.location = await this.getLocation(); } } else { console.debug("diagnostic is missing"); } this.$rootScope.$broadcast("cordovadetected"); }); } else { console.debug("cordova not definded"); if (this.scanCount < (5 * 4)) { this.scanCount++; setTimeout(this.scanForCordova, 200); } } } catch (error) { console.debug("Failed locating cordova. " + error); } } init() { this.getJSON("/config", async (error: any, data: any) => { var parser = document.createElement('a'); parser.href = data.wshost; parser.protocol; // => "http:" parser.hostname; // => "example.com" parser.port; // => "3000" parser.pathname; // => "/pathname/" parser.search; // => "?search=test" parser.hash; // => "#hash" parser.host; // => "example.com:3000" if (location.protocol == 'https:' && parser.protocol == "ws:") { data.wshost = "wss://" + parser.hostname; console.log("new wshost: " + data.wshost); if (parser.port != "80") { data.wshost = "wss://" + parser.hostname + parser.port; } } if (location.protocol == 'http:' && parser.protocol == "wss:") { data.wshost = "ws://" + parser.hostname; if (parser.port != "443") { data.wshost = "ws://" + parser.hostname + parser.port; } } console.debug("WebSocketClient::onopen: connecting to " + data.wshost); this.domain = data.domain; this.allow_personal_nodered = data.allow_personal_nodered; this.allow_user_registration = data.allow_user_registration; this.namespace = data.namespace; this.nodered_domain_schema = data.nodered_domain_schema; this._socketObject = new ReconnectingWebSocket(data.wshost); this._socketObject.onopen = (this.onopen).bind(this); this._socketObject.onmessage = (this.onmessage).bind(this); this._socketObject.onclose = (this.onclose).bind(this); this._socketObject.onerror = (this.onerror).bind(this); WebSocketClient.instance = this; }); } notificationOpenedCallback(state) { console.debug("notificationOpenedCallback"); console.debug(JSON.stringify(state)); //console.log(state); } notificationReceivedCallback(state) { console.debug("notificationReceivedCallback"); console.debug(JSON.stringify(state)); if (state.isAppInFocus) { window.location.href = state.payload.additionalData.URL; return; } // {"isAppInFocus":true,"shown":true,"androidNotificationId":-1616162934,"displayType":0,"payload":{"notificationID":"aee1f7a2-2108-489d-a401-86dba6a1ad99","body":"Android 2019-06-02T20:58:25.876Z","additionalData":{"URL":"https://aiotdev-frontend.openrpa.dk/#/Alert/5cf25ad801530ae6396519b8"},"launchURL":"https://aiotdev-frontend.openrpa.dk/#/Alert/5cf25ad801530ae6396519b8","lockScreenVisibility":1,"fromProjectNumber":"906036108091","priority":0,"rawPayload":"{\"google.delivered_priority\":\"normal\",\"google.sent_time\":1559509106669,\"google.ttl\":259200,\"google.original_priority\":\"normal\",\"custom\":\"{\\\"a\\\":{\\\"URL\\\":\\\"https:\\\\\\/\\\\\\/aiotdev-frontend.openrpa.dk\\\\\\/#\\\\\\/Alert\\\\\\/5cf25ad801530ae6396519b8\\\"},\\\"u\\\":\\\"https:\\\\\\/\\\\\\/aiotdev-frontend.openrpa.dk\\\\\\/#\\\\\\/Alert\\\\\\/5cf25ad801530ae6396519b8\\\",\\\"i\\\":\\\"aee1f7a2-2108-489d-a401-86dba6a1ad99\\\"}\",\"from\":\"906036108091\",\"alert\":\"Android 2019-06-02T20:58:25.876Z\",\"google.message_id\":\"0:1559509106674108%6c875f80f9fd7ecd\",\"notificationId\":-1616162934}"}} //console.log(state); } public connect(): void { } getJSON(url: string, callback: any): void { var xhr: XMLHttpRequest = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.responseType = "json"; xhr.onload = function (): void { var status: number = xhr.status; if (status === 200) { callback(null, xhr.response); } else { callback(status, xhr.response); } }; xhr.send(); } onSignedin(callback) { if (this.user !== null) { callback(this.user); return; } var cleanup = this.$rootScope.$on('signin', (event, data) => { if (event && data) { } cleanup(); callback(this.user); }); } timeout(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } private onopen(evt: Event) { // console.debug("WebSocketClient::onopen: connected"); this.$rootScope.$broadcast("socketopen"); setTimeout(this.scanForCordova.bind(this), 200); } private onclose(evt: CloseEvent): void { var me: WebSocketClient = WebSocketClient.instance; } private onerror(evt: ErrorEvent): void { var me: WebSocketClient = WebSocketClient.instance; } private onmessage(evt: MessageEvent): void { var me: WebSocketClient = WebSocketClient.instance; let msg: SocketMessage = SocketMessage.fromjson(evt.data); me._receiveQueue.push(msg); me.ProcessQueue.bind(me)(); } public async Send(message: Message): Promise { return new Promise(async (resolve, reject) => { this._Send(message, ((msg) => { if (msg.error !== null && msg.error !== undefined) { console.error(message); return reject(msg.error); } resolve(msg); }).bind(this)); }); } private _Send(message: Message, cb: QueuedMessageCallback): void { var messages: string[] = this.chunkString(message.data, 500); if (messages === null || messages === undefined || messages.length === 0) { var singlemessage: SocketMessage = SocketMessage.frommessage(message, "", 1, 0); if (message.replyto === null || message.replyto === undefined) { this.messageQueue[singlemessage.id] = new QueuedMessage(singlemessage, cb); } this._sendQueue.push(singlemessage); return; } if (message.id === null || message.id === undefined) { message.id = Math.random().toString(36).substr(2, 9); } for (let i: number = 0; i < messages.length; i++) { var _message: SocketMessage = SocketMessage.frommessage(message, messages[i], messages.length, i); this._sendQueue.push(_message); } if (message.replyto === null || message.replyto === undefined) { this.messageQueue[message.id] = new QueuedMessage(message, cb); } this.ProcessQueue(); } public chunkString(str: string, length: number): string[] { if (str === null || str === undefined) { return []; } // tslint:disable-next-line: quotemark return str.match(new RegExp('.{1,' + length + '}', 'g')); } private ProcessQueue(): void { let ids: string[] = []; this._receiveQueue.forEach(msg => { if (ids.indexOf(msg.id) === -1) { ids.push(msg.id); } }); ids.forEach(id => { var msgs: SocketMessage[] = this._receiveQueue.filter(function (msg: SocketMessage): boolean { return msg.id === id; }); msgs.sort((a, b) => a.index - b.index); var first: SocketMessage = msgs[0]; if (first.count === msgs.length) { if (msgs.length === 1) { var singleresult: Message = Message.frommessage(first, first.data); this._receiveQueue = this._receiveQueue.filter(function (msg: SocketMessage): boolean { return msg.id !== id; }); singleresult.Process(this); } else { var buffer: string = ""; msgs.forEach(msg => { if (msg.data !== null && msg.data !== undefined) { buffer += msg.data; } }); var result: Message = Message.frommessage(first, buffer); this._receiveQueue = this._receiveQueue.filter(function (msg: SocketMessage): boolean { return msg.id !== id; }); result.Process(this); } this._receiveQueue = this._receiveQueue.filter(function (msg: SocketMessage): boolean { return msg.id !== id; }); } }); if (this._socketObject !== null && this._socketObject.readyState !== 1) { this.connect(); setTimeout(() => { this.ProcessQueue(); }, 1500); return; } this._sendQueue.forEach(msg => { try { if (this._socketObject !== null && this._socketObject.readyState === 1) { let id: string = msg.id; this._socketObject.send(JSON.stringify(msg)); this._sendQueue = this._sendQueue.filter(function (msg: SocketMessage): boolean { return msg.id !== id; }); } } catch (error) { console.error(error); } }); } } }