/* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * The order of events during page load and PhoneGap startup is as follows: * * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. * window.onload Body onload event. * onNativeReady Internal event that indicates the PhoneGap native side is ready. * onPhoneGapInit Internal event that kicks off creation of all PhoneGap JavaScript objects (runs constructors). * onPhoneGapReady Internal event fired when all PhoneGap JavaScript objects have been created * onPhoneGapInfoReady Internal event fired when device properties are available * onDeviceReady User event fired to indicate that PhoneGap is ready * onResume User event fired to indicate a start/resume lifecycle event * * The only PhoneGap events that user code should register for are: * onDeviceReady * onResume * * Listeners can be registered as: * document.addEventListener("deviceready", myDeviceReadyListener, false); * document.addEventListener("resume", myResumeListener, false); */ if (typeof(DeviceInfo) != 'object') DeviceInfo = {}; /** * This represents the PhoneGap API itself, and provides a global namespace for accessing * information about the state of PhoneGap. * @class */ var PhoneGap = { queue: { ready: true, commands: [], timer: null } }; /** * Custom pub-sub channel that can have functions subscribed to it */ PhoneGap.Channel = function(type) { this.type = type; this.handlers = {}; this.guid = 0; this.fired = false; this.enabled = true; }; /** * 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; } var func = 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; f.observer_guid = g; this.handlers[g] = func; return g; }; /** * 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.unsubscribe(g); } if (this.fired) { if (typeof c == "object" && f instanceof Function) { f = PhoneGap.close(c, f); } f.apply(this, this.fireArgs); } else { g = this.subscribe(m); } return g; }; /** * 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]; }; /** * 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]; if (handler instanceof Function) { var rv = (handler.apply(this, arguments)==false); fail = fail || rv; } } this.fired = true; this.fireArgs = arguments; return !fail; } return true; }; /** * 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(); } var len = i; for (var j=0; j * * @param name The plugin name * @param obj The plugin object */ PhoneGap.addPlugin = function(name, obj) { if (!window.plugins[name]) { window.plugins[name] = obj; } else { console.log("Error: Plugin "+name+" already exists."); } } /** * onDOMContentLoaded channel is fired when the DOM content * of the page has been parsed. */ PhoneGap.onDOMContentLoaded = new PhoneGap.Channel('onDOMContentLoaded'); /** * onNativeReady channel is fired when the PhoneGap native code * has been initialized. */ PhoneGap.onNativeReady = new PhoneGap.Channel('onNativeReady'); /** * onPhoneGapInit channel is fired when the web page is fully loaded and * PhoneGap native code has been initialized. */ PhoneGap.onPhoneGapInit = new PhoneGap.Channel('onPhoneGapInit'); /** * onPhoneGapReady channel is fired when the JS PhoneGap objects have been created. */ PhoneGap.onPhoneGapReady = new PhoneGap.Channel('onPhoneGapReady'); /** * onPhoneGapInfoReady channel is fired when the PhoneGap device properties * has been set. */ PhoneGap.onPhoneGapInfoReady = new PhoneGap.Channel('onPhoneGapInfoReady'); /** * onResume channel is fired when the PhoneGap native code * resumes. */ PhoneGap.onResume = new PhoneGap.Channel('onResume'); /** * onPause channel is fired when the PhoneGap native code * pauses. */ PhoneGap.onPause = new PhoneGap.Channel('onPause'); // _nativeReady is global variable that the native side can set // to signify that the native code is ready. It is a global since // it may be called before any PhoneGap JS is ready. if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); } /** * onDeviceReady is fired only after all PhoneGap objects are created and * the device properties are set. */ PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady'); // Array of channels that must fire before "deviceready" is fired PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady]; // Hashtable of user defined channels that must also fire before "deviceready" is fired PhoneGap.deviceReadyChannelsMap = {}; /** * Indicate that a feature needs to be initialized before it is ready to be used. * This holds up PhoneGap's "deviceready" event until the feature has been initialized * and PhoneGap.initComplete(feature) is called. * * @param feature {String} The unique feature name */ PhoneGap.waitForInitialization = function(feature) { if (feature) { var channel = new PhoneGap.Channel(feature); PhoneGap.deviceReadyChannelsMap[feature] = channel; PhoneGap.deviceReadyChannelsArray.push(channel); } }; /** * Indicate that initialization code has completed and the feature is ready to be used. * * @param feature {String} The unique feature name */ PhoneGap.initializationComplete = function(feature) { var channel = PhoneGap.deviceReadyChannelsMap[feature]; if (channel) { channel.fire(); } }; /** * Create all PhoneGap objects once page has fully loaded and native side is ready. */ PhoneGap.Channel.join(function() { // Start listening for XHR callbacks setTimeout(function() { if (CallbackServer.usePolling()) { PhoneGap.JSCallbackPolling(); } else { PhoneGap.JSCallback(); } }, 1); // Run PhoneGap constructors PhoneGap.onPhoneGapInit.fire(); // Fire event to notify that all objects are created PhoneGap.onPhoneGapReady.fire(); PhoneGap.Channel.join(function() { // Turn off app loading dialog navigator.notification.activityStop(); PhoneGap.onDeviceReady.fire(); // Fire the onresume event, since first one happens before JavaScript is loaded PhoneGap.onResume.fire(); }, PhoneGap.deviceReadyChannelsArray); }, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]); /** * Fire onDeviceReady event once all constructors have run and PhoneGap info has been * received from native side. */ /* PhoneGap.Channel.join(function() { // Turn off app loading dialog navigator.notification.activityStop(); PhoneGap.onDeviceReady.fire(); // Fire the onresume event, since first one happens before JavaScript is loaded PhoneGap.onResume.fire(); }, [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady]); */ // Listen for DOMContentLoaded and notify our channel subscribers document.addEventListener('DOMContentLoaded', function() { PhoneGap.onDOMContentLoaded.fire(); }, false); // Intercept calls to document.addEventListener and watch for deviceready PhoneGap.m_document_addEventListener = document.addEventListener; document.addEventListener = function(evt, handler, capture) { var e = evt.toLowerCase(); if (e == 'deviceready') { PhoneGap.onDeviceReady.subscribeOnce(handler); } else if (e == 'resume') { PhoneGap.onResume.subscribe(handler); if (PhoneGap.onDeviceReady.fired) { PhoneGap.onResume.fire(); } } else if (e == 'pause') { PhoneGap.onPause.subscribe(handler); } else { PhoneGap.m_document_addEventListener.call(document, evt, handler, capture); } }; /** * If JSON not included, use our own stringify. (Android 1.6) * The restriction on ours is that it must be an array of simple types. * * @param args * @return */ PhoneGap.stringify = function(args) { if (typeof JSON == "undefined") { var s = "["; for (var i=0; i 0) { s = s + ","; } var type = typeof args[i]; if ((type == "number") || (type == "boolean")) { s = s + args[i]; } else if (args[i] instanceof Array) { s = s + "[" + args[i] + "]"; } else if (args[i] instanceof Object) { var start = true; s = s + '{'; for (var name in args[i]) { if (args[i][name] != null) { if (!start) { s = s + ','; } s = s + '"' + name + '":'; var nameType = typeof args[i][name]; if ((nameType == "number") || (nameType == "boolean")) { s = s + args[i][name]; } else if ((typeof args[i][name]) == 'function') { // don't copy the functions s = s + '""'; } else if (args[i][name] instanceof Object) { s = s + this.stringify(args[i][name]); } else { s = s + '"' + args[i][name] + '"'; } start=false; } } s = s + '}'; } else { var a = args[i].replace(/\\/g, '\\\\'); a = a.replace(/"/g, '\\"'); s = s + '"' + a + '"'; } } s = s + "]"; return s; } else { return JSON.stringify(args); } }; /** * Does a deep clone of the object. * * @param obj * @return */ PhoneGap.clone = function(obj) { if(!obj) { return obj; } if(obj instanceof Array){ var retVal = new Array(); for(var i = 0; i < obj.length; ++i){ retVal.push(PhoneGap.clone(obj[i])); } return retVal; } if (obj instanceof Function) { return obj; } if(!(obj instanceof Object)){ return obj; } if (obj instanceof Date) { return obj; } retVal = new Object(); for(i in obj){ if(!(i in retVal) || retVal[i] != obj[i]) { retVal[i] = PhoneGap.clone(obj[i]); } } return retVal; }; PhoneGap.callbackId = 0; PhoneGap.callbacks = {}; PhoneGap.callbackStatus = { NO_RESULT: 0, OK: 1, CLASS_NOT_FOUND_EXCEPTION: 2, ILLEGAL_ACCESS_EXCEPTION: 3, INSTANTIATION_EXCEPTION: 4, MALFORMED_URL_EXCEPTION: 5, IO_EXCEPTION: 6, INVALID_ACTION: 7, JSON_EXCEPTION: 8, ERROR: 9 }; /** * Execute a PhoneGap command. It is up to the native side whether this action is synch or async. * The native side can return: * Synchronous: PluginResult object as a JSON string * Asynchrounous: Empty string "" * If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError, * depending upon the result of the action. * * @param {Function} success The success callback * @param {Function} fail The fail callback * @param {String} service The name of the service to use * @param {String} action Action to be run in PhoneGap * @param {String[]} [args] Zero or more arguments to pass to the method */ PhoneGap.exec = function(success, fail, service, action, args) { try { var callbackId = service + PhoneGap.callbackId++; if (success || fail) { PhoneGap.callbacks[callbackId] = {success:success, fail:fail}; } // Note: Device returns string, but for some reason emulator returns object - so convert to string. var r = ""+PluginManager.exec(service, action, callbackId, this.stringify(args), true); // If a result was returned if (r.length > 0) { eval("var v="+r+";"); // If status is OK, then return value back to caller if (v.status == PhoneGap.callbackStatus.OK) { // If there is a success callback, then call it now with returned value if (success) { try { success(v.message); } catch (e) { console.log("Error in success callback: "+callbackId+" = "+e); } // Clear callback if not expecting any more results if (!v.keepCallback) { delete PhoneGap.callbacks[callbackId]; } } return v.message; } // If no result else if (v.status == PhoneGap.callbackStatus.NO_RESULT) { // Clear callback if not expecting any more results if (!v.keepCallback) { delete PhoneGap.callbacks[callbackId]; } } // If error, then display error else { console.log("Error: Status="+r.status+" Message="+v.message); // If there is a fail callback, then call it now with returned value if (fail) { try { fail(v.message); } catch (e) { console.log("Error in error callback: "+callbackId+" = "+e); } // Clear callback if not expecting any more results if (!v.keepCallback) { delete PhoneGap.callbacks[callbackId]; } } return null; } } } catch (e) { console.log("Error: "+e); } }; /** * Called by native code when returning successful result from an action. * * @param callbackId * @param args */ PhoneGap.callbackSuccess = function(callbackId, args) { if (PhoneGap.callbacks[callbackId]) { // If result is to be sent to callback if (args.status == PhoneGap.callbackStatus.OK) { try { if (PhoneGap.callbacks[callbackId].success) { PhoneGap.callbacks[callbackId].success(args.message); } } catch (e) { console.log("Error in success callback: "+callbackId+" = "+e); } } // Clear callback if not expecting any more results if (!args.keepCallback) { delete PhoneGap.callbacks[callbackId]; } } }; /** * Called by native code when returning error result from an action. * * @param callbackId * @param args */ PhoneGap.callbackError = function(callbackId, args) { if (PhoneGap.callbacks[callbackId]) { try { if (PhoneGap.callbacks[callbackId].fail) { PhoneGap.callbacks[callbackId].fail(args.message); } } catch (e) { console.log("Error in error callback: "+callbackId+" = "+e); } // Clear callback if not expecting any more results if (!args.keepCallback) { delete PhoneGap.callbacks[callbackId]; } } }; /** * Internal function used to dispatch the request to PhoneGap. It processes the * command queue and executes the next command on the list. If one of the * arguments is a JavaScript object, it will be passed on the QueryString of the * url, which will be turned into a dictionary on the other end. * @private */ // TODO: Is this used? PhoneGap.run_command = function() { if (!PhoneGap.available || !PhoneGap.queue.ready) return; PhoneGap.queue.ready = false; var args = PhoneGap.queue.commands.shift(); if (PhoneGap.queue.commands.length == 0) { clearInterval(PhoneGap.queue.timer); PhoneGap.queue.timer = null; } var uri = []; var dict = null; for (var i = 1; i < args.length; i++) { var arg = args[i]; if (arg == undefined || arg == null) arg = ''; if (typeof(arg) == 'object') dict = arg; else uri.push(encodeURIComponent(arg)); } var url = "gap://" + args[0] + "/" + uri.join("/"); if (dict != null) { var query_args = []; for (var name in dict) { if (typeof(name) != 'string') continue; query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name])); } if (query_args.length > 0) url += "?" + query_args.join("&"); } document.location = url; }; PhoneGap.JSCallbackPort = null; PhoneGap.JSCallbackToken = null; /** * This is only for Android. * * Internal function that uses XHR to call into PhoneGap Java code and retrieve * any JavaScript code that needs to be run. This is used for callbacks from * Java to JavaScript. */ PhoneGap.JSCallback = function() { var xmlhttp = new XMLHttpRequest(); // Callback function when XMLHttpRequest is ready xmlhttp.onreadystatechange=function(){ if(xmlhttp.readyState == 4){ // If callback has JavaScript statement to execute if (xmlhttp.status == 200) { var msg = xmlhttp.responseText; setTimeout(function() { try { var t = eval(msg); } catch (e) { // If we're getting an error here, seeing the message will help in debugging console.log("JSCallback: Message from Server: " + msg); console.log("JSCallback Error: "+e); } }, 1); setTimeout(PhoneGap.JSCallback, 1); } // If callback ping (used to keep XHR request from timing out) else if (xmlhttp.status == 404) { setTimeout(PhoneGap.JSCallback, 10); } // If security error else if (xmlhttp.status == 403) { console.log("JSCallback Error: Invalid token. Stopping callbacks."); } // If server is stopping else if (xmlhttp.status == 503) { console.log("JSCallback Error: Service unavailable. Stopping callbacks."); } // If request wasn't GET else if (xmlhttp.status == 400) { console.log("JSCallback Error: Bad request. Stopping callbacks."); } // If error, restart callback server else { console.log("JSCallback Error: Request failed."); CallbackServer.restartServer(); PhoneGap.JSCallbackPort = null; PhoneGap.JSCallbackToken = null; setTimeout(PhoneGap.JSCallback, 100); } } } if (PhoneGap.JSCallbackPort == null) { PhoneGap.JSCallbackPort = CallbackServer.getPort(); } if (PhoneGap.JSCallbackToken == null) { PhoneGap.JSCallbackToken = CallbackServer.getToken(); } xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true); xmlhttp.send(); }; /** * The polling period to use with JSCallbackPolling. * This can be changed by the application. The default is 50ms. */ PhoneGap.JSCallbackPollingPeriod = 50; /** * This is only for Android. * * Internal function that uses polling to call into PhoneGap Java code and retrieve * any JavaScript code that needs to be run. This is used for callbacks from * Java to JavaScript. */ PhoneGap.JSCallbackPolling = function() { var msg = CallbackServer.getJavascript(); if (msg) { setTimeout(function() { try { var t = eval(""+msg); } catch (e) { console.log("JSCallbackPolling: Message from Server: " + msg); console.log("JSCallbackPolling Error: "+e); } }, 1); setTimeout(PhoneGap.JSCallbackPolling, 1); } else { setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod); } }; /** * Create a UUID * * @return */ PhoneGap.createUUID = function() { return PhoneGap.UUIDcreatePart(4) + '-' + PhoneGap.UUIDcreatePart(2) + '-' + PhoneGap.UUIDcreatePart(2) + '-' + PhoneGap.UUIDcreatePart(2) + '-' + PhoneGap.UUIDcreatePart(6); }; PhoneGap.UUIDcreatePart = function(length) { var uuidpart = ""; for (var i=0; i frequency + 10 sec PhoneGap.exec( function(timeout) { if (timeout < (frequency + 10000)) { PhoneGap.exec(null, null, "Accelerometer", "setTimeout", [frequency + 10000]); } }, function(e) { }, "Accelerometer", "getTimeout", []); // Start watch timer var id = PhoneGap.createUUID(); navigator.accelerometer.timers[id] = setInterval(function() { PhoneGap.exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []); }, (frequency ? frequency : 1)); return id; }; /** * Clears the specified accelerometer watch. * * @param {String} id The id of the watch returned from #watchAcceleration. */ Accelerometer.prototype.clearWatch = function(id) { // Stop javascript timer & remove from timer list if (id && navigator.accelerometer.timers[id] != undefined) { clearInterval(navigator.accelerometer.timers[id]); delete navigator.accelerometer.timers[id]; } }; PhoneGap.addConstructor(function() { if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer(); }); /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * Constructor */ function App() { } /** * Clear the resource cache. */ App.prototype.clearCache = function() { PhoneGap.exec(null, null, "App", "clearCache", []); }; /** * Load the url into the webview. * * @param url The URL to load * @param props Properties that can be passed in to the activity: * wait: int => wait msec before loading URL * loadingDialog: "Title,Message" => display a native loading dialog * hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs. * loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser. * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error * errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html"); * keepRunning: boolean => enable app to keep running in background * * Example: * App app = new App(); * app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); */ App.prototype.loadUrl = function(url, props) { PhoneGap.exec(null, null, "App", "loadUrl", [url, props]); }; /** * Cancel loadUrl that is waiting to be loaded. */ App.prototype.cancelLoadUrl = function() { PhoneGap.exec(null, null, "App", "cancelLoadUrl", []); }; /** * Clear web history in this web view. * Instead of BACK button loading the previous web page, it will exit the app. */ App.prototype.clearHistory = function() { PhoneGap.exec(null, null, "App", "clearHistory", []); }; /** * Add a class that implements a service. * * @param serviceType * @param className */ App.prototype.addService = function(serviceType, className) { PhoneGap.exec(null, null, "App", "addService", [serviceType, className]); }; /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * This class provides access to the device camera. * * @constructor */ Camera = function() { this.successCallback = null; this.errorCallback = null; this.options = null; }; /** * Format of image that returned from getPicture. * * Example: navigator.camera.getPicture(success, fail, * { quality: 80, * destinationType: Camera.DestinationType.DATA_URL, * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) */ Camera.DestinationType = { DATA_URL: 0, // Return base64 encoded string FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android) }; Camera.prototype.DestinationType = Camera.DestinationType; /** * Source to getPicture from. * * Example: navigator.camera.getPicture(success, fail, * { quality: 80, * destinationType: Camera.DestinationType.DATA_URL, * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) */ Camera.PictureSourceType = { PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) CAMERA : 1, // Take picture from camera SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) }; Camera.prototype.PictureSourceType = Camera.PictureSourceType; /** * Gets a picture from source defined by "options.sourceType", and returns the * image as defined by the "options.destinationType" option. * The defaults are sourceType=CAMERA and destinationType=DATA_URL. * * @param {Function} successCallback * @param {Function} errorCallback * @param {Object} options */ Camera.prototype.getPicture = function(successCallback, errorCallback, options) { // successCallback required if (typeof successCallback != "function") { console.log("Camera Error: successCallback is not a function"); return; } // errorCallback optional if (errorCallback && (typeof errorCallback != "function")) { console.log("Camera Error: errorCallback is not a function"); return; } this.options = options; var quality = 80; if (options.quality) { quality = this.options.quality; } var destinationType = Camera.DestinationType.DATA_URL; if (this.options.destinationType) { destinationType = this.options.destinationType; } var sourceType = Camera.PictureSourceType.CAMERA; if (typeof this.options.sourceType == "number") { sourceType = this.options.sourceType; } PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType]); }; PhoneGap.addConstructor(function() { if (typeof navigator.camera == "undefined") navigator.camera = new Camera(); }); /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * This class provides access to device Compass data. * @constructor */ function Compass() { /** * The last known Compass position. */ this.lastHeading = null; /** * List of compass watch timers */ this.timers = {}; }; 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. (OPTIONAL) * @param {PositionOptions} options The options for getting the heading data such as timeout. (OPTIONAL) */ Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) { // 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 heading PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []); }; /** * 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. (OPTIONAL) * @param {HeadingOptions} options The options for getting the heading data such as timeout and the frequency of the watch. (OPTIONAL) * @return String The watch id that must be passed to #clearWatch to stop watching. */ Compass.prototype.watchHeading= function(successCallback, errorCallback, options) { // 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 PhoneGap.exec( function(timeout) { if (timeout < (frequency + 10000)) { PhoneGap.exec(null, null, "Compass", "setTimeout", [frequency + 10000]); } }, function(e) { }, "Compass", "getTimeout", []); // Start watch timer to get headings var id = PhoneGap.createUUID(); navigator.compass.timers[id] = setInterval( function() { PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []); }, (frequency ? frequency : 1)); return id; }; /** * Clears the specified heading watch. * * @param {String} id The ID of the watch returned from #watchHeading. */ Compass.prototype.clearWatch = function(id) { // Stop javascript timer & remove from timer list if (id && navigator.compass.timers[id]) { clearInterval(navigator.compass.timers[id]); delete navigator.compass.timers[id]; } }; PhoneGap.addConstructor(function() { if (typeof navigator.compass == "undefined") navigator.compass = new Compass(); }); /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * Contains information about a single contact. * @param {DOMString} id unique identifier * @param {DOMString} displayName * @param {ContactName} name * @param {DOMString} nickname * @param {ContactField[]} phoneNumbers array of phone numbers * @param {ContactField[]} emails array of email addresses * @param {ContactAddress[]} addresses array of addresses * @param {ContactField[]} ims instant messaging user ids * @param {ContactOrganization[]} organizations * @param {DOMString} revision date contact was last updated * @param {DOMString} birthday contact's birthday * @param {DOMString} gender contact's gender * @param {DOMString} note user notes about contact * @param {ContactField[]} photos * @param {ContactField[]} categories * @param {ContactField[]} urls contact's web sites * @param {DOMString} timezone the contacts time zone */ var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, addresses, ims, organizations, revision, birthday, gender, note, photos, categories, urls, timezone) { this.id = id || null; this.rawId = null; this.displayName = displayName || null; this.name = name || null; // ContactName this.nickname = nickname || null; this.phoneNumbers = phoneNumbers || null; // ContactField[] this.emails = emails || null; // ContactField[] this.addresses = addresses || null; // ContactAddress[] this.ims = ims || null; // ContactField[] this.organizations = organizations || null; // ContactOrganization[] this.revision = revision || null; this.birthday = birthday || null; this.gender = gender || null; this.note = note || null; this.photos = photos || null; // ContactField[] this.categories = categories || null; // ContactField[] this.urls = urls || null; // ContactField[] this.timezone = timezone || null; }; /** * Removes contact from device storage. * @param successCB success callback * @param errorCB error callback */ Contact.prototype.remove = function(successCB, errorCB) { if (this.id == null) { var errorObj = new ContactError(); errorObj.code = ContactError.NOT_FOUND_ERROR; errorCB(errorObj); } else { PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]); } }; /** * Creates a deep copy of this Contact. * With the contact ID set to null. * @return copy of this Contact */ Contact.prototype.clone = function() { var clonedContact = PhoneGap.clone(this); clonedContact.id = null; clonedContact.rawId = null; // Loop through and clear out any id's in phones, emails, etc. if (clonedContact.phoneNumbers) { for (i=0; i][;base64], * * @param file The name of the file */ FileReader.prototype.readAsDataURL = function(file) { this.fileName = file; // LOADING state this.readyState = FileReader.LOADING; // If loadstart callback if (typeof this.onloadstart == "function") { var evt = File._createEvent("loadstart", this); this.onloadstart(evt); } var me = this; // Read file navigator.fileMgr.readAsDataURL(file, // Success callback function(r) { // If DONE (cancelled), then don't do anything if (me.readyState == FileReader.DONE) { return; } // Save result me.result = r; // If onload callback if (typeof me.onload == "function") { var evt = File._createEvent("load", me); me.onload(evt); } // DONE state me.readyState = FileReader.DONE; // If onloadend callback if (typeof me.onloadend == "function") { var evt = File._createEvent("loadend", me); me.onloadend(evt); } }, // Error callback function(e) { // If DONE (cancelled), then don't do anything if (me.readyState == FileReader.DONE) { return; } // Save error me.error = e; // If onerror callback if (typeof me.onerror == "function") { var evt = File._createEvent("error", me); me.onerror(evt); } // DONE state me.readyState = FileReader.DONE; // If onloadend callback if (typeof me.onloadend == "function") { var evt = File._createEvent("loadend", me); me.onloadend(evt); } } ); }; /** * Read file and return data as a binary data. * * @param file The name of the file */ FileReader.prototype.readAsBinaryString = function(file) { // TODO - Can't return binary data to browser. this.fileName = file; }; /** * Read file and return data as a binary data. * * @param file The name of the file */ FileReader.prototype.readAsArrayBuffer = function(file) { // TODO - Can't return binary data to browser. this.fileName = file; }; //----------------------------------------------------------------------------- // File Writer //----------------------------------------------------------------------------- /** * This class writes to the mobile device file system. * * For Android: * The root directory is the root of the file system. * To write to the SD card, the file name is "sdcard/my_file.txt" * * @param filePath the file to write to * @param append if true write to the end of the file, otherwise overwrite the file */ function FileWriter(filePath, append) { this.fileName = ""; this.length = 0; if (filePath) { var f = navigator.fileMgr.getFileProperties(filePath); this.fileName = f.name; this.length = f.size; } // default is to write at the beginning of the file this.position = (append !== true) ? 0 : this.length; this.readyState = 0; // EMPTY this.result = null; // Error this.error = null; // Event handlers this.onwritestart = null; // When writing starts this.onprogress = null; // While writing the file, and reporting partial file data this.onwrite = null; // When the write has successfully completed. this.onwriteend = null; // When the request has completed (either in success or failure). this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. this.onerror = null; // When the write has failed (see errors). }; // States FileWriter.INIT = 0; FileWriter.WRITING = 1; FileWriter.DONE = 2; /** * Abort writing file. */ FileWriter.prototype.abort = function() { // check for invalid state if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { throw FileError.INVALID_STATE_ERR; } // set error var error = new FileError(); error.code = error.ABORT_ERR; this.error = error; // If error callback if (typeof this.onerror == "function") { var evt = File._createEvent("error", this); this.onerror(evt); } // If abort callback if (typeof this.onabort == "function") { var evt = File._createEvent("abort", this); this.onabort(evt); } this.readyState = FileWriter.DONE; // If write end callback if (typeof this.onwriteend == "function") { var evt = File._createEvent("writeend", this); this.onwriteend(evt); } }; /** * @Deprecated: use write instead * * @param file to write the data to * @param text to be written * @param bAppend if true write to end of file, otherwise overwrite the file */ FileWriter.prototype.writeAsText = function(file, text, bAppend) { // Throw an exception if we are already writing a file if (this.readyState == FileWriter.WRITING) { throw FileError.INVALID_STATE_ERR; } if (bAppend != true) { bAppend = false; // for null values } this.fileName = file; // WRITING state this.readyState = FileWriter.WRITING; var me = this; // If onwritestart callback if (typeof me.onwritestart == "function") { var evt = File._createEvent("writestart", me); me.onwritestart(evt); } // Write file navigator.fileMgr.writeAsText(file, text, bAppend, // Success callback function(r) { // If DONE (cancelled), then don't do anything if (me.readyState == FileWriter.DONE) { return; } // Save result me.result = r; // If onwrite callback if (typeof me.onwrite == "function") { var evt = File._createEvent("write", me); me.onwrite(evt); } // DONE state me.readyState = FileWriter.DONE; // If onwriteend callback if (typeof me.onwriteend == "function") { var evt = File._createEvent("writeend", me); me.onwriteend(evt); } }, // Error callback function(e) { // If DONE (cancelled), then don't do anything if (me.readyState == FileWriter.DONE) { return; } // Save error me.error = e; // If onerror callback if (typeof me.onerror == "function") { var evt = File._createEvent("error", me); me.onerror(evt); } // DONE state me.readyState = FileWriter.DONE; // If onwriteend callback if (typeof me.onwriteend == "function") { var evt = File._createEvent("writeend", me); me.onwriteend(evt); } } ); }; /** * Writes data to the file * * @param text to be written */ FileWriter.prototype.write = function(text) { // Throw an exception if we are already writing a file if (this.readyState == FileWriter.WRITING) { throw FileError.INVALID_STATE_ERR; } // WRITING state this.readyState = FileWriter.WRITING; var me = this; // If onwritestart callback if (typeof me.onwritestart == "function") { var evt = File._createEvent("writestart", me); me.onwritestart(evt); } // Write file navigator.fileMgr.write(this.fileName, text, this.position, // Success callback function(r) { // If DONE (cancelled), then don't do anything if (me.readyState == FileWriter.DONE) { return; } // So if the user wants to keep appending to the file me.length = Math.max(me.length, me.position + r); // position always increases by bytes written because file would be extended me.position += r; // If onwrite callback if (typeof me.onwrite == "function") { var evt = File._createEvent("write", me); me.onwrite(evt); } // DONE state me.readyState = FileWriter.DONE; // If onwriteend callback if (typeof me.onwriteend == "function") { var evt = File._createEvent("writeend", me); me.onwriteend(evt); } }, // Error callback function(e) { // If DONE (cancelled), then don't do anything if (me.readyState == FileWriter.DONE) { return; } // Save error me.error = e; // If onerror callback if (typeof me.onerror == "function") { var evt = File._createEvent("error", me); me.onerror(evt); } // DONE state me.readyState = FileWriter.DONE; // If onwriteend callback if (typeof me.onwriteend == "function") { var evt = File._createEvent("writeend", me); me.onwriteend(evt); } } ); }; /** * Moves the file pointer to the location specified. * * If the offset is a negative number the position of the file * pointer is rewound. If the offset is greater than the file * size the position is set to the end of the file. * * @param offset is the location to move the file pointer to. */ FileWriter.prototype.seek = function(offset) { // Throw an exception if we are already writing a file if (this.readyState === FileWriter.WRITING) { throw FileError.INVALID_STATE_ERR; } if (!offset) { return; } // See back from end of file. if (offset < 0) { this.position = Math.max(offset + this.length, 0); } // Offset is bigger then file size so set position // to the end of the file. else if (offset > this.length) { this.position = this.length; } // Offset is between 0 and file size so set the position // to start writing. else { this.position = offset; } }; /** * Truncates the file to the size specified. * * @param size to chop the file at. */ FileWriter.prototype.truncate = function(size) { // Throw an exception if we are already writing a file if (this.readyState == FileWriter.WRITING) { throw FileError.INVALID_STATE_ERR; } // WRITING state this.readyState = FileWriter.WRITING; var me = this; // If onwritestart callback if (typeof me.onwritestart == "function") { var evt = File._createEvent("writestart", me); me.onwritestart(evt); } // Write file navigator.fileMgr.truncate(this.fileName, size, // Success callback function(r) { // If DONE (cancelled), then don't do anything if (me.readyState == FileWriter.DONE) { return; } // Update the length of the file me.length = r; me.position = Math.min(me.position, r);; // If onwrite callback if (typeof me.onwrite == "function") { var evt = File._createEvent("write", me); me.onwrite(evt); } // DONE state me.readyState = FileWriter.DONE; // If onwriteend callback if (typeof me.onwriteend == "function") { var evt = File._createEvent("writeend", me); me.onwriteend(evt); } }, // Error callback function(e) { // If DONE (cancelled), then don't do anything if (me.readyState == FileWriter.DONE) { return; } // Save error me.error = e; // If onerror callback if (typeof me.onerror == "function") { var evt = File._createEvent("error", me); me.onerror(evt); } // DONE state me.readyState = FileWriter.DONE; // If onwriteend callback if (typeof me.onwriteend == "function") { var evt = File._createEvent("writeend", me); me.onwriteend(evt); } } ); }; /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * FileTransfer uploads a file to a remote server. */ function FileTransfer() {}; /** * FileUploadResult */ function FileUploadResult() { this.bytesSent = 0; this.responseCode = null; this.response = null; }; /** * FileTransferError */ function FileTransferError() { this.code = null; }; FileTransferError.FILE_NOT_FOUND_ERR = 1; FileTransferError.INVALID_URL_ERR = 2; FileTransferError.CONNECTION_ERR = 3; /** * Given an absolute file path, uploads a file on the device to a remote server * using a multipart HTTP request. * @param filePath {String} Full path of the file on the device * @param server {String} URL of the server to receive the file * @param successCallback (Function} Callback to be invoked when upload has completed * @param errorCallback {Function} Callback to be invoked upon error * @param options {FileUploadOptions} Optional parameters such as file name and mimetype */ FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) { // check for options var fileKey = null; var fileName = null; var mimeType = null; var params = null; if (options) { fileKey = options.fileKey; fileName = options.fileName; mimeType = options.mimeType; if (options.params) { params = options.params; } else { params = {}; } } PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]); }; /** * Options to customize the HTTP request used to upload files. * @param fileKey {String} Name of file request parameter. * @param fileName {String} Filename to be used by the server. Defaults to image.jpg. * @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg. * @param params {Object} Object with key: value params to send to the server. */ function FileUploadOptions(fileKey, fileName, mimeType, params) { this.fileKey = fileKey || null; this.fileName = fileName || null; this.mimeType = mimeType || null; this.params = params || null; }; /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * This class provides access to device GPS data. * @constructor */ function Geolocation() { // The last known GPS position. this.lastPosition = null; // Geolocation listeners this.listeners = {}; }; /** * Position error object * * @param code * @param message */ function PositionError(code, message) { this.code = code; this.message = message; }; PositionError.PERMISSION_DENIED = 1; PositionError.POSITION_UNAVAILABLE = 2; PositionError.TIMEOUT = 3; /** * Asynchronously aquires the current position. * * @param {Function} successCallback The function to call when the position data is available * @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL) * @param {PositionOptions} options The options for getting the position data. (OPTIONAL) */ Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) { if (navigator._geo.listeners["global"]) { console.log("Geolocation Error: Still waiting for previous getCurrentPosition() request."); try { errorCallback(new PositionError(PositionError.TIMEOUT, "Geolocation Error: Still waiting for previous getCurrentPosition() request.")); } catch (e) { } return; } var maximumAge = 10000; var enableHighAccuracy = false; var timeout = 10000; if (typeof options != "undefined") { if (typeof options.maximumAge != "undefined") { maximumAge = options.maximumAge; } if (typeof options.enableHighAccuracy != "undefined") { enableHighAccuracy = options.enableHighAccuracy; } if (typeof options.timeout != "undefined") { timeout = options.timeout; } } navigator._geo.listeners["global"] = {"success" : successCallback, "fail" : errorCallback }; PhoneGap.exec(null, null, "Geolocation", "getCurrentLocation", [enableHighAccuracy, timeout, maximumAge]); } /** * Asynchronously watches the geolocation for changes to geolocation. When a change occurs, * the successCallback is called with the new location. * * @param {Function} successCallback The function to call each time the location data is available * @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL) * @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL) * @return String The watch id that must be passed to #clearWatch to stop watching. */ Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options) { var maximumAge = 10000; var enableHighAccuracy = false; var timeout = 10000; if (typeof options != "undefined") { if (typeof options.frequency != "undefined") { maximumAge = options.frequency; } if (typeof options.maximumAge != "undefined") { maximumAge = options.maximumAge; } if (typeof options.enableHighAccuracy != "undefined") { enableHighAccuracy = options.enableHighAccuracy; } if (typeof options.timeout != "undefined") { timeout = options.timeout; } } var id = PhoneGap.createUUID(); navigator._geo.listeners[id] = {"success" : successCallback, "fail" : errorCallback }; PhoneGap.exec(null, null, "Geolocation", "start", [id, enableHighAccuracy, timeout, maximumAge]); return id; }; /* * Native callback when watch position has a new position. * PRIVATE METHOD * * @param {String} id * @param {Number} lat * @param {Number} lng * @param {Number} alt * @param {Number} altacc * @param {Number} head * @param {Number} vel * @param {Number} stamp */ Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, stamp) { var coords = new Coordinates(lat, lng, alt, altacc, head, vel); var loc = new Position(coords, stamp); try { if (lat == "undefined" || lng == "undefined") { navigator._geo.listeners[id].fail(new PositionError(PositionError.POSITION_UNAVAILABLE, "Lat/Lng are undefined.")); } else { navigator._geo.lastPosition = loc; navigator._geo.listeners[id].success(loc); } } catch (e) { console.log("Geolocation Error: Error calling success callback function."); } if (id == "global") { delete navigator._geo.listeners["global"]; } }; /** * Native callback when watch position has an error. * PRIVATE METHOD * * @param {String} id The ID of the watch * @param {Number} code The error code * @param {String} msg The error message */ Geolocation.prototype.fail = function(id, code, msg) { try { navigator._geo.listeners[id].fail(new PositionError(code, msg)); } catch (e) { console.log("Geolocation Error: Error calling error callback function."); } }; /** * Clears the specified heading watch. * * @param {String} id The ID of the watch returned from #watchPosition */ Geolocation.prototype.clearWatch = function(id) { PhoneGap.exec(null, null, "Geolocation", "stop", [id]); delete navigator._geo.listeners[id]; }; /** * Force the PhoneGap geolocation to be used instead of built-in. */ Geolocation.usingPhoneGap = false; Geolocation.usePhoneGap = function() { if (Geolocation.usingPhoneGap) { return; } Geolocation.usingPhoneGap = true; // Set built-in geolocation methods to our own implementations // (Cannot replace entire geolocation, but can replace individual methods) navigator.geolocation.setLocation = navigator._geo.setLocation; navigator.geolocation.getCurrentPosition = navigator._geo.getCurrentPosition; navigator.geolocation.watchPosition = navigator._geo.watchPosition; navigator.geolocation.clearWatch = navigator._geo.clearWatch; navigator.geolocation.start = navigator._geo.start; navigator.geolocation.stop = navigator._geo.stop; }; PhoneGap.addConstructor(function() { navigator._geo = new Geolocation(); // No native geolocation object for Android 1.x, so use PhoneGap geolocation if (typeof navigator.geolocation == 'undefined') { navigator.geolocation = navigator._geo; Geolocation.usingPhoneGap = true; } }); /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ function KeyEvent() { } KeyEvent.prototype.backTrigger = function() { var e = document.createEvent('Events'); e.initEvent('backKeyDown'); document.dispatchEvent(e); }; KeyEvent.prototype.menuTrigger = function() { var e = document.createEvent('Events'); e.initEvent('menuKeyDown'); document.dispatchEvent(e); }; KeyEvent.prototype.searchTrigger = function() { var e = document.createEvent('Events'); e.initEvent('searchKeyDown'); document.dispatchEvent(e); }; if (document.keyEvent == null || typeof document.keyEvent == 'undefined') { window.keyEvent = document.keyEvent = new KeyEvent(); } /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * List of media objects. * PRIVATE */ PhoneGap.mediaObjects = {}; /** * Object that receives native callbacks. * PRIVATE */ PhoneGap.Media = function() {}; /** * Get the media object. * PRIVATE * * @param id The media object id (string) */ PhoneGap.Media.getMediaObject = function(id) { return PhoneGap.mediaObjects[id]; }; /** * Audio has status update. * PRIVATE * * @param id The media object id (string) * @param status The status code (int) * @param msg The status message (string) */ PhoneGap.Media.onStatus = function(id, msg, value) { var media = PhoneGap.mediaObjects[id]; // If state update if (msg == Media.MEDIA_STATE) { if (value == Media.MEDIA_STOPPED) { if (media.successCallback) { media.successCallback(); } } if (media.statusCallback) { media.statusCallback(value); } } else if (msg == Media.MEDIA_DURATION) { media._duration = value; } else if (msg == Media.MEDIA_ERROR) { if (media.errorCallback) { media.errorCallback(value); } } }; /** * This class provides access to the device media, interfaces to both sound and video * * @param src The file name or url to play * @param successCallback The callback to be called when the file is done playing or recording. * successCallback() - OPTIONAL * @param errorCallback The callback to be called if there is an error. * errorCallback(int errorCode) - OPTIONAL * @param statusCallback The callback to be called when media status has changed. * statusCallback(int statusCode) - OPTIONAL * @param positionCallback The callback to be called when media position has changed. * positionCallback(long position) - OPTIONAL */ Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) { // successCallback optional if (successCallback && (typeof successCallback != "function")) { console.log("Media Error: successCallback is not a function"); return; } // errorCallback optional if (errorCallback && (typeof errorCallback != "function")) { console.log("Media Error: errorCallback is not a function"); return; } // statusCallback optional if (statusCallback && (typeof statusCallback != "function")) { console.log("Media Error: statusCallback is not a function"); return; } // statusCallback optional if (positionCallback && (typeof positionCallback != "function")) { console.log("Media Error: positionCallback is not a function"); return; } this.id = PhoneGap.createUUID(); PhoneGap.mediaObjects[this.id] = this; this.src = src; this.successCallback = successCallback; this.errorCallback = errorCallback; this.statusCallback = statusCallback; this.positionCallback = positionCallback; this._duration = -1; this._position = -1; }; // Media messages Media.MEDIA_STATE = 1; Media.MEDIA_DURATION = 2; Media.MEDIA_ERROR = 9; // Media states Media.MEDIA_NONE = 0; Media.MEDIA_STARTING = 1; Media.MEDIA_RUNNING = 2; Media.MEDIA_PAUSED = 3; Media.MEDIA_STOPPED = 4; Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; // TODO: Will MediaError be used? /** * This class contains information about any Media errors. * @constructor */ function MediaError() { this.code = null, this.message = ""; }; MediaError.MEDIA_ERR_ABORTED = 1; MediaError.MEDIA_ERR_NETWORK = 2; MediaError.MEDIA_ERR_DECODE = 3; MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; /** * Start or resume playing audio file. */ Media.prototype.play = function() { PhoneGap.exec(null, null, "Media", "startPlayingAudio", [this.id, this.src]); }; /** * Stop playing audio file. */ Media.prototype.stop = function() { return PhoneGap.exec(null, null, "Media", "stopPlayingAudio", [this.id]); }; /** * Pause playing audio file. */ Media.prototype.pause = function() { PhoneGap.exec(null, null, "Media", "pausePlayingAudio", [this.id]); }; /** * Get duration of an audio file. * The duration is only set for audio that is playing, paused or stopped. * * @return duration or -1 if not known. */ Media.prototype.getDuration = function() { return this._duration; }; /** * Get position of audio. * * @return */ Media.prototype.getCurrentPosition = function(success, fail) { PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]); }; /** * Start recording audio file. */ Media.prototype.startRecord = function() { PhoneGap.exec(null, null, "Media", "startRecordingAudio", [this.id, this.src]); }; /** * Stop recording audio file. */ Media.prototype.stopRecord = function() { PhoneGap.exec(null, null, "Media", "stopRecordingAudio", [this.id]); }; /** * Release the resources. */ Media.prototype.release = function() { PhoneGap.exec(null, null, "Media", "release", [this.id]); }; /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * This class contains information about any NetworkStatus. * @constructor */ function NetworkStatus() { //this.code = null; //this.message = ""; }; NetworkStatus.NOT_REACHABLE = 0; NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1; NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2; /** * This class provides access to device Network data (reachability). * @constructor */ function Network() { /** * The last known Network status. * { hostName: string, ipAddress: string, remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) } */ this.lastReachability = null; }; /** * Called by the geolocation framework when the reachability status has changed. * @param {Reachibility} reachability The current reachability status. */ // TODO: Callback from native code not implemented for Android Network.prototype.updateReachability = function(reachability) { this.lastReachability = reachability; }; /** * Determine if a URI is reachable over the network. * @param {Object} uri * @param {Function} callback * @param {Object} options (isIpAddress:boolean) */ Network.prototype.isReachable = function(uri, callback, options) { var isIpAddress = false; if (options && options.isIpAddress) { isIpAddress = options.isIpAddress; } PhoneGap.exec(callback, null, "Network Status", "isReachable", [uri, isIpAddress]); }; PhoneGap.addConstructor(function() { if (typeof navigator.network == "undefined") navigator.network = new Network(); }); /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * This class provides access to notifications on the device. */ function Notification() { } /** * Open a native alert dialog, with a customizable title and button text. * * @param {String} message Message to print in the body of the alert * @param {Function} completeCallback The callback that is called when user clicks on a button. * @param {String} title Title of the alert dialog (default: Alert) * @param {String} buttonLabel Label of the close button (default: OK) */ Notification.prototype.alert = function(message, completeCallback, title, buttonLabel) { var _title = (title || "Alert"); var _buttonLabel = (buttonLabel || "OK"); PhoneGap.exec(completeCallback, null, "Notification", "alert", [message,_title,_buttonLabel]); }; /** * Open a native confirm dialog, with a customizable title and button text. * The result that the user selects is returned to the result callback. * * @param {String} message Message to print in the body of the alert * @param {Function} resultCallback The callback that is called when user clicks on a button. * @param {String} title Title of the alert dialog (default: Confirm) * @param {String} buttonLabels Comma separated list of the labels of the buttons (default: 'OK,Cancel') */ Notification.prototype.confirm = function(message, resultCallback, title, buttonLabels) { var _title = (title || "Confirm"); var _buttonLabels = (buttonLabels || "OK,Cancel"); PhoneGap.exec(resultCallback, null, "Notification", "confirm", [message,_title,_buttonLabels]); }; /** * Start spinning the activity indicator on the statusbar */ Notification.prototype.activityStart = function() { PhoneGap.exec(null, null, "Notification", "activityStart", ["Busy","Please wait..."]); }; /** * Stop spinning the activity indicator on the statusbar, if it's currently spinning */ Notification.prototype.activityStop = function() { PhoneGap.exec(null, null, "Notification", "activityStop", []); }; /** * Display a progress dialog with progress bar that goes from 0 to 100. * * @param {String} title Title of the progress dialog. * @param {String} message Message to display in the dialog. */ Notification.prototype.progressStart = function(title, message) { PhoneGap.exec(null, null, "Notification", "progressStart", [title, message]); }; /** * Set the progress dialog value. * * @param {Number} value 0-100 */ Notification.prototype.progressValue = function(value) { PhoneGap.exec(null, null, "Notification", "progressValue", [value]); }; /** * Close the progress dialog. */ Notification.prototype.progressStop = function() { PhoneGap.exec(null, null, "Notification", "progressStop", []); }; /** * Causes the device to blink a status LED. * * @param {Integer} count The number of blinks. * @param {String} colour The colour of the light. */ Notification.prototype.blink = function(count, colour) { // NOT IMPLEMENTED }; /** * Causes the device to vibrate. * * @param {Integer} mills The number of milliseconds to vibrate for. */ Notification.prototype.vibrate = function(mills) { PhoneGap.exec(null, null, "Notification", "vibrate", [mills]); }; /** * Causes the device to beep. * On Android, the default notification ringtone is played "count" times. * * @param {Integer} count The number of beeps. */ Notification.prototype.beep = function(count) { PhoneGap.exec(null, null, "Notification", "beep", [count]); }; PhoneGap.addConstructor(function() { if (typeof navigator.notification == "undefined") navigator.notification = new Notification(); }); /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /** * This class contains position information. * @param {Object} lat * @param {Object} lng * @param {Object} acc * @param {Object} alt * @param {Object} altacc * @param {Object} head * @param {Object} vel * @constructor */ function Position(coords, timestamp) { this.coords = coords; this.timestamp = (timestamp != 'undefined') ? timestamp : new Date().getTime(); } function Coordinates(lat, lng, alt, acc, head, vel, altacc) { /** * The latitude of the position. */ this.latitude = lat; /** * The longitude of the position, */ this.longitude = lng; /** * The accuracy of the position. */ this.accuracy = acc; /** * The altitude of the position. */ this.altitude = alt; /** * The direction the device is moving at the position. */ this.heading = head; /** * The velocity with which the device is moving at the position. */ this.speed = vel; /** * The altitude accuracy of the position. */ this.altitudeAccuracy = (altacc != 'undefined') ? altacc : null; } /** * This class specifies the options for requesting position data. * @constructor */ function PositionOptions() { /** * Specifies the desired position accuracy. */ this.enableHighAccuracy = true; /** * The timeout after which if position data cannot be obtained the errorCallback * is called. */ this.timeout = 10000; } /** * This class contains information about any GSP errors. * @constructor */ function PositionError() { this.code = null; this.message = ""; } PositionError.UNKNOWN_ERROR = 0; PositionError.PERMISSION_DENIED = 1; PositionError.POSITION_UNAVAILABLE = 2; PositionError.TIMEOUT = 3; /* * PhoneGap is available under *either* the terms of the modified BSD license *or* the * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. * * Copyright (c) 2005-2010, Nitobi Software Inc. * Copyright (c) 2010, IBM Corporation */ /* * This is purely for the Android 1.5/1.6 HTML 5 Storage * I was hoping that Android 2.0 would deprecate this, but given the fact that * most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required */ /** * Storage object that is called by native code when performing queries. * PRIVATE METHOD */ var DroidDB = function() { this.queryQueue = {}; }; /** * Callback from native code when query is complete. * PRIVATE METHOD * * @param id Query id */ DroidDB.prototype.completeQuery = function(id, data) { var query = this.queryQueue[id]; if (query) { try { delete this.queryQueue[id]; // Get transaction var tx = query.tx; // If transaction hasn't failed // Note: We ignore all query results if previous query // in the same transaction failed. if (tx && tx.queryList[id]) { // Save query results var r = new DroidDB_Result(); r.rows.resultSet = data; r.rows.length = data.length; try { if (typeof query.successCallback == 'function') { query.successCallback(query.tx, r); } } catch (ex) { console.log("executeSql error calling user success callback: "+ex); } tx.queryComplete(id); } } catch (e) { console.log("executeSql error: "+e); } } }; /** * Callback from native code when query fails * PRIVATE METHOD * * @param reason Error message * @param id Query id */ DroidDB.prototype.fail = function(reason, id) { var query = this.queryQueue[id]; if (query) { try { delete this.queryQueue[id]; // Get transaction var tx = query.tx; // If transaction hasn't failed // Note: We ignore all query results if previous query // in the same transaction failed. if (tx && tx.queryList[id]) { tx.queryList = {}; try { if (typeof query.errorCallback == 'function') { query.errorCallback(query.tx, reason); } } catch (ex) { console.log("executeSql error calling user error callback: "+ex); } tx.queryFailed(id, reason); } } catch (e) { console.log("executeSql error: "+e); } } }; var DatabaseShell = function() { }; /** * Start a transaction. * Does not support rollback in event of failure. * * @param process {Function} The transaction function * @param successCallback {Function} * @param errorCallback {Function} */ DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) { var tx = new DroidDB_Tx(); tx.successCallback = successCallback; tx.errorCallback = errorCallback; try { process(tx); } catch (e) { console.log("Transaction error: "+e); if (tx.errorCallback) { try { tx.errorCallback(e); } catch (ex) { console.log("Transaction error calling user error callback: "+e); } } } }; /** * Transaction object * PRIVATE METHOD */ var DroidDB_Tx = function() { // Set the id of the transaction this.id = PhoneGap.createUUID(); // Callbacks this.successCallback = null; this.errorCallback = null; // Query list this.queryList = {}; }; /** * Mark query in transaction as complete. * If all queries are complete, call the user's transaction success callback. * * @param id Query id */ DroidDB_Tx.prototype.queryComplete = function(id) { delete this.queryList[id]; // If no more outstanding queries, then fire transaction success if (this.successCallback) { var count = 0; for (var i in this.queryList) { count++; } if (count == 0) { try { this.successCallback(); } catch(e) { console.log("Transaction error calling user success callback: " + e); } } } }; /** * Mark query in transaction as failed. * * @param id Query id * @param reason Error message */ DroidDB_Tx.prototype.queryFailed = function(id, reason) { // The sql queries in this transaction have already been run, since // we really don't have a real transaction implemented in native code. // However, the user callbacks for the remaining sql queries in transaction // will not be called. this.queryList = {}; if (this.errorCallback) { try { this.errorCallback(reason); } catch(e) { console.log("Transaction error calling user error callback: " + e); } } }; /** * SQL query object * PRIVATE METHOD * * @param tx The transaction object that this query belongs to */ var DroidDB_Query = function(tx) { // Set the id of the query this.id = PhoneGap.createUUID(); // Add this query to the queue droiddb.queryQueue[this.id] = this; // Init result this.resultSet = []; // Set transaction that this query belongs to this.tx = tx; // Add this query to transaction list this.tx.queryList[this.id] = this; // Callbacks this.successCallback = null; this.errorCallback = null; } /** * Execute SQL statement * * @param sql SQL statement to execute * @param params Statement parameters * @param successCallback Success callback * @param errorCallback Error callback */ DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { // Init params array if (typeof params == 'undefined') { params = []; } // Create query and add to queue var query = new DroidDB_Query(this); droiddb.queryQueue[query.id] = query; // Save callbacks query.successCallback = successCallback; query.errorCallback = errorCallback; // Call native code PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]); }; /** * SQL result set that is returned to user. * PRIVATE METHOD */ DroidDB_Result = function() { this.rows = new DroidDB_Rows(); }; /** * SQL result set object * PRIVATE METHOD */ DroidDB_Rows = function() { this.resultSet = []; // results array this.length = 0; // number of rows }; /** * Get item from SQL result set * * @param row The row number to return * @return The row object */ DroidDB_Rows.prototype.item = function(row) { return this.resultSet[row]; }; /** * Open database * * @param name Database name * @param version Database version * @param display_name Database display name * @param size Database size in bytes * @return Database object */ DroidDB_openDatabase = function(name, version, display_name, size) { PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]); var db = new DatabaseShell(); return db; }; /** * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api. * TODO: Do similar for sessionStorage. */ var CupcakeLocalStorage = function() { try { this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440); var storage = {}; this.db.transaction( function (transaction) { transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); transaction.executeSql('SELECT * FROM storage', [], function(tx, result) { for(var i = 0; i < result.rows.length; i++) { storage[result.rows.item(i)['id']] = result.rows.item(i)['body']; } PhoneGap.initializationComplete("cupcakeStorage"); }); }, function (err) { alert(err.message); } ); this.setItem = function(key, val) { console.log('set'); storage[key] = val; this.db.transaction( function (transaction) { transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]); } ); } this.getItem = function(key) { return storage[key]; } this.removeItem = function(key) { delete storage[key]; this.db.transaction( function (transaction) { transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); transaction.executeSql('DELETE FROM storage where id=?', [key]); } ); } } catch(e) { alert("Database error "+e+"."); return; } }; PhoneGap.addConstructor(function() { if (typeof window.openDatabase == "undefined") { navigator.openDatabase = window.openDatabase = DroidDB_openDatabase; window.droiddb = new DroidDB(); } if (typeof window.localStorage == "undefined") { navigator.localStorage = window.localStorage = new CupcakeLocalStorage(); PhoneGap.waitForInitialization("cupcakeStorage"); } });