Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-wp7
diff --git a/templates/standalone/cordovalib/Commands/Accelerometer.cs b/templates/standalone/cordovalib/Commands/Accelerometer.cs
index ae1e860..baea009 100644
--- a/templates/standalone/cordovalib/Commands/Accelerometer.cs
+++ b/templates/standalone/cordovalib/Commands/Accelerometer.cs
@@ -19,6 +19,7 @@
 using System.Threading;
 using Microsoft.Devices.Sensors;
 using System.Globalization;
+using System.Diagnostics;
 
 namespace WP7CordovaClassLib.Cordova.Commands
 {
@@ -172,14 +173,14 @@
         private string GetCurrentAccelerationFormatted()
         {
             // convert to unix timestamp
-            long timestamp = ((accelerometer.CurrentValue.Timestamp.DateTime - StartOfEpoch).Ticks) / 10000;
-            string resultCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2},\"timestamp\":{3}",
+            // long timestamp = ((accelerometer.CurrentValue.Timestamp.DateTime - StartOfEpoch).Ticks) / 10000;
+            // Note: Removed timestamp, to let the JS side create it using (new Date().getTime()) -jm
+            // this resolves an issue with inconsistencies between JS dates and Native DateTime 
+            string resultCoordinates = String.Format("\"x\":{0},\"y\":{1},\"z\":{2}",
                             (accelerometer.CurrentValue.Acceleration.X * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
                             (accelerometer.CurrentValue.Acceleration.Y * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
-                            (accelerometer.CurrentValue.Acceleration.Z * gConstant).ToString("0.00000", CultureInfo.InvariantCulture),
-                            timestamp.ToString());
-            resultCoordinates = "{" + resultCoordinates + "}";
-            return resultCoordinates;
+                            (accelerometer.CurrentValue.Acceleration.Z * gConstant).ToString("0.00000", CultureInfo.InvariantCulture));
+            return  "{" + resultCoordinates + "}";
         }
 
         /// <summary>
diff --git a/templates/standalone/cordovalib/Commands/AudioPlayer.cs b/templates/standalone/cordovalib/Commands/AudioPlayer.cs
index 43ae237..83f178c 100644
--- a/templates/standalone/cordovalib/Commands/AudioPlayer.cs
+++ b/templates/standalone/cordovalib/Commands/AudioPlayer.cs
@@ -34,11 +34,11 @@
         #region Constants
 
         // AudioPlayer states
-        private const int MediaNone = 0;
-        private const int MediaStarting = 1;
-        private const int MediaRunning = 2;
-        private const int MediaPaused = 3;
-        private const int MediaStopped = 4;
+        private const int PlayerState_None = 0;
+        private const int PlayerState_Starting = 1;
+        private const int PlayerState_Running = 2;
+        private const int PlayerState_Paused = 3;
+        private const int PlayerState_Stopped = 4;
 
         // AudioPlayer messages
         private const int MediaState = 1;
@@ -89,7 +89,7 @@
         /// <summary>
         /// State of recording or playback
         /// </summary>
-        private int state = MediaNone;
+        private int state = PlayerState_None;
 
         /// <summary>
         /// File name to play or record to
@@ -155,7 +155,7 @@
         {
             if (this.player != null)
             {
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorPlayModeSet));
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorPlayModeSet),false);
             }
             else if (this.recorder == null)
             {
@@ -171,17 +171,17 @@
                     this.WriteWavHeader(this.memoryStream, this.recorder.SampleRate);
                     this.recorder.Start();
                     FrameworkDispatcher.Update();
-                    this.SetState(MediaRunning);
+                    this.SetState(PlayerState_Running);
                 }
                 catch (Exception)
                 {
-                    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingRecording));
+                    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingRecording),false);
                 }
             }
             else
             {
 
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorAlreadyRecording));
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorAlreadyRecording),false);
             }
         }
 
@@ -192,7 +192,7 @@
         {
             if (this.recorder != null)
             {
-                if (this.state == MediaRunning)
+                if (this.state == PlayerState_Running)
                 {
                     try
                     {
@@ -201,7 +201,7 @@
                         this.recorder = null;
                         SaveAudioClipToLocalStorage();
                         this.FinalizeXnaGameLoop();
-                        this.SetState(MediaStopped);
+                        this.SetState(PlayerState_Stopped);
                     }
                     catch (Exception)
                     {
@@ -223,7 +223,7 @@
         {
             if (this.recorder != null)
             {
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorRecordModeSet));
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorRecordModeSet),false);
                 return;
             }
 
@@ -286,28 +286,30 @@
                             else
                             {
                                 Debug.WriteLine("Error: source doesn't exist :: " + filePath);
-                                throw new ArgumentException("Source doesn't exist");
+                                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, 1),false);
+                                return;
+                                //throw new ArgumentException("Source doesn't exist");
                             }
                         }
                     }
