1 // 3.0.2-SNAPSHOT
2 var exec = require('cordova/exec'),
3 channel = require('cordova/channel'),
4 logonFired = false, // Flag to determine if logon manager is done
5 promptActive = false, // Flag to prevent prompt from displaying more than once
6 bundle = null; // Internationalization. Loaded with device ready
7
8
9 // Event channels for AppUpdate
10 var channels = {
11 'checking': channel.create('checking'),
12 'noupdate': channel.create('noupdate'),
13 'downloading': channel.create('downloading'),
14 'progress': channel.create('progress'),
15 'error': channel.create('error'),
16 'updateready': channel.create('updateready')
17 };
18
19 // Holds the dom 0 handlers that are registered for the channels
20 var domZeroHandlers = {};
21
22 // Private callback that plugin calls for events
23 var _eventHandler = function (event) {
24 if (event.type) {
25 if (event.type in channels) {
26 channels[event.type].fire(event);
27 }
28 }
29 };
30
31 /** @namespace sap */
32
33 /**
34 * Used to provide server-based updates to the application content.
35 * <br/><br/>
36 * The AppUpdate plugin updates the contents of the www folder of deployed Kapsel
37 * applications. After an application successfully does a logon to an SAP Mobile Platform 3
38 * server, the AppUpdate plugin is able to download an available update. See Uploading Hybrid Apps in user documentation
39 * for information on how to upload an update to SAP Mobile Platform 3 server.
40 * <br/><br/>
41 * After an update is completely downloaded, the application user is
42 * prompted to install the update and restart the application. They can decline
43 * if they wish.
44 * <br/><br/>
45 * Once an update is installed, the application's revision number is updated.
46 * <br/><br/>
47 * <b>Adding and Removing the AppUpdate Plugin</b><br/>
48 * The AppUpdate plugin is added and removed using the
49 * <a href="http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface">Cordova CLI</a>.<br/>
50 * <br/>
51 * To add the AppUpdate plugin to your project, use the following command:<br/>
52 * cordova plugin add <path to directory containing Kapsel plugins>\appupdate<br/>
53 * <br/>
54 * To remove the AppUpdate plugin from your project, use the following command:<br/>
55 * cordova plugin rm com.sap.mp.cordova.plugins.appupdate
56 * <br/><br/>
57 *
58 * <b>Hybrid App Revision Preference</b><br/>
59 * This is an optional preference that tells the AppUdate plugin if the local
60 * assets are uploaded to the server, and at what number. If this preference is
61 * not provided, the default revision is 0.
62 * In your config.xml file you can add the following preference:<br/>
63 * <preference name="hybridapprevision" value="1" />
64 <br/>
65 <br/>
66 * This means that the local assets in your www folder are uploaded to the server
67 * and the server is reporting revision 1 for them. This allows the application
68 * to receive a delta update when revision 2 is available instead of a full update.
69 * <br/><br/>
70 *
71 * <b>Caveats</b><br/>
72 * It is important to test that your update has valid HTML, Javascript, and CSS.
73 * Otherwise, the update could prevent the application from functioning correctly,
74 * and may no longer be updateable. You can test the updated application in a
75 * separate simulator or additional test device. You can also validate your
76 * Javascript with tools like <a href="http://www.jslint.com">JSLint</a>, or
77 * <a href="http://www.jshint.com">JSHint</a>.
78 * You can validate CSS with <a href="http://csslint.net">CSS Lint</a>.
79 * <br/><br/>
80 *
81 * @namespace
82 * @alias AppUpdate
83 * @memberof sap
84 */
85 module.exports = {
86 /**
87 * Force an update check. By default updates are done automatically during logon and resume.
88 * See events for what will be fired during this process.
89 * @example
90 * sap.AppUpdate.update();
91 */
92 update: function () {
93 // Abort if logon event has not yet fired
94 if (logonFired) {
95 sap.Logon.unlock(function (connectionInfo) {
96 //Add application ID required for REST call
97 connectionInfo.applicationId = sap.Logon.applicationId;
98
99 exec(_eventHandler, null, 'AppUpdate', 'update', [connectionInfo]);
100 });
101 }
102 },
103
104 /**
105 * Replaces the app resources with any newly downloaded resources.
106 * @example
107 * sap.AppUpdate.reloadApp();
108 */
109 reloadApp: function () {
110 exec(null, null, 'AppUpdate', 'reloadApp', []);
111 },
112
113 /**
114 * Removes all local updates and loads the original web assets bundled with the app. Call this after delete registration.
115 * Reset calls error callback if called during the update process.
116 * @example
117 * sap.Logon.core.deleteRegistration(function() {
118 * sap.AppUpdate.reset();
119 * }, function() {});
120 */
121 reset: function (successCallback, errorCallback) {
122 exec(successCallback, errorCallback, 'AppUpdate', 'reset', []);
123 },
124
125 /**
126 * Add a listener for an AppUpdate event. See events for available event names.
127 * @param {string} eventname Name of the app update event.
128 * @param {function} f Function to call when event is fired.
129 * @example
130 * sap.AppUpdate.addEventListener('checking', function(e) {
131 * console.log("Checking for update");
132 * });
133 */
134 addEventListener: function (eventname, f) {
135 if (eventname in channels) {
136 channels[eventname].subscribe(f);
137 }
138 },
139
140 /**
141 * Removes a listener for an AppUpdate event. See events for available event names.
142 * @param {string} eventname Name of the app update event.
143 * @param {function} f Function that was registered.
144 * @example
145 * // Adding the listener
146 * var listener = function(e) {
147 * console.log("Checking for update");
148 * });
149 * sap.AppUpdate.addEventListener('checking', listener);
150 *
151 * // Removing the listener
152 * sap.AppUpdate.removeEventListener('checking', listener);
153 */
154 removeEventListener: function (eventname, f) {
155 if (eventname in channels) {
156 channels[eventname].unsubscribe(f);
157 }
158 }
159
160 /**
161 * Event fired when AppUpdate is checking for an update.
162 *
163 * @event sap.AppUpdate#checking
164 * @type {object}
165 * @property {string} type - The name of the event. Value will be checking.
166 * @example
167 * sap.AppUpdate.addEventListener('checking', function(e) {
168 * console.log("Checking for update");
169 * });
170 */
171
172 /**
173 * Event fired when AppUpdate finds no available updates on server.
174 *
175 * @event sap.AppUpdate#noupdate
176 * @type {object}
177 * @property {string} type - The name of the event. Value will be noupdate.
178 * @example
179 * sap.AppUpdate.addEventListener('noupdate', function(e) {
180 * console.log("No update");
181 * });
182 */
183
184 /**
185 * Event fired when AppUpdate has found an update and is starting the download.
186 *
187 * @event sap.AppUpdate#downloading
188 * @type {object}
189 * @property {string} type - The name of the event. Value will be downloading.
190 * @example
191 * sap.AppUpdate.addEventListener('downloading', function(e) {
192 * console.log("Downloading update");
193 * });
194 */
195
196
197 /**
198 * Event fired when AppUpdate has made progress downloading the update.
199 *
200 * @since 3.0.2
201 * @event sap.AppUpdate#progress
202 * @type {object}
203 * @property {string} type - The name of the event. Value will be progress.
204 * @property {boolean} lengthComputable - Specifies whether or not the total size is known.
205 * @property {int} loaded - The number of bytes transferred so far.
206 * @property {int} total - The total number of bytes of content that will be transferred. If total size is unknown, this value is zero.
207 * @example
208 * sap.AppUpdate.addEventListener('progress', function(e) {
209 * if (e.lengthComputable) {
210 * var percent = Math.round(e.loaded / e.total * 100);
211 * console.log("Progress " + percent);
212 * }
213 * });
214 */
215
216 /**
217 * Event fired when AppUpdate encounters an error while checking for an update or downloading an update.
218 * The status code and status message are provided with this event.
219 *
220 * @event sap.AppUpdate#error
221 * @type {object}
222 * @property {string} type - The name of the event. Value will be error.
223 * @property {int} statusCode - The http error code.
224 * @property {string} statusMessage - The http status message.
225 * @example
226 * sap.AppUpdate.addEventListener('error', function(e) {
227 * console.log("Error downloading update. statusCode: " + e.statusCode + " statusMessage: " + e.statusMessage);
228 * });
229 */
230
231 /**
232 * Event fired when AppUpdate has a newly downloaded update available.
233 * A default handler is already added to sap.AppUpdate.onupdateready that will ask the user to reload the app.
234 * When handling this event you should call sap.AppUpdate.reloadApp() to apply the downloaded update.
235 *
236 * @event sap.AppUpdate#updateready
237 * @type {object}
238 * @property {string} type - The name of the event. Value will be updateready.
239 * @property {int} revision - The revision that was downloaded.
240 * @example
241 *
242 * // This will listen for updateready event.
243 * // Note: Use sap.AppUpdate.onupdateready if you want to override the default handler.
244 * sap.AppUpdate.addEventListener('updateready', function(e) {
245 * console.log("Update ready");
246 * });
247 *
248 * // Override default handler so that we automatically load the update
249 * // without first prompting the user for permission,
250 * sap.AppUpdate.onupdateready = function(e) {
251 * // No notification just reload
252 * console.log("Apply application update...");
253 * sap.AppUpdate.reloadApp();
254 * };
255 *
256 * // Override default handler with custom prompt to warn the user that the
257 * // application is ready to update.
258 * sap.AppUpdate.onupdateready = function() {
259 * console.log("Confirming application update…");
260 * navigator.notification.confirm('Update Available',
261 * function(buttonIndex) {
262 * if (buttonIndex === 2) {
263 * console.log("Applying application update…");
264 * sap.AppUpdate.reloadApp();
265 * }
266 * },
267 * "Update", ["Later", "Relaunch Now"]);
268 * };
269 */
270 };
271
272 // Add getter/setter for DOM0 style events
273 for (var type in channels) {
274 function defineSetGet(eventType) {
275 module.exports.__defineGetter__("on" + eventType, function () {
276 return domZeroHandlers[eventType];
277 });
278
279 module.exports.__defineSetter__("on" + eventType, function (val) {
280 // Remove current handler
281 if (domZeroHandlers[eventType]) {
282 module.exports.removeEventListener(eventType, domZeroHandlers[eventType]);
283 }
284
285 // Add new handler
286 if (val) {
287 domZeroHandlers[eventType] = val;
288 module.exports.addEventListener(eventType, domZeroHandlers[eventType]);
289 }
290 });
291 }
292
293 defineSetGet(type);
294 }
295
296 // Add default update ready implementation
297 module.exports.onupdateready = function () {
298 if (!promptActive) {
299 promptActive = true;
300
301 var onConfirm = function (buttonIndex) {
302 promptActive = false;
303 if (buttonIndex === 2) {
304 // Only reload if we are unlocked
305 sap.Logon.unlock(function (connectionInfo) {
306 //Add application ID required for REST call
307 connectionInfo.applicationId = sap.Logon.applicationId;
308
309 module.exports.reloadApp();
310 });
311 }
312 }
313
314 if (!bundle) {
315 // Load required translations
316 var i18n = require('com.sap.mp.cordova.plugins.i18n.i18n');
317 bundle = i18n.load({
318 path: "plugins/com.sap.mp.cordova.plugins.appupdate/www"
319 });
320 }
321
322 window.navigator.notification.confirm(
323 bundle.get("update_available"),
324 onConfirm,
325 bundle.get("update"), [bundle.get("later"), bundle.get("relaunch_now")]);
326 }
327 }
328
329 // When logon is ready an update check is started
330 document.addEventListener("onSapLogonSuccess", function () {
331 logonFired = true;
332 module.exports.update();
333 }, false);
334
335 document.addEventListener("onSapResumeSuccess", module.exports.update, false);
336