/*
* (c) 2021 BlackBerry Limited. All rights reserved.
*/
;
(function() {
var cordovaExec = require('cordova/exec'),
isOpenFirstChannelCalled = false;
/**
* @class GDPushChannelResponse
* @classdesc This class encapsulates the response returned from the GDPush class.
*
* @param {string} json The input data (formatted as JSON text) used to construct the
* response object.
*
* @property {string} channelID The unique ID for the push channel that generated this response.
*
* @property {string} responseType This value is used to distinguish what action triggered this response.
* Valid values are:
* <ul>
* <li>open - The channel was just successfully opened.</li>
* <li>message - A new message was received from the server. The responseData property will be
* populated with the data from the server.</li>
* <li>error - A channel error occurred. The responseData may or may not be populated with a
* description of the error.</li>
* <li>close - The channel connection was closed.</li>
* <li>pingFail - Ping Failure is an optional feature of the Push Channel framework. The application
* server registers for ping after receiving the Push Channel token from the client. If an
* application server registers for ping, then the server will be periodically checked ("pinged") by
* the BlackBerry Dynamics Network Operating Center (NOC). If the application server does not respond to a
* ping, then the NOC notifies the client.</li>
* </ul>
*
* @property {string} responseData This field will be populated with data from the server if the
* response contained data what was intended to be processed by the client.
*
* @return {GDPushChannelResponse}
*
* @example
* // This object is used by GDPushChannel.parseChannelResponse() method and is not used directly
*/
var GDPushChannelResponse = function(json) {
var channelID = null,
responseType = null,
responseData = null;
try {
var obj = JSON.parse(unescape(json));
channelID = obj.channelID;
responseType = obj.responseType;
/*
* The response could have been JSON text, which we might need to revert to it's
* string representation.
*/
try {
if (typeof obj.responseData === 'Object') {
responseData = JSON.stringify(obj.responseData);
} else {
responseData = obj.responseData;
}
} catch (e) {
responseData = obj.responseData;
}
} catch (e) {
responseType = "error";
}
Object.defineProperties(this, {
'channelID': {
get: function() {
return channelID;
}
},
'responseType': {
get: function() {
return responseType;
}
},
'responseData': {
get: function() {
return responseData;
}
},
'toString': {
value: function() {
return '[object GDPushChannelResponse]';
}
}
})
};
Object.preventExtensions(GDPushChannelResponse);
/**
* @class GDPushChannel
* @classdesc This class encapsulates the GD Push Channel object. The Push Channel framework is a
* BlackBerry Dynamics (GD) feature used to receive notifications from an application server. Note that
* the GD Push Channel feature is not part of the native iOS notification feature set.
* <br/>
* Push Channels are established from the client end, then used by the server when needed. The sequence
* of events is as follows:<br/>
* <ol>
* <li>The client sets an event handler for Push Channel notifications</li>
* <li>The client application requests a Push Channel token from the BlackBerry Dynamics proxy infrastructure</li>
* <li>The client application sends the token to its server using, for example, a socket or HTTP request</li>
* <li>The client can now wait for a Push Channel notification</li>
* </ol>
* The BlackBerry Dynamics platform keeps data communications between client and server alive while the client is
* waiting for a Push Channel notification. This is achieved by sending "heartbeat" messages at an interval
* that is dynamically optimized for battery and network performance.<br/>
* <br/>
*
* @property {function} onChannelResponse This function is the callback handler that is called
* whenever a response is returned from the channel connection. This function should check
* the value of the responseType returned and determine the required action to take. If the
* responseType = "open", then the channelID returned in the response should be used to reference this
* channel in subsequent calls over this connection (see <a href="#open">GDPushChannel.open</a>). NOTE: This
* function is required to be a non-null value.
*/
var GDPushChannel = function(responseCallback) {
var channelResponseCallback = null;
if (typeof responseCallback === 'function') {
channelResponseCallback = responseCallback;
}
Object.defineProperties(this, {
'onChannelResponse': {
get: function() {
return channelResponseCallback;
},
set: function(callback) {
channelResponseCallback = callback;
},
configurable: false
},
'toString': {
value: function() {
return '[object GDPushChannel]';
}
}
});
};
Object.defineProperty(GDPushChannel, 'toString', {
value: function() {
return 'function GDPushChannel() { [native code] }';
}
});
Object.preventExtensions(GDPushChannel);
/**
* @function GDPushChannel#open
*
* @description Call this function to open the Push Channel. This function can only be called when
* the channel is not opened. This function creates a request for a Push Channel to be sent to the Good
* Dynamics proxy infrastructure Network Operating Center (NOC). The NOC will create the channel, and
* issue a Push Channel token, which can then be used to identify the channel. The application code that
* handles the Intent must initiate sending of the Push Channel token to the application server, out of
* band. The application server will then be able to use the token to address Push Channel messages back
* to the application, via the BlackBerry Dynamics proxy infrastructure.
* See the Push Channel Back-End API of Blackberry Dynamics Docs
* (<a href="https://developer.blackberry.com/devzone/files/blackberry-dynamics/android/enumcom_1_1good_1_1gd_1_1push_1_1_push_channel_event_type.html#a7bb49e261987a2c4aee1067014700544">
* Push Channel Open & Push Channel Back-End API of Blackberry Dynamics</a>).
*
* @return {GDPushChannelResponse} A push channel response object in JSON format. The result should be
* parsed and saved as a GDPushChannelResponse object in the callback handler. If the channel was
* opened then the response object will be initialized with a channelID property that can be used to
* reference this channel connection. Additionally, the response will also contain a token that uniquely
* identifies the device associated with this push channel. Since this is an asynchronous call, the
* response will be returned via the onChannelResponse callback.
*
* Note that it can take some time for establishing push connection during opening first Push Channel.
* Please, wait until "onChannelResponse" callback is called with "GDPushChannelResponse" response object.
*
* @example
* function myPushChannel() {
* var savedChannelID,
* pushChannelToken;
* var channel = new window.plugins.GDPushChannel(pushChannelResponse);
* channel.open();
*
* channel.isAvailable(function(result) {
* console.log("GDPushChannel status: ", result);
* });
*
* //-- GDPushChannelResponse
* function pushChannelResponse(response) {
* try {
* var channelResponse = channel.parseChannelResponse(response);
*
* console.log("Got response channelID: " + channelResponse.channelID);
* console.log("Got response responseType: " + channelResponse.responseType);
* console.log("Got response responseData: " + channelResponse.responseData);
* switch (channelResponse.responseType) {
* case "open":
* savedChannelID = channelResponse.channelID;
* pushChannelToken = channelResponse.responseData;
* console.log("Channel connection opened with ID :" + savedChannelID);
* break;
*
* // Send application server the savedChannelID (token) here at following format:
* // POST https://gdmdc.good.com//GNP1.0?method=notify HTTP/1.1
* // Host: gdmdc.good.com:443
* // Content-Type: text/plain; charset=utf-8
* // Content-length: 30
* // X-Good-GNP-Token: pushChannelToken
* // For more details see Push Channel Back-End API:
* // https://developer.blackberry.com/devzone/files/blackberry-dynamics/ios/_g_n_p.html
*
* case "message":
* // handle pushed message from the server
* channel.close(channelResponse.channelID);
* break;
* case "error":
* console.log("Received an error status from the channel connection.");
* break;
* case "close":
* console.log("Channel connection closed successfully.");
* break;
* case "pingFail":
* break;
* default:
* break;
* }
* } catch (e) {
* throw new Error("Invalid response object sent to channel response callback handler.");
* }
* };
*
* };
*/
GDPushChannel.prototype.open = function() {
// Make sure that the response callback handler is not null.
if (this.onChannelResponse === null ||
typeof this.onChannelResponse === 'undefined') {
throw new Error("onChannelResponse callback handler for GDPushChannel object is null.");
}
var messageForNotAvailableConnection = 'secure push connection is not established',
connetionAttempts = 10,
self = this;
cordovaExec(function(response) {
if (response.indexOf(messageForNotAvailableConnection) === -1) {
self.onChannelResponse(response);
} else {
retryOpenConnection();
}
}, null, "GDPush", "open", null);
function retryOpenConnection() {
var connectionInterval = setInterval(function() {
cordovaExec(function(response) {
if (response.indexOf(messageForNotAvailableConnection) === -1 || connetionAttempts === 0) {
clearInterval(connectionInterval);
self.onChannelResponse(response);
return;
}
connetionAttempts--;
}, null, "GDPush", "open", null);
}, 1000);
}
};
/**
* @function GDPushChannel#close
*
* @description Call this function to initiate permanent disconnection of the Push Channel. This function
* creates a request for Push Channel termination to be sent to the BlackBerry Dynamics proxy infrastructure Network
* Operating Center (NOC). The NOC will delete the channel, and invalidate the Push Channel token that was
* issued when the channel was initially opened.
*
* @param {string} channelID The unique ID for the push channel to close.
*
* @example
* See the example above (it is added to GDPushChannel.open() method).
*/
GDPushChannel.prototype.close = function(channelID) {
if (channelID === null || typeof channelID === 'undefined') {
throw new Error("Null channelID passed to GDPushChannel.close.");
}
var parms = [channelID];
cordovaExec(this.onChannelResponse, this.onChannelResponse, "GDPush", "close", parms);
};
/**
* @function GDPushChannel#parseChannelResponse
*
* @description Call this function to transform the push channel response text into a
* GDPushChannelResponse object.
*
* @param {string} responseText A string representing the push channel response text.
*
* @return {GDPushChannelResponse} The push channel response object.
*
* @example
* See the example <a href="./GDPushChannel.html">here</a>
*/
GDPushChannel.prototype.parseChannelResponse = function(responseText) {
return new GDPushChannelResponse(responseText);
};
/**
* @function GDPushChannel#isAvailable
*
* @description This function returns the current status of the Push Channel connection.
*
* @param {function} responseCallback Callback function to invoke when the function returns.
* A single result string will be passed as the input to the callback function: "true" or "false".
*
* @return {string} "true" or "false".
*
* @example
* See the example <a href="./GDPushChannel.html">here</a>
*/
GDPushChannel.prototype.isAvailable = function(responseCallback) {
cordovaExec(responseCallback, responseCallback, "GDPush", "isConnected", null);
};
// ***** END: MODULE METHOD DEFINITIONS - GDPushChannel *****
function hideJSFunctionsImplementationInConsoleForObject(prototypeObject) {
for (protoFunction in prototypeObject) {
if (prototypeObject.hasOwnProperty(protoFunction)) {
// Checking, if function property 'name' is configurable
// (for old browser, which has pre-ES2015 implementation(Android 5.0) function name property isn't configurable)
var objProtoProperty = prototypeObject[protoFunction],
isFuncNamePropConfigurable = Object.getOwnPropertyDescriptor(objProtoProperty, 'name').configurable;
if (isFuncNamePropConfigurable) {
Object.defineProperty(prototypeObject[protoFunction],
'name', {
value: protoFunction,
configurable: false
}
);
}
Object.defineProperty(prototypeObject[protoFunction],
'toString', {
value: function() {
var funcName = this.name || protoFunction;
return 'function ' + funcName + '() { [native code] }';
},
writable: false,
configurable: false
});
}
}
Object.preventExtensions(prototypeObject);
}
// hide functions implementation in web inspector
hideJSFunctionsImplementationInConsoleForObject(GDPushChannel.prototype);
// Install the plugin.
module.exports = GDPushChannel;
}()); // End the Module Definition.
//************************************************************************************************