-                    this.SetState(MediaStarting);
+                    this.SetState(PlayerState_Starting);
                 }
                 catch (Exception e)
                 {
                     Debug.WriteLine("Error: " + e.Message);
-                    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingPlayback));
+                    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStartingPlayback),false);
                 }
             }
             else
             {
-                if (this.state != MediaRunning)
+                if (this.state != PlayerState_Running)
                 {
                     this.player.Play();
-                    this.SetState(MediaRunning);
+                    this.SetState(PlayerState_Running);
                 }
                 else
                 {
-                    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorResumeState));
+                    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorResumeState),false);
                 }
             }
         }
@@ -317,15 +319,21 @@
         /// </summary>
         private void MediaOpened(object sender, RoutedEventArgs arg)
         {
-            if (!this.prepareOnly)
+            if (this.player != null)
             {
-                this.player.Play();
-                this.SetState(MediaRunning);
+                this.duration = this.player.NaturalDuration.TimeSpan.TotalSeconds;
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaDuration, this.duration),false);
+                if (!this.prepareOnly)
+                {
+                    this.player.Play();
+                    this.SetState(PlayerState_Running);
+                }
+                this.prepareOnly = false;
             }
-
-            this.duration = this.player.NaturalDuration.TimeSpan.TotalSeconds;
-            this.prepareOnly = false;
-            this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaDuration, this.duration));
+            else
+            {
+                // TODO: occasionally MediaOpened is signalled, but player is null
+            }
         }
 
         /// <summary>
@@ -333,7 +341,7 @@
         /// </summary>
         private void MediaEnded(object sender, RoutedEventArgs arg)
         {
-            this.SetState(MediaStopped);
+            this.SetState(PlayerState_Stopped);
         }
 
         /// <summary>
@@ -342,7 +350,7 @@
         private void MediaFailed(object sender, RoutedEventArgs arg)
         {
             player.Stop();
-            this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError.ToString(), "Media failed"));
+            this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError.ToString(), "Media failed"),false);
         }
 
         /// <summary>
@@ -355,7 +363,7 @@
             {
                 TimeSpan tsPos = new TimeSpan(0, 0, 0, 0, milliseconds);
                 this.player.Position = tsPos;
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, milliseconds / 1000.0f));
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, milliseconds / 1000.0f),false);
             }
         }
 
@@ -376,14 +384,14 @@
         /// </summary>
         public void pausePlaying()
         {
-            if (this.state == MediaRunning)
+            if (this.state == PlayerState_Running)
             {
                 this.player.Pause();
-                this.SetState(MediaPaused);
+                this.SetState(PlayerState_Paused);
             }
             else
             {
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorPauseState));
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorPauseState),false);
             }
         }
 
@@ -393,17 +401,17 @@
         /// </summary>
         public void stopPlaying()
         {
-            if ((this.state == MediaRunning) || (this.state == MediaPaused))
+            if ((this.state == PlayerState_Running) || (this.state == PlayerState_Paused))
             {
                 this.player.Stop();
 
                 this.player.Position = new TimeSpan(0L);
-                this.SetState(MediaStopped);
+                this.SetState(PlayerState_Stopped);
             }
-            else
-            {
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStopState));
-            }
+            //else // Why is it an error to call stop on a stopped media?
+            //{
+            //    this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaError, MediaErrorStopState), false);
+            //}
         }
 
         /// <summary>
@@ -412,15 +420,15 @@
         /// <returns>current position</returns>
         public double getCurrentPosition()
         {
-            if ((this.state == MediaRunning) || (this.state == MediaPaused))
+            if ((this.state == PlayerState_Running) || (this.state == PlayerState_Paused))
             {
                 double currentPosition = this.player.Position.TotalSeconds;
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, currentPosition));
+                //this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaPosition, currentPosition),false);
                 return currentPosition;
             }
             else
             {
-                return -1;
+                return 0;
             }
         }
 
@@ -457,7 +465,7 @@
         {
             if (this.state != state)
             {
-                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaState, state));
+                this.handler.InvokeCustomScript(new ScriptCallback(CallbackFunction, this.id, MediaState, state),false);
             }
 
             this.state = state;
diff --git a/templates/standalone/cordovalib/Commands/BaseCommand.cs b/templates/standalone/cordovalib/Commands/BaseCommand.cs
index 5cdad2b..5f5c379 100644
--- a/templates/standalone/cordovalib/Commands/BaseCommand.cs
+++ b/templates/standalone/cordovalib/Commands/BaseCommand.cs
@@ -72,12 +72,15 @@
         }
 
 
-        public void InvokeCustomScript(ScriptCallback script)
+        public void InvokeCustomScript(ScriptCallback script, bool removeHandler)
         {
             if (this.OnCustomScript != null)
             {
                 this.OnCustomScript(this, script);
-                this.OnCustomScript = null;
+                if (removeHandler)
+                {
+                    this.OnCustomScript = null;
+                }
             }
         }
 
