diff --git a/tutorial/accelerometer.html b/tutorial/accelerometer.html
new file mode 100755
index 00000000..84441968
--- /dev/null
+++ b/tutorial/accelerometer.html
@@ -0,0 +1,232 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Start
+
+
+
+
+
+
+
+
+
diff --git a/tutorial/beep.wav b/tutorial/beep.wav
new file mode 100755
index 00000000..05f5997f
Binary files /dev/null and b/tutorial/beep.wav differ
diff --git a/tutorial/contacts.html b/tutorial/contacts.html
new file mode 100644
index 00000000..09cc137c
--- /dev/null
+++ b/tutorial/contacts.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Pick a Contact
+
+
+
+
+
+
+
+
Get first 10 contacts
+
+
+
+
+
+
+
+
diff --git a/tutorial/geolocation.html b/tutorial/geolocation.html
new file mode 100755
index 00000000..4e6c5d15
--- /dev/null
+++ b/tutorial/geolocation.html
@@ -0,0 +1,176 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+
+
+
+
Getting your current location ...
+
+
Find who are tweeting within 1 mile radius of where you are!
+
+
+
+
+
+
+
+
diff --git a/tutorial/images/FoggyRocks.png b/tutorial/images/FoggyRocks.png
new file mode 100644
index 00000000..25975562
Binary files /dev/null and b/tutorial/images/FoggyRocks.png differ
diff --git a/tutorial/images/TutBG.png b/tutorial/images/TutBG.png
new file mode 100644
index 00000000..db3a2069
Binary files /dev/null and b/tutorial/images/TutBG.png differ
diff --git a/tutorial/images/back_button.png b/tutorial/images/back_button.png
new file mode 100644
index 00000000..ca0a70a6
Binary files /dev/null and b/tutorial/images/back_button.png differ
diff --git a/tutorial/images/back_button_clicked.png b/tutorial/images/back_button_clicked.png
new file mode 100644
index 00000000..ca68ce2e
Binary files /dev/null and b/tutorial/images/back_button_clicked.png differ
diff --git a/tutorial/images/backgroundStripes.png b/tutorial/images/backgroundStripes.png
new file mode 100755
index 00000000..cab0e983
Binary files /dev/null and b/tutorial/images/backgroundStripes.png differ
diff --git a/tutorial/images/bar_large.png b/tutorial/images/bar_large.png
new file mode 100644
index 00000000..354c722f
Binary files /dev/null and b/tutorial/images/bar_large.png differ
diff --git a/tutorial/images/bar_media.png b/tutorial/images/bar_media.png
new file mode 100644
index 00000000..5d7f9c34
Binary files /dev/null and b/tutorial/images/bar_media.png differ
diff --git a/tutorial/images/header.png b/tutorial/images/header.png
new file mode 100644
index 00000000..f78a44be
Binary files /dev/null and b/tutorial/images/header.png differ
diff --git a/tutorial/images/header2.png b/tutorial/images/header2.png
new file mode 100644
index 00000000..8ad80e5f
Binary files /dev/null and b/tutorial/images/header2.png differ
diff --git a/tutorial/images/header3.png b/tutorial/images/header3.png
new file mode 100644
index 00000000..07d45c8f
Binary files /dev/null and b/tutorial/images/header3.png differ
diff --git a/tutorial/images/list_arrow.png b/tutorial/images/list_arrow.png
new file mode 100644
index 00000000..9f68c719
Binary files /dev/null and b/tutorial/images/list_arrow.png differ
diff --git a/tutorial/images/list_item_selected_bg.png b/tutorial/images/list_item_selected_bg.png
new file mode 100644
index 00000000..6bb0ad05
Binary files /dev/null and b/tutorial/images/list_item_selected_bg.png differ
diff --git a/tutorial/images/on_off_bg.png b/tutorial/images/on_off_bg.png
new file mode 100644
index 00000000..36b2eac9
Binary files /dev/null and b/tutorial/images/on_off_bg.png differ
diff --git a/tutorial/images/selection.png b/tutorial/images/selection.png
new file mode 100644
index 00000000..537e3f0b
Binary files /dev/null and b/tutorial/images/selection.png differ
diff --git a/tutorial/index.html b/tutorial/index.html
new file mode 100755
index 00000000..7db14d67
--- /dev/null
+++ b/tutorial/index.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+ PhoneGap Tutorial
+
+
+
+
+
+
+
Accelerometer
+
+
+
+
+
Notification
+
+
+
+
+
+
Contacts
+
+
+
+
+
+
GeoLocation
+
+
+
+
+ Form Inputs
+
+
+
+
+
+
+
Media
+
+
+
+
+
+
diff --git a/tutorial/inputs.html b/tutorial/inputs.html
new file mode 100755
index 00000000..67a80eef
--- /dev/null
+++ b/tutorial/inputs.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tutorial/master.css b/tutorial/master.css
new file mode 100755
index 00000000..dddb7cdb
--- /dev/null
+++ b/tutorial/master.css
@@ -0,0 +1,304 @@
+
+body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6
+{
+ margin: 0pt;
+ padding: 0pt;
+}
+
+
+
+body
+{
+ background:#000 url(images/TutBG.png) repeat-y fixed 0 0;
+ color:#666;
+ font-family:Helvetica,'Lucida Grande',sans-serif;
+ line-height:1.5em;
+ margin:0px;
+ margin-bottom:20px;
+
+}
+
+#header{
+ position: relative;
+ left: 0px;
+ top: 0px;
+ height: 44px;
+ font-size: 16pt;
+ border-top-width: 0px;
+ border-right-width: 1px;
+ border-bottom-width: 0px;
+ border-left-width: 1px;
+ width: auto;
+ margin-right: 0px;
+ margin-left: 0px;
+ background: url( header.png );
+}
+
+.header_title{
+ text-align: center;
+ position: relative;
+ font-weight: bold;
+ color: rgb(255, 255, 255);
+ text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0px;
+}
+
+.view{
+ background: url( images/backgroundStripes.png );
+ background-repeat: repeat;
+ min-height: 406px;
+}
+
+.topBar
+{
+ color:#eee;
+ font-size:1.2em;
+ text-align:center;
+ margin:0;
+ margin-top:0px;
+ padding:0;
+ background-image: url( 'images/header.png' );
+ background-repeat: repeat-x;
+ height:44px;
+ text-height:44px;
+}
+
+
+.pageTitle
+{
+ text-align: center;
+ color:#FFF;
+ line-height:44px;
+}
+
+.back_button
+{
+ font-weight: bold;
+ font-size: 12px;
+ color: rgb(255, 255, 255);
+ text-shadow: rgba(0, 0, 0, 0.6) 0px -1px 0px;
+ text-align: center;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ height: 22px;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ width: 60px;
+ line-height:22px;
+ right: auto;
+ bottom: auto;
+ margin-top: 0px;
+ position:absolute;
+ left:4px;
+ top:11px;
+ -webkit-border-image: url(images/back_button.png) 0 5 0 16 / 1px 5px 1px 16px stretch stretch;
+}
+
+.list
+{
+ margin-top: 5px;
+}
+
+.list li{
+ width: 290px;
+ height: 20px;
+ background-color: #FFF;
+ border-left: 1px solid #AAA;
+ border-right: 1px solid #AAA;
+ border-bottom: 1px solid #AAA;
+
+ list-style-type: none;
+ padding: 12px 5px 10px 5px;
+ margin-left: -36px;
+}
+
+.list li.active{
+ background-image: url( 'selection.png' );
+ background-repeat: repeat-x;
+ background-color: #194fdb !important;
+}
+
+.list li:first-of-type{
+ border-top: 1px solid #AAA;
+ -webkit-border-top-right-radius: 8px 8px;
+ -webkit-border-top-left-radius: 8px 8px;
+}
+
+.list li:last-of-type{
+ border-top: none;
+ border-bottom: 1px solid #AAA;
+
+ -webkit-border-bottom-left-radius: 8px 8px;
+ -webkit-border-bottom-right-radius: 8px 8px;
+}
+
+.list li:only-of-type{
+ border-top: 1px solid #AAA;
+ border-bottom: 1px solid #AAA;
+
+ -webkit-border-top-right-radius: 8px 8px;
+ -webkit-border-top-left-radius: 8px 8px;
+ -webkit-border-bottom-left-radius: 8px 8px;
+ -webkit-border-bottom-right-radius: 8px 8px;
+
+}
+
+.list .list_label{
+ font-weight: bold;
+ color: #000;
+ text-align: left;
+ width: 145px;
+ float: left;
+}
+
+.list .list_value{
+ color: #6e82a8;
+ text-align: right;
+ width: 140px;
+ float: right;
+}
+
+.list .selected_item{
+ color: #4c566c;
+}
+
+.list_section_label{
+ font-size: 16px;
+ font-weight: bold;
+ margin-left: 15px;
+ text-shadow: rgba(255, 255, 255, 1) 0px 1px 0px;
+ color: #4c566c;
+}
+
+.list_section_note{
+ font-size: 14px;
+ margin-left: 15px;
+ text-shadow: rgba(255, 255, 255, 1) 0px 1px 0px;
+ color: #4c566c;
+ text-align: center;
+ margin-bottom: 15px;
+ margin-top: -5px;
+}
+
+
+
+.item
+{
+ background:rgba(64,64,64,0.5);
+ border: 1px solid rgba(128,128,128,0.5);
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ clear:both;
+ margin:15px 6px 0;
+ width:295px;
+ padding:4px 0px 2px 10px;
+}
+
+a
+{
+ color:#FFF;
+ text-decoration:none;
+}
+
+
+#info{
+ background:#ffa;
+ border: 1px solid #ffd324;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ clear:both;
+ margin:15px 6px 0;
+ width:295px;
+ padding:4px 0px 2px 10px;
+}
+
+#info h4{
+ font-size:.95em;
+ margin:0;
+ padding:0;
+}
+
+#stage.theme{
+ padding-top:3px;
+}
+
+/* Definition List */
+#Page1 > dl{
+ padding-top:10px;
+ clear:both;
+ margin:0;
+ list-style-type:none;
+ padding-left:10px;
+ overflow:auto;
+}
+
+#Page1 > dl > dt{
+ font-weight:bold;
+ float:left;
+ margin-left:5px;
+}
+
+#Page1 > dl > dd{
+ width:45px;
+ float:left;
+ color:#a87;
+ font-weight:bold;
+}
+
+/* Content Styling */
+h1, h2, p{
+ margin:1em 0 .5em 13px;
+}
+
+h1{
+ color:#eee;
+ font-size:1.6em;
+ text-align:center;
+ margin:0;
+ margin-top:15px;
+ padding:0;
+}
+
+h2{
+ clear:both;
+ margin:0;
+ padding:3px;
+ font-size:1em;
+ text-align:center;
+}
+
+
+
+/* Stage Buttons */
+#stage.theme a.btn
+{
+ border: 1px solid #555;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ text-align:center;
+ display:block;
+ float:left;
+ background:#444;
+ width:150px;
+ color:#9ab;
+ font-size:1.1em;
+ text-decoration:none;
+ padding:1.2em 0;
+ margin:3px 0px 3px 5px;
+}
+
+a.btn.large
+{
+ width:64px;
+ height:32px;
+ padding:1.2em 0;
+}
+
+
+
+
+
+
+
+
+
diff --git a/tutorial/media.html b/tutorial/media.html
new file mode 100755
index 00000000..b00cdd6e
--- /dev/null
+++ b/tutorial/media.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+
+
+
+ percBass.wav
+
+
+
Play
+
+
+
+
+
+
+
+
diff --git a/tutorial/notification.html b/tutorial/notification.html
new file mode 100755
index 00000000..42afaef8
--- /dev/null
+++ b/tutorial/notification.html
@@ -0,0 +1,105 @@
+
+
+
+
+
+ PhoneGap
+
+
+
+
+
+
+
+
+
+
+
Show Custom Alert
+
+
+
+
+
Show Activity Indicator
+
+
+
+
+
+
Vibrate
+
+
+
+
+
+
Show Loading ( 2 Seconds )
+
+
+
+
+
+
Beep
+
+
+
+
+
diff --git a/tutorial/percBass.wav b/tutorial/percBass.wav
new file mode 100755
index 00000000..e63dcc4a
Binary files /dev/null and b/tutorial/percBass.wav differ
diff --git a/tutorial/phonegap.js b/tutorial/phonegap.js
new file mode 100644
index 00000000..d569266a
--- /dev/null
+++ b/tutorial/phonegap.js
@@ -0,0 +1,1272 @@
+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
+ */
+PhoneGap = {
+ queue: {
+ ready: true,
+ commands: [],
+ timer: null
+ },
+ _constructors: []
+};
+
+/**
+ * Boolean flag indicating if the PhoneGap API is available and initialized.
+ */ // TODO: Remove this, it is unused here ... -jm
+PhoneGap.available = DeviceInfo.uuid != undefined;
+
+/**
+ * Add an initialization function to a queue that ensures it will run and initialize
+ * application constructors only once PhoneGap has been initialized.
+ * @param {Function} func The function callback you want run once PhoneGap is initialized
+ */
+PhoneGap.addConstructor = function(func) {
+ var state = document.readyState;
+ if ( state == 'loaded' || state == 'complete' )
+ {
+ func();
+ }
+ else
+ {
+ PhoneGap._constructors.push(func);
+ }
+};
+
+(function()
+ {
+ var timer = setInterval(function()
+ {
+
+ var state = document.readyState;
+
+ if ( state == 'loaded' || state == 'complete' )
+ {
+ clearInterval(timer); // stop looking
+ // run our constructors list
+ while (PhoneGap._constructors.length > 0)
+ {
+ var constructor = PhoneGap._constructors.shift();
+ try
+ {
+ constructor();
+ }
+ catch(e)
+ {
+ if (typeof(debug['log']) == 'function')
+ {
+ debug.log("Failed to run constructor: " + debug.processMessage(e));
+ }
+ else
+ {
+ alert("Failed to run constructor: " + e.message);
+ }
+ }
+ }
+ // all constructors run, now fire the deviceready event
+ var e = document.createEvent('Events');
+ e.initEvent('deviceready');
+ document.dispatchEvent(e);
+ }
+ }, 5);
+})();
+
+
+/**
+ * Execute a PhoneGap command in a queued fashion, to ensure commands do not
+ * execute with any race conditions, and only run when PhoneGap is ready to
+ * recieve them.
+ * @param {String} command Command to be run in PhoneGap, e.g. "ClassName.method"
+ * @param {String[]} [args] Zero or more arguments to pass to the method
+ */
+PhoneGap.exec = function() {
+ PhoneGap.queue.commands.push(arguments);
+ if (PhoneGap.queue.timer == null)
+ PhoneGap.queue.timer = setInterval(PhoneGap.run_command, 10);
+};
+
+/**
+ * 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
+ */
+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;
+
+};
+function Acceleration(x, y, z)
+{
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.timestamp = new Date().getTime();
+}
+
+// Need to define these for android
+_accel = {};
+_accel.x = 0;
+_accel.y = 0;
+_accel.z = 0;
+
+function gotAccel(x, y, z)
+{
+ _accel.x = x;
+ _accel.y = y;
+ _accel.z = z;
+}
+
+/**
+ * This class provides access to device accelerometer data.
+ * @constructor
+ */
+function Accelerometer() {
+ /**
+ * The last known acceleration.
+ */
+ this.lastAcceleration = null;
+}
+
+/**
+ * Asynchronously aquires the current acceleration.
+ * @param {Function} successCallback The function to call when the acceleration
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the acceleration data.
+ * @param {AccelerationOptions} options The options for getting the accelerometer data
+ * such as timeout.
+ */
+Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
+ // If the acceleration is available then call success
+ // If the acceleration is not available then call error
+
+ // Created for iPhone, Iphone passes back _accel obj litteral
+ if (typeof successCallback == "function") {
+ var accel = new Acceleration(_accel.x,_accel.y,_accel.z);
+ Accelerometer.lastAcceleration = accel;
+ successCallback(accel);
+ }
+}
+
+/**
+ * Asynchronously aquires the acceleration repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the acceleration
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the acceleration data.
+ * @param {AccelerationOptions} options The options for getting the accelerometer data
+ * such as timeout.
+ */
+
+Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
+ // TODO: add the interval id to a list so we can clear all watches
+ var frequency = (options != undefined)? options.frequency : 10000;
+
+ Accel.start(frequency);
+ return setInterval(function() {
+ navigator.accelerometer.getCurrentAcceleration(successCallback, errorCallback, options);
+ }, frequency);
+}
+
+/**
+ * Clears the specified accelerometer watch.
+ * @param {String} watchId The ID of the watch returned from #watchAcceleration.
+ */
+Accelerometer.prototype.clearWatch = function(watchId) {
+ Accel.stop();
+ clearInterval(watchId);
+}
+
+PhoneGap.addConstructor(function() {
+ if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
+});
+/**
+ * This class provides access to the device camera.
+ * @constructor
+ */
+function Camera() {
+
+}
+
+/**
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
+
+ this.winCallback = successCallback;
+ this.failCallback = errorCallback;
+ if (options.quality)
+ {
+ GapCam.takePicture(options.quality);
+ }
+ else
+ {
+ GapCam.takePicture(80);
+ }
+}
+
+Camera.prototype.win = function(picture)
+{
+ this.winCallback(picture);
+}
+
+Camera.prototype.fail = function(err)
+{
+ this.failCallback(err);
+}
+
+PhoneGap.addConstructor(function() {
+ if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
+});
+/**
+ * This class provides access to device Compass data.
+ * @constructor
+ */
+function Compass() {
+ /**
+ * The last known Compass position.
+ */
+ this.lastHeading = null;
+ this.lastError = null;
+ this.callbacks = {
+ onHeadingChanged: [],
+ onError: []
+ };
+};
+
+/**
+ * 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.
+ */
+Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
+ if (this.lastHeading == null) {
+ this.start(options);
+ }
+ else
+ if (typeof successCallback == "function") {
+ successCallback(this.lastHeading);
+ }
+};
+
+/**
+ * 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.
+ */
+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);
+};
+
+
+/**
+ * Clears the specified heading watch.
+ * @param {String} watchId The ID of the watch returned from #watchHeading.
+ */
+Compass.prototype.clearWatch = function(watchId) {
+ clearInterval(watchId);
+};
+
+
+/**
+ * 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);
+ }
+};
+
+/**
+ * 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();
+});
+var Contact = function(){
+ this.name = new ContactName();
+ this.emails = [];
+ this.phones = [];
+}
+
+var ContactName = function()
+{
+ this.formatted = "";
+ this.familyName = "";
+ this.givenName = "";
+ this.additionalNames = [];
+ this.prefixes = [];
+ this.suffixes = [];
+}
+
+
+var ContactEmail = function()
+{
+ this.types = [];
+ this.address = "";
+}
+
+var ContactPhoneNumber = function()
+{
+ this.types = [];
+ this.number = "";
+}
+
+
+var Contacts = function()
+{
+ this.records = [];
+}
+
+Contacts.prototype.find = function(obj, win, fail)
+{
+ if(obj.name != null)
+ {
+ // Build up the search term that we'll use in SQL, based on the structure/contents of the contact object passed into find.
+ var searchTerm = '';
+ if (obj.name.givenName && obj.name.givenName.length > 0) {
+ searchTerm = obj.name.givenName.split(' ').join('%');
+ }
+ if (obj.name.familyName && obj.name.familyName.length > 0) {
+ searchTerm += obj.name.familyName.split(' ').join('%');
+ }
+ if (!obj.name.familyName && !obj.name.givenName && obj.name.formatted) {
+ searchTerm = obj.name.formatted;
+ }
+ ContactHook.search(searchTerm, "", "");
+ }
+ this.win = win;
+ this.fail = fail;
+}
+
+Contacts.prototype.droidFoundContact = function(name, npa, email)
+{
+ var contact = new Contact();
+ contact.name = new ContactName();
+ contact.name.formatted = name;
+ contact.name.givenName = name;
+ var mail = new ContactEmail();
+ mail.types.push("home");
+ mail.address = email;
+ contact.emails.push(mail);
+ phone = new ContactPhoneNumber();
+ phone.types.push("home");
+ phone.number = npa;
+ contact.phones.push(phone);
+ this.records.push(contact);
+}
+
+Contacts.prototype.droidDone = function()
+{
+ this.win(this.records);
+}
+
+PhoneGap.addConstructor(function() {
+ if(typeof navigator.contacts == "undefined") navigator.contacts = new Contacts();
+});
+var Crypto = function()
+{
+}
+
+Crypto.prototype.encrypt = function(seed, string, callback)
+{
+ GapCrypto.encrypt(seed, string);
+ this.encryptWin = callback;
+}
+
+Crypto.prototype.decrypt = function(seed, string, callback)
+{
+ GapCrypto.decrypt(seed, string);
+ this.decryptWin = callback;
+}
+
+Crypto.prototype.gotCryptedString = function(string)
+{
+ this.encryptWin(string);
+}
+
+Crypto.prototype.getPlainString = function(string)
+{
+ this.decryptWin(string);
+}
+
+PhoneGap.addConstructor(function() {
+ if (typeof navigator.Crypto == "undefined")
+ {
+ navigator.Crypto = new Crypto();
+ }
+});
+
+/**
+ * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+function Device() {
+ this.available = PhoneGap.available;
+ this.platform = null;
+ this.version = null;
+ this.name = null;
+ this.gap = null;
+ this.uuid = null;
+ try {
+ if (window.DroidGap) {
+ this.available = true;
+ this.uuid = window.DroidGap.getUuid();
+ this.version = window.DroidGap.getOSVersion();
+ this.gapVersion = window.DroidGap.getVersion();
+ this.platform = window.DroidGap.getPlatform();
+ this.name = window.DroidGap.getProductName();
+ }
+ } catch(e) {
+ this.available = false;
+ }
+}
+
+PhoneGap.addConstructor(function() {
+ navigator.device = window.device = new Device();
+});
+
+
+
+PhoneGap.addConstructor(function() { if (typeof navigator.fileMgr == "undefined") navigator.fileMgr = new FileMgr();});
+
+
+/**
+ * This class provides iPhone read and write access to the mobile device file system.
+ * Based loosely on http://www.w3.org/TR/2009/WD-FileAPI-20091117/#dfn-empty
+ */
+function FileMgr()
+{
+ this.fileWriters = {}; // empty maps
+ this.fileReaders = {};
+
+ this.docsFolderPath = "../../Documents";
+ this.tempFolderPath = "../../tmp";
+ this.freeDiskSpace = -1;
+ this.getFileBasePaths();
+ this.getFreeDiskSpace();
+}
+
+// private, called from Native Code
+FileMgr.prototype._setPaths = function(docs,temp)
+{
+ this.docsFolderPath = docs;
+ this.tempFolderPath = temp;
+}
+
+// private, called from Native Code
+FileMgr.prototype._setFreeDiskSpace = function(val)
+{
+ this.freeDiskSpace = val;
+}
+
+
+// FileWriters add/remove
+// called internally by writers
+FileMgr.prototype.addFileWriter = function(filePath,fileWriter)
+{
+ this.fileWriters[filePath] = fileWriter;
+}
+
+FileMgr.prototype.removeFileWriter = function(filePath)
+{
+ this.fileWriters[filePath] = null;
+}
+
+// File readers add/remove
+// called internally by readers
+FileMgr.prototype.addFileReader = function(filePath,fileReader)
+{
+ this.fileReaders[filePath] = fileReader;
+}
+
+FileMgr.prototype.removeFileReader = function(filePath)
+{
+ this.fileReaders[filePath] = null;
+}
+
+/*******************************************
+ *
+ * private reader callback delegation
+ * called from native code
+ */
+FileMgr.prototype.reader_onloadstart = function(filePath,result)
+{
+ this.fileReaders[filePath].onloadstart(result);
+}
+
+FileMgr.prototype.reader_onprogress = function(filePath,result)
+{
+ this.fileReaders[filePath].onprogress(result);
+}
+
+FileMgr.prototype.reader_onload = function(filePath,result)
+{
+ this.fileReaders[filePath].result = unescape(result);
+ this.fileReaders[filePath].onload(this.fileReaders[filePath].result);
+}
+
+FileMgr.prototype.reader_onerror = function(filePath,err)
+{
+ this.fileReaders[filePath].result = err;
+ this.fileReaders[filePath].onerror(err);
+}
+
+FileMgr.prototype.reader_onloadend = function(filePath,result)
+{
+ this.fileReaders[filePath].onloadend(result);
+}
+
+/*******************************************
+ *
+ * private writer callback delegation
+ * called from native code
+*/
+FileMgr.prototype.writer_onerror = function(filePath,err)
+{
+ this.fileWriters[filePath].onerror(err);
+}
+
+FileMgr.prototype.writer_oncomplete = function(filePath,result)
+{
+ this.fileWriters[filePath].oncomplete(result); // result contains bytes written
+}
+
+
+FileMgr.prototype.getFileBasePaths = function()
+{
+ //PhoneGap.exec("File.getFileBasePaths");
+}
+
+FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback)
+{
+ var test = FileUtil.testFileExists(fileName);
+ test ? successCallback() : errorCallback();
+}
+
+FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback)
+{
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ var test = FileUtil.testDirectoryExists(dirName);
+ test ? successCallback() : errorCallback();
+}
+
+FileMgr.prototype.createDirectory = function(dirName, successCallback, errorCallback)
+{
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ var test = FileUtils.createDirectory(dirName);
+ test ? successCallback() : errorCallback();
+}
+
+FileMgr.prototype.deleteDirectory = function(dirName, successCallback, errorCallback)
+{
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ var test = FileUtils.deleteDirectory(dirName);
+ test ? successCallback() : errorCallback();
+}
+
+FileMgr.prototype.deleteFile = function(fileName, successCallback, errorCallback)
+{
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ FileUtils.deleteFile(fileName);
+ test ? successCallback() : errorCallback();
+}
+
+FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback)
+{
+ if(this.freeDiskSpace > 0)
+ {
+ return this.freeDiskSpace;
+ }
+ else
+ {
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+ this.freeDiskSpace = FileUtils.getFreeDiskSpace();
+ (this.freeDiskSpace > 0) ? successCallback() : errorCallback();
+ }
+}
+
+
+// File Reader
+
+
+function FileReader()
+{
+ this.fileName = "";
+ this.result = null;
+ this.onloadstart = null;
+ this.onprogress = null;
+ this.onload = null;
+ this.onerror = null;
+ this.onloadend = null;
+}
+
+
+FileReader.prototype.abort = function()
+{
+ // Not Implemented
+}
+
+FileReader.prototype.readAsText = function(file)
+{
+ if(this.fileName && this.fileName.length > 0)
+ {
+ navigator.fileMgr.removeFileReader(this.fileName,this);
+ }
+ this.fileName = file;
+ navigator.fileMgr.addFileReader(this.fileName,this);
+ //alert("Calling File.read : " + this.fileName);
+ //window.location = "gap://File.readFile/"+ file;
+ this.onerror = errorCallback;
+ this.oncomplete = successCallback;
+
+ return FileUtil.read(fileName);
+}
+
+FileReader.prototype.hasRead(data)
+{
+
+}
+
+// File Writer
+
+function FileWriter()
+{
+ this.fileName = "";
+ this.result = null;
+ this.readyState = 0; // EMPTY
+ this.result = null;
+ this.onerror = null;
+ this.oncomplete = null;
+}
+
+FileWriter.prototype.writeAsText = function(file,text,bAppend)
+{
+ if(this.fileName && this.fileName.length > 0)
+ {
+ navigator.fileMgr.removeFileWriter(this.fileName,this);
+ }
+ this.fileName = file;
+ if(bAppend != true)
+ {
+ bAppend = false; // for null values
+ }
+ navigator.fileMgr.addFileWriter(file,this);
+ this.readyState = 0; // EMPTY
+ var call = FileUtil.write(file, text, bAppend);
+ this.result = null;
+}
+
+
+
+
+
+/**
+ * This class provides access to device GPS data.
+ * @constructor
+ */
+function Geolocation() {
+ /**
+ * The last known GPS position.
+ */
+ this.lastPosition = null;
+ this.lastError = null;
+ this.callbacks = {
+ onLocationChanged: [],
+ onError: []
+ };
+};
+
+Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options)
+{
+ var position = Geo.getCurrentLocation();
+ this.global_success = successCallback;
+ this.fail = errorCallback;
+}
+
+/**
+ * Asynchronously aquires the position repeatedly at a given interval.
+ * @param {Function} successCallback The function to call each time the position
+ * data is available
+ * @param {Function} errorCallback The function to call when there is an error
+ * getting the position data.
+ * @param {PositionOptions} options The options for getting the position data
+ * such as timeout and the frequency of the watch.
+ */
+Geolocation.prototype.watchPosition = 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.getCurrentPosition(successCallback, errorCallback, options);
+ var frequency = 10000;
+ if (typeof(options) == 'object' && options.frequency)
+ frequency = options.frequency;
+
+ var that = this;
+ return setInterval(function() {
+ that.getCurrentPosition(successCallback, errorCallback, options);
+ }, frequency);
+};
+
+
+/**
+ * Clears the specified position watch.
+ * @param {String} watchId The ID of the watch returned from #watchPosition.
+ */
+Geolocation.prototype.clearWatch = function(watchId) {
+ clearInterval(watchId);
+};
+
+/**
+ * Called by the geolocation framework when the current location is found.
+ * @param {PositionOptions} position The current position.
+ */
+Geolocation.prototype.setLocation = function(position) {
+ this.lastPosition = position;
+ for (var i = 0; i < this.callbacks.onLocationChanged.length; i++) {
+ var f = this.callbacks.onLocationChanged.shift();
+ f(position);
+ }
+};
+
+/**
+ * Called by the geolocation framework when an error occurs while looking up the current position.
+ * @param {String} message The text of the error message.
+ */
+Geolocation.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);
+ }
+};
+
+// Run the global callback
+Geolocation.prototype.gotCurrentPosition = function(lat, lng, alt, altacc, head, vel, stamp)
+{
+ if (lat == "undefined" || lng == "undefined")
+ {
+ this.fail();
+ }
+ else
+ {
+ coords = new Coordinates(lat, lng, alt, altacc, head, vel);
+ loc = new Position(coords, stamp);
+ this.lastPosition = loc;
+ this.global_success(loc);
+ }
+}
+
+/*
+* This turns on the GeoLocator class, which has two listeners.
+* The listeners have their own timeouts, and run independently of this process
+* In this case, we return the key to the watch hash
+*/
+
+Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options)
+{
+ var frequency = (options != undefined)? options.frequency : 10000;
+
+ if (!this.listeners)
+ {
+ this.listeners = [];
+ }
+
+ var key = this.listeners.push( {"success" : successCallback, "fail" : failCallback }) - 1;
+
+ // TO-DO: Get the names of the method and pass them as strings to the Java.
+ return Geolocation.start(frequency, key);
+}
+
+/*
+ * Retrieve and stop this listener from listening to the GPS
+ *
+ */
+Geolocation.prototype.success = function(key, lat, lng, alt, altacc, head, vel, stamp)
+{
+ var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
+ var loc = new Position(coords, stamp);
+ this.listeners[key].success(loc);
+}
+
+Geolocation.prototype.fail = function(key)
+{
+ this.listeners[key].fail();
+}
+
+Geolocation.prototype.clearWatch = function(watchId)
+{
+ Geo.stop(watchId);
+}
+// Taken from Jesse's geo fix (similar problem) in PhoneGap iPhone. Go figure, same browser!
+function __proxyObj(origObj, proxyObj, funkList) {
+ for (var v in funkList) {
+ origObj[funkList[v]] = proxyObj[funkList[v]];
+ }
+}
+PhoneGap.addConstructor(function() {
+ navigator._geo = new Geolocation();
+ __proxyObj(navigator.geolocation, navigator._geo,
+ ["setLocation", "getCurrentPosition", "watchPosition",
+ "clearWatch", "setError", "start", "stop", "gotCurrentPosition"]
+ );
+});function KeyEvent()
+{
+}
+
+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();
+}
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ * @constructor
+ */
+function Media(src, successCallback, errorCallback) {
+ this.src = src;
+ this.successCallback = successCallback;
+ this.errorCallback = errorCallback;
+}
+
+Media.prototype.record = function() {
+}
+
+Media.prototype.play = function() {
+}
+
+Media.prototype.pause = function() {
+}
+
+Media.prototype.stop = function() {
+}
+
+
+/**
+ * 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;
+
+
+//if (typeof navigator.audio == "undefined") navigator.audio = new Media(src);
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ * @constructor
+ */
+
+Media.prototype.play = function() {
+ DroidGap.startPlayingAudio(this.src);
+}
+
+Media.prototype.stop = function() {
+ DroidGap.stopPlayingAudio();
+}
+
+Media.prototype.startRecord = function() {
+ DroidGap.startRecordingAudio(this.src);
+}
+
+Media.prototype.stopRecordingAudio = function() {
+ DroidGap.stopRecordingAudio();
+}
+
+
+/**
+ * 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;
+};
+
+/**
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options (isIpAddress:boolean)
+ */
+Network.prototype.isReachable = function(hostName, successCallback, options) {
+}
+
+/**
+ * Called by the geolocation framework when the reachability status has changed.
+ * @param {Reachibility} reachability The current reachability status.
+ */
+Network.prototype.updateReachability = function(reachability) {
+ this.lastReachability = reachability;
+};
+
+PhoneGap.addConstructor(function() {
+ if (typeof navigator.network == "undefined") navigator.network = new Network();
+});
+Network.prototype.isReachable = function(uri, win, options)
+{
+ var status = new NetworkStatus();
+ if(NetworkManager.isReachable(uri))
+ {
+ if (NetworkManager.isWifiActive)
+ status.code = 2;
+ else
+ status.code = 1;
+ }
+ else
+ status.code = 0;
+ win(status);
+}
+/**
+ * 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 {String} [title="Alert"] Title of the alert dialog (default: Alert)
+ * @param {String} [buttonLabel="OK"] Label of the close button (default: OK)
+ */
+Notification.prototype.alert = function(message, title, buttonLabel) {
+ // Default is to use a browser alert; this will use "index.html" as the title though
+ alert(message);
+};
+
+/**
+ * Start spinning the activity indicator on the statusbar
+ */
+Notification.prototype.activityStart = function() {
+};
+
+/**
+ * Stop spinning the activity indicator on the statusbar, if it's currently spinning
+ */
+Notification.prototype.activityStop = function() {
+};
+
+/**
+ * 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) {
+
+};
+
+/**
+ * Causes the device to vibrate.
+ * @param {Integer} mills The number of milliseconds to vibrate for.
+ */
+Notification.prototype.vibrate = function(mills) {
+
+};
+
+/**
+ * Causes the device to beep.
+ * @param {Integer} count The number of beeps.
+ * @param {Integer} volume The volume of the beep.
+ */
+Notification.prototype.beep = function(count, volume) {
+
+};
+
+// TODO: of course on Blackberry and Android there notifications in the UI as well
+
+PhoneGap.addConstructor(function() {
+ if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
+});
+
+Notification.prototype.vibrate = function(mills)
+{
+ DroidGap.vibrate(mills);
+}
+
+/*
+ * On the Android, we don't beep, we notify you with your
+ * notification! We shouldn't keep hammering on this, and should
+ * review what we want beep to do.
+ */
+
+Notification.prototype.beep = function(count, volume)
+{
+ DroidGap.beep(count);
+}
+/**
+ * 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 = new Date().getTime();
+}
+
+function Coordinates(lat, lng, alt, acc, head, vel) {
+ /**
+ * 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;
+}
+
+/**
+ * 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;
+/*
+ * 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
+ */
+
+var DroidDB = function()
+{
+ this.txQueue = [];
+}
+
+DroidDB.prototype.addResult = function(rawdata, tx_id)
+{
+ eval("var data = " + rawdata);
+ var tx = this.txQueue[tx_id];
+ tx.resultSet.push(data);
+}
+
+DroidDB.prototype.completeQuery = function(tx_id)
+{
+ var tx = this.txQueue[tx_id];
+ var r = new result();
+ r.rows.resultSet = tx.resultSet;
+ r.rows.length = tx.resultSet.length;
+ tx.win(r);
+}
+
+DroidDB.prototype.fail = function(reason, tx_id)
+{
+ var tx = this.txQueue[tx_id];
+ tx.fail(reason);
+}
+
+var DatabaseShell = function()
+{
+
+}
+
+DatabaseShell.prototype.transaction = function(process)
+{
+ tx = new Tx();
+ process(tx);
+}
+
+var Tx = function()
+{
+ droiddb.txQueue.push(this);
+ this.id = droiddb.txQueue.length - 1;
+ this.resultSet = [];
+}
+
+Tx.prototype.executeSql = function(query, params, win, fail)
+{
+ droidStorage.executeSql(query, params, this.id);
+ tx.win = win;
+ tx.fail = fail;
+}
+
+var result = function()
+{
+ this.rows = new Rows();
+}
+
+var Rows = function()
+{
+ this.resultSet = [];
+ this.length = 0;
+}
+
+Rows.prototype.item = function(row_id)
+{
+ return this.resultSet[id];
+}
+
+var dbSetup = function(name, version, display_name, size)
+{
+ droidStorage.openDatabase(name, version, display_name, size)
+ db_object = new DatabaseShell();
+ return db_object;
+}
+
+PhoneGap.addConstructor(function() {
+ if (typeof navigator.openDatabase == "undefined")
+ {
+ navigator.openDatabase = window.openDatabase = dbSetup;
+ window.droiddb = new DroidDB();
+ }
+});
+