From 9f1730ae47be1575bdebcea4ded9944437bf4f0c Mon Sep 17 00:00:00 2001 From: Dave Johnson Date: Mon, 23 Aug 2010 17:14:08 -0700 Subject: [PATCH] Add back JSCallback to deviceready event --- example/phonegap.js | 603 +++++++++++++++++++-------- framework/assets/js/phonegap.js.base | 4 + 2 files changed, 435 insertions(+), 172 deletions(-) diff --git a/example/phonegap.js b/example/phonegap.js index 9e8c8e27..dfd6f458 100644 --- a/example/phonegap.js +++ b/example/phonegap.js @@ -15,9 +15,9 @@ PhoneGap = { }; - - - +/** + * Custom pub-sub channel that can have functions subscribed to it + */ PhoneGap.Channel = function(type) { this.type = type; @@ -27,15 +27,19 @@ PhoneGap.Channel = function(type) this.enabled = true; }; -PhoneGap.Channel.prototype.sub = function(f, c, g) -{ +/** + * Subscribes the given function to the channel. Any time that + * Channel.fire is called so too will the function. + * Optionally specify an execution context for the function + * and a guid that can be used to stop subscribing to the channel. + * Returns the guid. + */ +PhoneGap.Channel.prototype.subscribe = function(f, c, g) { // need a function to call - if (f == null) - return; + if (f == null) { return; } var func = f; - if (typeof c == "object" && f instanceof Function) - func = PhoneGap.close(c, f); + if (typeof c == "object" && f instanceof Function) { func = PhoneGap.close(c, f); } g = g || func.observer_guid || f.observer_guid || this.guid++; func.observer_guid = g; @@ -44,36 +48,40 @@ PhoneGap.Channel.prototype.sub = function(f, c, g) return g; }; -PhoneGap.Channel.prototype.sob = function(f, c) -{ +/** + * Like subscribe but the function is only called once and then it + * auto-unsubscribes itself. + */ +PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { var g = null; var _this = this; var m = function() { f.apply(c || null, arguments); - _this.dub(g); + _this.unsubscribe(g); } if (this.fired) { - if (typeof c == "object" && f instanceof Function) - f = PhoneGap.close(c, f); + if (typeof c == "object" && f instanceof Function) { f = PhoneGap.close(c, f); } f.apply(this, this.fireArgs); } else { - g = this.sub(m); + g = this.subscribe(m); } return g; }; -PhoneGap.Channel.prototype.dub = function(g) -{ - if (g instanceof Function) - g = g.observer_guid; +/** + * Unsubscribes the function with the given guid from the channel. + */ +PhoneGap.Channel.prototype.unsubscribe = function(g) { + if (g instanceof Function) { g = g.observer_guid; } this.handlers[g] = null; delete this.handlers[g]; }; -PhoneGap.Channel.prototype.fire = function(e) -{ - if (this.enabled) - { +/** + * Calls all functions subscribed to this channel. + */ +PhoneGap.Channel.prototype.fire = function(e) { + if (this.enabled) { var fail = false; for (var item in this.handlers) { var handler = this.handlers[item]; @@ -89,20 +97,22 @@ PhoneGap.Channel.prototype.fire = function(e) return true; }; -PhoneGap.Channel.merge = function(h, e) { - var i = e.length; +/** + * Calls the provided function only after all of the channels specified + * have been fired. + */ +PhoneGap.Channel.join = function(h, c) { + var i = c.length; var f = function() { if (!(--i)) h(); } for (var j=0; j frequency + 10 sec + var timeout = Accel.getTimeout(); + if (timeout < (frequency + 10000)) { + Accel.setTimeout(frequency + 10000); // set to frequency + 10 sec + } + + var id = PhoneGap.createUUID(); + Accel.start(); + + // Start watch timer + navigator.accelerometer.timers[id] = setInterval(function() { + var status = Accel.getStatus(); + + // If accelerometer is running + if (status == Accelerometer.RUNNING) { + try { + var accel = new Acceleration(Accel.getX(), Accel.getY(), Accel.getZ()); + successCallback(accel); + } catch (e) { + console.log("Accelerometer Error in successCallback: " + e); + } + } + + // If accelerometer had error + else if (status != Accelerometer.STARTING) { + console.log("Accelerometer Error: "+ Accelerometer.ERROR_MSG[status]); + try { + navigator.accelerometer.clearWatch(id); + if (errorCallback) { + errorCallback(status); + } + } catch (e) { + console.log("Accelerometer Error in errorCallback: " + e); + } + } + }, (frequency ? frequency : 1)); + + return id; } /** * Clears the specified accelerometer watch. - * @param {String} watchId The ID of the watch returned from #watchAcceleration. + * + * @param {String} id The id of the watch returned from #watchAcceleration. */ -Accelerometer.prototype.clearWatch = function(watchId) { - Accel.stop(watchId); -} +Accelerometer.prototype.clearWatch = function(id) { -Accelerometer.prototype.epicFail = function(watchId, message) { - accelWatcher[key].fail(); + // Stop javascript timer & remove from timer list + if (id && navigator.accelerometer.timers[id]) { + clearInterval(navigator.accelerometer.timers[id]); + delete navigator.accelerometer.timers[id]; + } } PhoneGap.addConstructor(function() { @@ -368,90 +558,169 @@ function Compass() { /** * The last known Compass position. */ - this.lastHeading = null; - this.lastError = null; - this.callbacks = { - onHeadingChanged: [], - onError: [] - }; + this.lastHeading = null; + + /** + * List of compass watch timers + */ + this.timers = {}; }; +Compass.STOPPED = 0; +Compass.STARTING = 1; +Compass.RUNNING = 2; +Compass.ERROR_FAILED_TO_START = 3; +Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"]; + /** * Asynchronously aquires the current heading. - * @param {Function} successCallback The function to call when the heading - * data is available - * @param {Function} errorCallback The function to call when there is an error - * getting the heading data. - * @param {PositionOptions} options The options for getting the heading data - * such as timeout. + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. + * @param {PositionOptions} options The options for getting the heading data such as timeout. */ Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) { - if (this.lastHeading == null) { - CompassHook.start(); - } - else - if (typeof successCallback == "function") { - successCallback(this.lastHeading); - } -}; + + // successCallback required + if (typeof successCallback != "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + // Get current compass status + var status = CompassHook.getStatus(); + + // If running, then call successCallback + if (status == Compass.RUNNING) { + try { + var heading = CompassHook.getHeading(); + successCallback(heading); + } catch (e) { + console.log("Compass Error in successCallback: " + e); + } + } + + // If not running, then start it + else { + CompassHook.start(); + + // Wait until started + var timer = setInterval(function() { + var status = CompassHook.getStatus(); + if (status != Compass.STARTING) { + clearInterval(timer); + + // If compass is running + if (status == Compass.RUNNING) { + try { + var heading = CompassHook.getHeading(); + successCallback(heading); + } catch (e) { + console.log("Compass Error in successCallback: " + e); + } + } + + // If compass error + else { + console.log("Compass Error: "+ Compass.ERROR_MSG[status]); + try { + if (errorCallback) { + errorCallback(status); + } + } catch (e) { + console.log("Compass Error in errorCallback: " + e); + } + } + } + }, 10); + } +} /** * Asynchronously aquires the heading repeatedly at a given interval. - * @param {Function} successCallback The function to call each time the heading - * data is available - * @param {Function} errorCallback The function to call when there is an error - * getting the heading data. - * @param {HeadingOptions} options The options for getting the heading data - * such as timeout and the frequency of the watch. + * + * @param {Function} successCallback The function to call each time the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. + * @param {HeadingOptions} options The options for getting the heading data such as timeout and the frequency of the watch. + * @return String The watch id that must be passed to #clearWatch to stop watching. */ Compass.prototype.watchHeading= function(successCallback, errorCallback, options) { - // Invoke the appropriate callback with a new Position object every time the implementation - // determines that the position of the hosting device has changed. - - this.getCurrentHeading(successCallback, errorCallback, options); - var frequency = 100; - if (typeof(options) == 'object' && options.frequency) - frequency = options.frequency; - var self = this; - return setInterval(function() { - self.getCurrentHeading(successCallback, errorCallback, options); - }, frequency); -}; + // Default interval (100 msec) + var frequency = (options != undefined) ? options.frequency : 100; + + // successCallback required + if (typeof successCallback != "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + // Make sure compass timeout > frequency + 10 sec + var timeout = CompassHook.getTimeout(); + if (timeout < (frequency + 10000)) { + CompassHook.setTimeout(frequency + 10000); // set to frequency + 10 sec + } + + var id = PhoneGap.createUUID(); + CompassHook.start(); + + // Start watch timer + navigator.compass.timers[id] = setInterval(function() { + var status = CompassHook.getStatus(); + + // If compass is running + if (status == Compass.RUNNING) { + try { + var heading = CompassHook.getHeading(); + successCallback(heading); + } catch (e) { + console.log("Compass Error in successCallback: " + e); + } + } + + // If compass had error + else if (status != Compass.STARTING) { + console.log("Compass Error: "+ Compass.ERROR_MSG[status]); + try { + navigator.compass.clearWatch(id); + if (errorCallback) { + errorCallback(status); + } + } catch (e) { + console.log("Compass Error in errorCallback: " + e); + } + } + }, (frequency ? frequency : 1)); + + return id; +} /** * Clears the specified heading watch. - * @param {String} watchId The ID of the watch returned from #watchHeading. + * + * @param {String} id The ID of the watch returned from #watchHeading. */ -Compass.prototype.clearWatch = function(watchId) { - clearInterval(watchId); -}; +Compass.prototype.clearWatch = function(id) { - -/** - * Called by the geolocation framework when the current heading is found. - * @param {HeadingOptions} position The current heading. - */ -Compass.prototype.setHeading = function(heading) { - this.lastHeading = heading; - for (var i = 0; i < this.callbacks.onHeadingChanged.length; i++) { - var f = this.callbacks.onHeadingChanged.shift(); - f(heading); + // Stop javascript timer & remove from timer list + if (id && navigator.compass.timers[id]) { + clearInterval(navigator.compass.timers[id]); + delete navigator.compass.timers[id]; } -}; - -/** - * Called by the geolocation framework when an error occurs while looking up the current position. - * @param {String} message The text of the error message. - */ -Compass.prototype.setError = function(message) { - this.lastError = message; - for (var i = 0; i < this.callbacks.onError.length; i++) { - var f = this.callbacks.onError.shift(); - f(message); - } -}; +} PhoneGap.addConstructor(function() { if (typeof navigator.compass == "undefined") navigator.compass = new Compass(); @@ -628,17 +897,7 @@ Device.prototype.exitApp = function() PhoneGap.addConstructor(function() { navigator.device = window.device = new Device(); -});// Intercept calls to document.addEventListener and watch for deviceready -PhoneGap._document_addEventListener = document.addEventListener; - -document.addEventListener = function(evt, handler, capture) { - if (evt.toLowerCase() == 'deviceready') { - PhoneGap.onDeviceReady.sob(handler); - } else { - PhoneGap._document_addEventListener.call(document, evt, handler); - } -}; - +}); PhoneGap.addConstructor(function() { if (typeof navigator.fileMgr == "undefined") navigator.fileMgr = new FileMgr();}); diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base index 15d17d7e..d5c484ac 100755 --- a/framework/assets/js/phonegap.js.base +++ b/framework/assets/js/phonegap.js.base @@ -160,6 +160,10 @@ if (_nativeReady) { PhoneGap.onNativeReady.fire(); } */ PhoneGap.onDeviceReady = new PhoneGap.Channel(); +PhoneGap.onDeviceReady.subscribeOnce(function() { + PhoneGap.JSCallback(); +}); + PhoneGap.Channel.join(function() { PhoneGap.onDeviceReady.fire(); }, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]);