diff --git a/templates/standalone/cordovalib/Commands/Compass.cs b/templates/standalone/cordovalib/Commands/Compass.cs
index 9f8d402..02defd6 100644
--- a/templates/standalone/cordovalib/Commands/Compass.cs
+++ b/templates/standalone/cordovalib/Commands/Compass.cs
@@ -129,14 +129,13 @@
         /// </summary>
         /// <returns>Coordinates in JSON format</returns>
         private string GetHeadingFormatted(CompassReading reading)
-        {
-            string result = String.Format("\"magneticHeading\":{0},\"headingAccuracy\":{1},\"trueHeading\":{2},\"timestamp\":{3}",
+        {   
+            // NOTE: timestamp is generated on the JS side, to avoid issues with format conversions
+            string result = String.Format("\"magneticHeading\":{0},\"headingAccuracy\":{1},\"trueHeading\":{2}",
                             reading.MagneticHeading.ToString("0.0", CultureInfo.InvariantCulture),
                             reading.HeadingAccuracy.ToString("0.0", CultureInfo.InvariantCulture),
-                            reading.TrueHeading.ToString("0.0", CultureInfo.InvariantCulture),
-                            reading.Timestamp.UtcTicks.ToString());
-            result = "{" + result + "}";
-            return result;
+                            reading.TrueHeading.ToString("0.0", CultureInfo.InvariantCulture));
+            return "{" + result + "}";
         }
 
         public void getHeading(string options)
diff --git a/templates/standalone/cordovalib/Commands/Contacts.cs b/templates/standalone/cordovalib/Commands/Contacts.cs
index 1157b91..8428ea5 100644
--- a/templates/standalone/cordovalib/Commands/Contacts.cs
+++ b/templates/standalone/cordovalib/Commands/Contacts.cs
@@ -303,6 +303,10 @@
             {
                 foreach (JSONContactAddress address in contact.addresses)
                 {
+                    if (address.type == null)
+                    {
+                        address.type = "home"; // set a default
+                    }
                     string fieldType = address.type.ToLower();
                     if (fieldType == "work")
                     {
@@ -565,8 +569,7 @@
                                                address.PhysicalAddress.PostalCode,
                                                address.PhysicalAddress.CountryRegion);
 
-
-            Debug.WriteLine("getFormattedJSONAddress returning :: " + jsonAddress);
+            //Debug.WriteLine("getFormattedJSONAddress returning :: " + jsonAddress);
 
             return "{" + jsonAddress + "}";
         }
@@ -579,7 +582,7 @@
                 retVal += this.getFormattedJSONAddress(address, false) + ",";
             }
 
-            Debug.WriteLine("FormatJSONAddresses returning :: " + retVal);
+            //Debug.WriteLine("FormatJSONAddresses returning :: " + retVal);
             return retVal.TrimEnd(',');
         }
 
@@ -647,9 +650,9 @@
                                                FormatJSONName(con),
                                                con.Notes.FirstOrDefault());
 
-            Debug.WriteLine("jsonContact = " + jsonContact);
-
-            return "{" + jsonContact + "}";
+            //Debug.WriteLine("jsonContact = " + jsonContact);
+            // JSON requires new line characters be escaped
+            return "{" + jsonContact.Replace("\n", "\\n") +"}";
         }
     }
 }
diff --git a/templates/standalone/cordovalib/Commands/Device.cs b/templates/standalone/cordovalib/Commands/Device.cs
index 91e8679..498214e 100644
--- a/templates/standalone/cordovalib/Commands/Device.cs
+++ b/templates/standalone/cordovalib/Commands/Device.cs
@@ -62,7 +62,7 @@
             get
             {
                 // TODO: should be able to dynamically read the Cordova version from somewhere...
-                return "2.1.0rc1";
+                return "2.1.0";
             }
         }
 
diff --git a/templates/standalone/cordovalib/Commands/Media.cs b/templates/standalone/cordovalib/Commands/Media.cs
index 56076bd..f5efdac 100644
--- a/templates/standalone/cordovalib/Commands/Media.cs
+++ b/templates/standalone/cordovalib/Commands/Media.cs
@@ -66,7 +66,9 @@
 
                 try
                 {
-                    mediaOptions = JSON.JsonHelper.Deserialize<MediaOptions[]>(options)[0];
+                    string[] optionsString = JSON.JsonHelper.Deserialize<string[]>(options);
+                    mediaOptions = new MediaOptions();
+                    mediaOptions.Id = optionsString[0];
                 }
                 catch (Exception)
                 {
@@ -246,7 +248,6 @@
 
                 AudioPlayer audio = new AudioPlayer(this, mediaOptions.Id);
                 Media.players.Add(mediaOptions.Id, audio);
-
                 DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
 
             }
@@ -300,7 +301,6 @@
                     try
                     {
                         audio.startPlaying(mediaOptions.Src);
-
                         DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
                     }
                     catch (Exception e)