Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-js
diff --git a/build/packager.js b/build/packager.js
index 6569020..db00fd4 100644
--- a/build/packager.js
+++ b/build/packager.js
@@ -37,7 +37,9 @@
     
     copyProps(modules, collectFiles(path.join('lib', platform)))
 
-    var output = []
+    var output = [];
+	
+	output.push("// File generated at :: "  + new Date() + "\n");
 
     // write header     
     output.push('/*\n' + getContents('LICENSE-for-js-file.txt') + '\n*/')
@@ -130,12 +132,17 @@
 //------------------------------------------------------------------------------
 function writeModule(oFile, fileName, moduleId, debug) {
     var contents = '\n' + getContents(fileName, 'utf8') + '\n'
+
+	// Windows fix, '\' is an escape, but defining requires '/' -jm
+    moduleId = path.join('cordova', moduleId).split("\\").join("/");
+	
+	
     
-    moduleId = path.join('cordova', moduleId)
+    var signature = 'function(require, exports, module)';
+	
+	
     
-    var signature = 'function(require, exports, module)'
-    
-    contents = 'define("' + moduleId + '", ' + signature + ' {' + contents + '})\n'
+    contents = 'define("' + moduleId + '", ' + signature + ' {' + contents + '});\n'
 
     writeContents(oFile, fileName, contents, debug)    
 }
diff --git a/lib/common/channel.js b/lib/common/channel.js
index baf08b6..bb12b62 100755
--- a/lib/common/channel.js
+++ b/lib/common/channel.js
@@ -1,5 +1,38 @@
 /**
- * Custom pub-sub channel that can have functions subscribed to it
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded         Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady              Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady             Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady         Internal event fired when device properties are available.
+ * onCordovaConnectionReady   Internal event fired when the connection property has been set.
+ * onDeviceReady              User event fired to indicate that Cordova is ready
+ * onResume                   User event fired to indicate a start/resume lifecycle event
+ * onPause                    User event fired to indicate a pause lifecycle event
+ * onDestroy                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The only Cordova events that user code should register for are:
+ *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ *      pause                 App has moved to background
+ *      resume                App has returned to foreground
+ *
+ * Listeners can be registered as:
+ *      document.addEventListener("deviceready", myDeviceReadyListener, false);
+ *      document.addEventListener("resume", myResumeListener, false);
+ *      document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ *      window.onload
+ *      window.onunload
+ *
+ */
+
+/**
+ * Channel
  * @constructor
  * @param type  String the channel name
  * @param opts  Object options to pass into the channel, currently
@@ -169,8 +202,7 @@
     return true;
 };
 
-//HACK: defining them here so they are ready super fast!
-
+// defining them here so they are ready super fast!
 // DOM event that is received when the web page is loaded and parsed.
 channel.create('onDOMContentLoaded');
 
diff --git a/lib/common/plugin/Acceleration.js b/lib/common/plugin/Acceleration.js
index f02d24b..834ad01 100644
--- a/lib/common/plugin/Acceleration.js
+++ b/lib/common/plugin/Acceleration.js
@@ -1,8 +1,8 @@
-var Acceleration = function(x, y, z) {
+var Acceleration = function(x, y, z, timestamp) {
   this.x = x;
   this.y = y;
   this.z = z;
-  this.timestamp = new Date().getTime();
+  this.timestamp = timestamp || (new Date()).getTime();
 };
 
 module.exports = Acceleration;
diff --git a/lib/common/plugin/Camera.js b/lib/common/plugin/Camera.js
index db6c224..d104707 100644
--- a/lib/common/plugin/Camera.js
+++ b/lib/common/plugin/Camera.js
@@ -80,9 +80,26 @@
     if (typeof options.mediaType == "number") {
         mediaType = options.mediaType;
     }
-    // TODO: enable allow edit?
+    var allowEdit = false;
+    if (typeof options.allowEdit == "boolean") {
+    	allowEdit = options.allowEdit;
+    } else if (typeof options.allowEdit == "number") {
+    	allowEdit = options.allowEdit <= 0 ? false : true;
+    }
+    var correctOrientation = false;
+    if (typeof options.correctOrientation == "boolean") {
+    	correctOrientation = options.correctOrientation;
+    } else if (typeof options.correctOrientation == "number") {
+    	correctOrientation = options.correctOrientation <=0 ? false : true;
+    }
+    var saveToPhotoAlbum = false;
+	if (typeof options.saveToPhotoAlbum == "boolean") {
+    	saveToPhotoAlbum = options.saveToPhotoAlbum;
+    } else if (typeof options.saveToPhotoAlbum == "number") {
+    	saveToPhotoAlbum = options.saveToPhotoAlbum <=0 ? false : true;
+    }
 
-    exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, mediaType]);
+    exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, mediaType, allowEdit, correctOrientation, saveToPhotoAlbum]);
 }
 
 module.exports = cameraExport;
diff --git a/lib/common/plugin/CompassHeading.js b/lib/common/plugin/CompassHeading.js
index 2336beb..77bd465 100644
--- a/lib/common/plugin/CompassHeading.js
+++ b/lib/common/plugin/CompassHeading.js
@@ -2,7 +2,7 @@
   this.magneticHeading = (magneticHeading !== undefined ? magneticHeading : null);
   this.trueHeading = (trueHeading !== undefined ? trueHeading : null);
   this.headingAccuracy = (headingAccuracy !== undefined ? headingAccuracy : null);
-  this.timestamp = (timestamp !== undefined ? new Date(timestamp) : new Date());
+  this.timestamp = (timestamp !== undefined ? timestamp : new Date().getTime());
 };
 
 module.exports = CompassHeading;
diff --git a/lib/common/plugin/FileTransfer.js b/lib/common/plugin/FileTransfer.js
index a417f5c..6749a80 100644
--- a/lib/common/plugin/FileTransfer.js
+++ b/lib/common/plugin/FileTransfer.js
@@ -14,8 +14,9 @@
 * @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
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
 */
-FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) {
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
     // check for options
     var fileKey = null;
     var fileName = null;
@@ -37,7 +38,7 @@
         }
     }
 
-    exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]);
+    exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]);
 };
 
 /**
diff --git a/lib/common/plugin/compass.js b/lib/common/plugin/compass.js
index 1685f2d..fae9d27 100644
--- a/lib/common/plugin/compass.js
+++ b/lib/common/plugin/compass.js
@@ -12,7 +12,7 @@
          * getting the heading data.
          * @param {CompassOptions} options The options for getting the heading data (not used).
          */
-        getCurrentHeading:function(successCallback, errorCallback) {
+        getCurrentHeading:function(successCallback, errorCallback, options) {
             // successCallback required
             if (typeof successCallback !== "function") {
               console.log("Compass Error: successCallback is not a function");
@@ -35,7 +35,7 @@
             }
             
             // Get heading
-            exec(win, fail, "Compass", "getHeading", []);
+            exec(win, fail, "Compass", "getHeading", [options]);
         },
 
         /**
@@ -45,11 +45,13 @@
          * @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.
+         * such as timeout and the frequency of the watch. For iOS, filter parameter
+         * specifies to watch via a distance filter rather than time.
          */
         watchHeading:function(successCallback, errorCallback, options) {
             // Default interval (100 msec)
             var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100;
+            var filter = (options !== undefined && options.filter !== undefined) ? options.filter : 0;
 
             // successCallback required
             if (typeof successCallback !== "function") {
@@ -63,13 +65,18 @@
               return;
             }
 
-            // Start watch timer to get headings
             var id = utils.createUUID();
-
-            timers[id] = window.setInterval(function() {
-                compass.getCurrentHeading(successCallback, errorCallback);
-            }, frequency);
-
+			if (filter > 0) {
+				// is an iOS request for watch by filter, no timer needed
+				timers[id] = "iOS";
+				compass.getCurrentHeading(successCallback, errorCallback, options);
+			} else {
+				// Start watch timer to get headings
+            	timers[id] = window.setInterval(function() {
+                	compass.getCurrentHeading(successCallback, errorCallback);
+            	}, frequency);
+			}
+				
             return id;
         },
 
@@ -80,11 +87,15 @@
         clearWatch:function(id) {
             // Stop javascript timer & remove from timer list
             if (id && timers[id]) {
-              clearInterval(timers[id]);
-              delete timers[id];
+            	if (timers[id] != "iOS") {
+              		clearInterval(timers[id]);
+              	} else {
+            		// is iOS watch by filter so call into device to stop
+            		exec(null, null, "Compass", "stopHeading", []);
+            	}
+	            delete timers[id];
             }
         }
-        // TODO: add the filter-based iOS-only methods
     };
 
 module.exports = compass;
diff --git a/lib/cordova.js b/lib/cordova.js
index ac55943..e155b4f 100644
--- a/lib/cordova.js
+++ b/lib/cordova.js
@@ -75,6 +75,13 @@
   return event;
 }
 
+if(typeof window.console === "undefined")
+{
+	window.console = { 
+		log:function(){}
+	};
+}
+
 var cordova = {
     define:define,
     require:require,
@@ -94,6 +101,15 @@
       delete documentEventHandlers[event];
     },
     /**
+     * Retreive original event handlers that were replaced by Cordova
+     *
+     * @return object
+     */
+    getOriginalHandlers: function() {
+        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+    },
+    /**
      * Method to fire event from native code
      */
     fireDocumentEvent: function(type, data) {
diff --git a/lib/ios/plugin/ios/Entry.js b/lib/ios/plugin/ios/Entry.js
index 230da8a..1be783a 100644
--- a/lib/ios/plugin/ios/Entry.js
+++ b/lib/ios/plugin/ios/Entry.js
@@ -3,5 +3,9 @@
         // TODO: refactor path in a cross-platform way so we can eliminate 
         // these kinds of platform-specific hacks.
         return "file://localhost" + this.fullPath;
+    },
+    toURI: function() {
+    	console.log("DEPRECATED: Update your code to use 'toURL'");
+    	return "file://localhost" + this.fullPath;
     }
-}
+};
diff --git a/lib/wp7/exec.js b/lib/wp7/exec.js
index 78eef40..6b1f929 100644
--- a/lib/wp7/exec.js
+++ b/lib/wp7/exec.js
@@ -13,45 +13,209 @@
  * @param {String} service      The name of the service to use
  * @param {String} action       Action to be run in cordova
  * @param {String[]} [args]     Zero or more arguments to pass to the method
- */
 
-module.exports = function(success, fail, service, action, args) {
+ */
+ 
+ /* this will become a programmatic way to gen the named args ... TODO: -jm
+var NamedArgs = 
+{
+	File:{
+		getFileMetadata:["fullPath"],
+		readAsText:["fileName","encoding"],
+		readAsDataURL:["fileName"],
+		getDirectory:["fullPath","path","options"],
+		removeRecursively:["fullPath"],
+		getFile:["fullPath","path","options"],
+		readEntries:["fullPath"],
+		write:["fileName","data","position"],
+		truncate:["fileName","size"]
+	}
+}
+*/
+ 
+var MonkeyPatch = 
+{
+	File:
+	{
+		"getFileMetadata":function(arg)
+		{
+			return  {fullPath:arg[0]};
+		},
+		"readAsText":function(arg)
+		{ //[this.fileName, enc]
+			return {fileName:arg[0],encoding:arg[1]};
+		},
+		"readAsDataURL":function(arg)
+		{
+			return {fileName:arg[0]};
+		},
+		"getDirectory":function(arg)
+		{
+			return {fullPath:arg[0],path:arg[1],options:arg[2]};
+		},
+		"removeRecursively":function(arg)
+		{ 
+			return {fullPath:arg[0]};
+		},
+		"getFile":function(arg)
+		{
+			return {fullPath:arg[0],path:arg[1],options:arg[2]};
+		},
+		"readEntries":function(arg)
+		{
+			return {fullPath:arg[0]};
+		},
+		"write":function(arg)
+		{
+			return {fileName:arg[0],data:arg[1],position:arg[2]};
+		},
+		"truncate":function(arg)
+		{
+			return {fileName:arg[0],size:arg[1]};
+		}
+
+	},
+	FileTransfer: 
+	{
+		// [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]
+		"upload":function(arg)
+		{
+			// note, chuncked mode is not supported in WP7 currently
+			return {filePath:arg[0],server:arg[1],fileKey:arg[2],fileName:arg[3],mimeType:arg[4],params:arg[5],debug:arg[6]};
+		}
+	},
+	Contacts:
+	{
+		"remove":function(arg) // actually caught by our other case inside exec
+		{
+			return arg[0];
+		},
+		"save":function(arg) // actually caught by our other case inside exec
+		{
+			return arg[0];
+		},
+		"search":function(arg)
+		{
+			return {fields:arg[0],options:arg[1]};
+		}
+	},
+	Capture:
+	{
+		captureAudio:function(arg)// actually caught by our other case inside exec
+		{
+			return arg[0];	
+		},
+		captureVideo:function(arg)// actually caught by our other case inside exec
+		{
+			return arg[0];	
+		},
+		captureImage:function(arg)// actually caught by our other case inside exec
+		{
+			return arg[0];	
+		}
+	},
+	Media:
+	{
+		create:function(arg)
+		{
+			return {id:arg[0],src:arg[1]};
+		},
+		startPlayingAudio:function(arg)
+		{
+			return {id:arg[0],src:arg[1],milliseconds:arg[2]};
+		},
+		stopPlayingAudio:function(arg)
+		{
+			return {id:arg[0]};
+		},
+		seekToAudio:function(arg)
+		{
+			return {id:arg[0],milliseconds:arg[1]};
+		},
+		pausePlayingAudio:function(arg)
+		{
+			return {id:arg[0]};
+		},
+		getCurrentPositionAudio:function(arg)
+		{
+			return {id:arg[0]};
+		},
+		startRecordingAudio:function(arg)
+		{
+			return {id:arg[0],src:arg[1]};
+		},
+		stopRecordingAudio:function(arg)
+		{
+			return {id:arg[0]};
+		},
+		release:function(arg)
+		{
+			return {id:arg[0]};
+		},
+		setVolume:function(arg)
+		{
+			return {id:arg[0],volume:arg[1]};
+		}
+	},
+	Notification:
+	{
+		"alert":function(arg)
+		{
+			return {message:arg[0],title:arg[1],buttonLabel:arg[2]};
+		},
+		"confirm":function(arg)
+		{
+			return {message:arg[0],title:arg[1],buttonLabel:arg[2]};
+		}
+	},
+	Camera:
+	{
+		"takePicture":function(arg)
+		{
+			//"takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]);
+			return {quality:arg[0],destinationType:arg[1],sourceType:arg[2],targetWidth:arg[3],targetHeight:arg[4],encodingType:arg[5]};
+		}
+	}
+	
+};
+
+module.exports = function(success, fail, service, action, args) 
+{
+
     var callbackId = service + cordova.callbackId++;
-    if (typeof success == "function" || typeof fail == "function") {
+    if (typeof success == "function" || typeof fail == "function") 
+	{
         cordova.callbacks[callbackId] = {success:success, fail:fail};
     }
     // generate a new command string, ex. DebugConsole/log/DebugConsole23/{"message":"wtf dude?"}
+	
+	 if(MonkeyPatch[service] && MonkeyPatch[service][action])
+	 {
+		//console.log("MonkeyPatching " + service + "." + action);
+		args =  MonkeyPatch[service][action](args);
+	 }
+	 else if(args && args.length && args.length == 1)
+	 {
+		 args = args[0]; 
+	 }
+	
      var command = service + "/" + action + "/" + callbackId + "/" + JSON.stringify(args);
      // pass it on to Notify
-     window.external.Notify(command);
+	 try
+	 {
+		 if(window.external)
+		 {
+			 window.external.Notify(command);
+		 }
+		 else
+		 {
+			console.log("window.external not available :: command=" + command);  
+		 }
+	 }
+	 catch(e)
+	 {
+		 console.log("Exception calling native with command :: " + command + " :: exception=" + e); 
+	 }
 };
 
-// TODO: is this what native side invokes?
-// if so pluginize under plugin/wp7
-cordovaCommandResult = function(status,callbackId,args,cast) {
-    if(status === "backbutton") {
 
-        cordova.fireEvent(document,"backbutton");
-        return "true";
-
-    } else if(status === "resume") {
-
-        cordova.onResume.fire();
-        return "true";
-
-    } else if(status === "pause") {
-
-        cordova.onPause.fire();
-        return "true";  
-    }
-    
-    var safeStatus = parseInt(status, 10);
-    if(safeStatus === cordova.callbackStatus.NO_RESULT ||
-       safeStatus === cordova.callbackStatus.OK) {
-        cordova.CallbackSuccess(callbackId,args,cast);
-    }
-    else
-    {
-        cordova.CallbackError(callbackId,args,cast);
-    }
-};
diff --git a/lib/wp7/platform.js b/lib/wp7/platform.js
index f2d74b4..ff61830 100644
--- a/lib/wp7/platform.js
+++ b/lib/wp7/platform.js
@@ -1,16 +1,275 @@
+var cordova = require('cordova'),
+      exec = require('cordova/exec');
+
 module.exports = {
     id: "wp7",
-    initialize:function() {},
+    initialize:function() {
+
+console.log("window.CordovaMediaonStatus = " + window.CordovaMediaonStatus);
+
+if(!window.localStorage)
+{(function()
+{
+	
+	
+
+    var DOMStorage = function(type)
+    {
+        // default type is local
+        if(type == "sessionStorage")
+        {
+            this._type = type;
+        }
+        Object.defineProperty( this, "length", 
+        {
+            configurable: true,
+            get: function(){ return this.getLength() }
+        });
+
+    };
+
+    DOMStorage.prototype = 
+    {
+        _type:"localStorage",
+        _result:null,
+        keys:null,
+    
+        onResult:function(key,valueStr)
+        {
+            if(!this.keys)
+            {
+                this.keys = [];
+            }
+            this._result = valueStr;
+        },
+
+        onKeysChanged:function(jsonKeys)
+        {
+            this.keys = JSON.parse(jsonKeys);
+
+            var key;
+            for(var n = 0,len =this.keys.length; n < len; n++)
+            {
+                key = this.keys[n];
+                if(!this.hasOwnProperty(key))
+                {
+                    Object.defineProperty( this, key, 
+                    {
+
+                        configurable: true,
+                        get: function(){ return this.getItem(key); },
+                        set: function(val){ return this.setItem(key,val); }
+                    });
+                }
+            }
+
+        },
+
+        initialize:function()
+        {
+            window.external.Notify("DOMStorage/" + this._type + "/load/keys");
+        },
+
+    /*
+        The length attribute must return the number of key/value pairs currently present in the list associated with the object.
+    */
+        getLength:function()
+        {
+            if(!this.keys)
+            {
+                this.initialize();
+            }
+            return this.keys.length;
+        },
+
+    /*
+        The key(n) method must return the name of the nth key in the list. 
+        The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn't change. 
+        (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.) 
+        If n is greater than or equal to the number of key/value pairs in the object, then this method must return null. 
+    */
+        key:function(n)
+        {
+            if(!this.keys)
+            {
+                this.initialize();
+            }
+
+            if(n >= this.keys.length)
+            {
+                return null;
+            }
+            else
+            {
+                return this.keys[n];
+            }
+        },
+
+    /*
+        The getItem(key) method must return the current value associated with the given key. 
+        If the given key does not exist in the list associated with the object then this method must return null.
+    */
+        getItem:function(key)
+        {
+            if(!this.keys)
+            {
+                this.initialize();
+            }
+
+            var retVal = null;
+            if(this.keys.indexOf(key) > -1)
+            {
+                window.external.Notify("DOMStorage/" + this._type + "/get/" + key);
+                retVal = this._result;
+                this._result = null;
+            }
+            return retVal;
+        },
+    /*
+        The setItem(key, value) method must first check if a key/value pair with the given key already exists 
+        in the list associated with the object.
+        If it does not, then a new key/value pair must be added to the list, with the given key and with its value set to value.
+        If the given key does exist in the list, then it must have its value updated to value.
+        If it couldn't set the new value, the method must raise an QUOTA_EXCEEDED_ERR exception. 
+        (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)
+    */
+        setItem:function(key,value)
+        {
+            if(!this.keys)
+            {
+                this.initialize();
+            }
+            window.external.Notify("DOMStorage/" + this._type + "/set/" + key + "/" + value);
+        },
+
+    /*
+        The removeItem(key) method must cause the key/value pair with the given key to be removed from the list 
+        associated with the object, if it exists. 
+        If no item with that key exists, the method must do nothing.
+    */
+        removeItem:function(key)
+        {
+            if(!this.keys)
+            {
+                this.initialize();
+            }
+            var index = this.keys.indexOf(key);
+            if(index > -1)
+            {
+                this.keys.splice(index,1);
+                // TODO: need sanity check for keys ? like 'clear','setItem', ...
+                window.external.Notify("DOMStorage/" + this._type + "/remove/" + key);
+                delete this[key];
+            }
+            
+        },
+
+    /*
+        The clear() method must atomically cause the list associated with the object to be emptied of all 
+        key/value pairs, if there are any. 
+        If there are none, then the method must do nothing.
+    */
+        clear:function()
+        {
+            if(!this.keys)
+            {
+                this.initialize();
+            }
+
+            for(var n=0,len=this.keys.length; n < len;n++)
+            {
+                // TODO: do we need a sanity check for keys ? like 'clear','setItem', ...
+                delete this[this.keys[n]];
+            }
+            this.keys = [];
+            window.external.Notify("DOMStorage/" + this._type + "/clear/");
+        }
+    };
+
+    // initialize DOMStorage
+    
+    Object.defineProperty( window, "localStorage", 
+    {
+        writable: false,
+        configurable: false,
+        value:new DOMStorage("localStorage")
+    });
+    window.localStorage.initialize();
+
+    Object.defineProperty( window, "sessionStorage", 
+    {
+        writable: false,
+        configurable: false,
+        value:new DOMStorage("sessionStorage")
+    });
+    window.sessionStorage.initialize();
+
+
+})();};
+
+
+		// INject a lsitener for the backbutton, and tell native to override the flag (true/false) when we have 1 or more, or 0, listeners
+    var backButtonChannel = cordova.addDocumentEventHandler('backbutton', {
+      onSubscribe:function() {
+        if (this.numHandlers === 1) {
+            exec(null, null, "CoreEvents", "overridebackbutton", [true]);
+        }
+      },
+      onUnsubscribe:function() {
+        if (this.numHandlers === 0) {
+          exec(null, null, "CoreEvents", "overridebackbutton", [false]);
+        }
+      }
+    });
+	},
     objects: {
+		CordovaCommandResult: {
+			path:"cordova/plugin/wp7/CordovaCommandResult"
+		},
+		CordovaMediaonStatus: {
+			path:"cordova/plugin/wp7/CordovaMediaonStatus"
+		},
+		requestFileSystem: {
+			path:"cordova/plugin/wp7/requestFileSystem"
+		},
+		resolveLocalFileSystemURI: {
+			path:"cordova/plugin/wp7/resolveLocalFileSystemURI"
+		},		
+		File: { 
+            path: "cordova/plugin/File"
+        },
+        FileReader: { 
+            path: "cordova/plugin/FileReader"
+        },
+        FileError: { 
+            path: "cordova/plugin/FileError"
+        },
+        MediaError: {
+            path: "cordova/plugin/MediaError"
+        },
         navigator: {
             children: {
                 device: {
-                    path: "cordova/plugin/wp7/device"
-                }
+                    path: "cordova/plugin/wp7/device",
+					children:{
+						capture:{
+							path:"cordova/plugin/capture"	
+						}
+					}
+                },
+				console: {
+					path: "cordova/plugin/wp7/console"
+					
+				}
             }
         },
-        device: {
-            path: 'cordova/plugin/wp7/device'
+        device:{
+          path:"cordova/plugin/wp7/device"
+        },
+        console:{
+          path: "cordova/plugin/wp7/console"
         }
     }
 };
+
+
diff --git a/lib/wp7/plugin/wp7/CordovaCommandResult.js b/lib/wp7/plugin/wp7/CordovaCommandResult.js
new file mode 100644
index 0000000..9b7f390
--- /dev/null
+++ b/lib/wp7/plugin/wp7/CordovaCommandResult.js
@@ -0,0 +1,54 @@
+
+var cordova = require('cordova');
+var channel = require('cordova/channel');
+
+// singular WP7 callback function attached to window, status is used to determin if it is a success or error
+module.exports = function(status,callbackId,args,cast) {
+
+	if(status === "backbutton") {
+		cordova.fireDocumentEvent("backbutton");
+		return "true";
+	} 
+	else if(status === "resume") {
+		channel.onResume.fire();
+		return "true";
+	} 
+	else if(status === "pause") {
+		channel.onPause.fire();
+		return "true";  
+	}
+	
+	var parsedArgs;
+	try
+	{
+		parsedArgs = JSON.parse(args);
+		
+	}
+	catch(ex)
+	{
+		console.log("Parse error in CordovaCommandResult :: " + ex);
+		return;
+	}
+	
+	try
+	{
+		// For some commands, the message is a JSON encoded string
+		// and other times, it is just a string, the try/catch handles the 
+		// case where message was indeed, just a string.
+		parsedArgs.message = JSON.parse(parsedArgs.message);
+	}
+	catch(ex)
+	{
+
+	}
+	var safeStatus = parseInt(status, 10);
+	if(safeStatus === cordova.callbackStatus.NO_RESULT ||
+	   safeStatus === cordova.callbackStatus.OK) {
+		cordova.callbackSuccess(callbackId,parsedArgs,cast);
+	}
+	else {
+		cordova.callbackError(callbackId,parsedArgs,cast);
+	}
+};
+
+
diff --git a/lib/wp7/plugin/wp7/CordovaMediaonStatus.js b/lib/wp7/plugin/wp7/CordovaMediaonStatus.js
new file mode 100644
index 0000000..b77415d
--- /dev/null
+++ b/lib/wp7/plugin/wp7/CordovaMediaonStatus.js
@@ -0,0 +1,9 @@
+
+var cordova = require('cordova');
+
+module.exports = function(args) {
+	
+	console.log("media on status :: " + args);
+	//var res = JSON.parse(args);
+    //require("cordova/media").onStatus(res.id, res.msg, res.value);
+};
diff --git a/lib/wp7/plugin/wp7/console.js b/lib/wp7/plugin/wp7/console.js
new file mode 100644
index 0000000..2488f81
--- /dev/null
+++ b/lib/wp7/plugin/wp7/console.js
@@ -0,0 +1,19 @@
+
+var exec = require('cordova/exec'),
+    channel = require('cordova/channel');
+var cordova = require("cordova");
+
+var debugConsole = {
+	log:function(msg){
+		exec(null,null,"DebugConsole","log",msg);
+	},
+	warn:function(msg){
+		exec(null,null,"DebugConsole","warn",msg);
+	},
+	error:function(msg){
+		exec(null,null,"DebugConsole","error",msg);
+	}	
+};
+
+module.exports = debugConsole;
+
diff --git a/lib/wp7/plugin/wp7/device.js b/lib/wp7/plugin/wp7/device.js
index b51fe93..8940115 100644
--- a/lib/wp7/plugin/wp7/device.js
+++ b/lib/wp7/plugin/wp7/device.js
@@ -1,26 +1,60 @@
+var channel = require('cordova/channel'),
+    utils = require('cordova/utils'),
+    exec = require('cordova/exec');
+
 /**
- * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
  * phone, etc.
  * @constructor
  */
-var Device = function() 
-{
+function Device() {
+    this.available = false;
     this.platform = null;
-    this.version  = null;
-    this.name     = null;
-    this.phonegap = null;
-    this.uuid     = null;
-    try {      
-	this.platform = DeviceInfo.platform;
-	this.version  = DeviceInfo.version;
-	this.name     = DeviceInfo.name;
-	this.phonegap = DeviceInfo.gap;
-	this.uuid     = DeviceInfo.uuid;
-    } 
-    catch(e) {
-        // TODO: 
+    this.version = null;
+    this.name = null;
+    this.uuid = null;
+    this.cordova = null;
+
+    var me = this;
+
+    channel.onCordovaReady.subscribeOnce(function() {
+        me.getInfo(function(info) {
+            me.available = true;
+            me.platform = info.platform;
+            me.version = info.version;
+            me.name = info.name;
+            me.uuid = info.uuid;
+            me.cordova = info.cordova;
+            channel.onCordovaInfoReady.fire();
+        },function(e) {
+            me.available = false;
+            utils.alert("[ERROR] Error initializing Cordova: " + e);
+        });
+    });
+}
+
+/**
+ * Get device info
+ *
+ * @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)
+ */
+Device.prototype.getInfo = function(successCallback, errorCallback) {
+
+    // successCallback required
+    if (typeof successCallback !== "function") {
+        console.log("Device Error: successCallback is not a function");
+        return;
     }
-    this.available = PhoneGap.available = !!this.uuid;
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Device Error: errorCallback is not a function");
+        return;
+    }
+
+    // Get info
+    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
 };
 
 module.exports = new Device();
diff --git a/lib/wp7/plugin/wp7/requestFileSystem.js b/lib/wp7/plugin/wp7/requestFileSystem.js
new file mode 100644
index 0000000..a22d4a3
--- /dev/null
+++ b/lib/wp7/plugin/wp7/requestFileSystem.js
@@ -0,0 +1,42 @@
+
+var FileError = require('cordova/plugin/FileError'),
+    FileSystem = require('cordova/plugin/FileSystem'),
+    exec = require('cordova/exec');
+
+/**
+ * Request a file system in which to store application data.
+ * @param type  local file system type
+ * @param size  indicates how much storage space, in bytes, the application expects to need
+ * @param successCallback  invoked with a FileSystem object
+ * @param errorCallback  invoked if error occurs retrieving file system
+ */
+var requestFileSystem = function(type, size, successCallback, errorCallback) {
+	// wp7 custom imp
+    var fail = function(code) {
+        if (typeof errorCallback === 'function') {
+            errorCallback(new FileError(code));
+        }
+    };
+
+    if (type < 0 || type > 3) {
+        fail(FileError.SYNTAX_ERR);
+    } else {
+        // if successful, return a FileSystem object
+        var success = function(file_system) {
+            if (file_system) {
+                if (typeof successCallback === 'function') {
+                    // grab the name and root from the file system object
+                    var result = new FileSystem(file_system.name, file_system.root);
+                    successCallback(result);
+                }
+            }
+            else {
+                // no FileSystem object returned
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+        exec(success, fail, "File", "requestFileSystem", {type:type,size:size});
+    }
+};
+
+module.exports = requestFileSystem;
\ No newline at end of file
diff --git a/lib/wp7/plugin/wp7/resolveLocalFileSystemURI.js b/lib/wp7/plugin/wp7/resolveLocalFileSystemURI.js
new file mode 100644
index 0000000..f00c339
--- /dev/null
+++ b/lib/wp7/plugin/wp7/resolveLocalFileSystemURI.js
@@ -0,0 +1,37 @@
+
+
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry'),
+    FileEntry = require('cordova/plugin/FileEntry'),
+    exec = require('cordova/exec');
+	
+module.exports = function(uri, successCallback, errorCallback) {
+    // error callback
+    var fail = function(error) {
+        if (typeof errorCallback === 'function') {
+            errorCallback(new FileError(error));
+        }
+    };
+    // if successful, return either a file or directory entry
+    var success = function(entry) {
+        var result;
+
+        if (entry) {
+            if (typeof successCallback === 'function') {
+                // create appropriate Entry object
+                result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath);
+                try {
+                    successCallback(result);
+                }
+                catch (e) {
+                    console.log('Error invoking callback: ' + e);
+                }
+            }
+        }
+        else {
+            // no Entry object returned
+            fail(FileError.NOT_FOUND_ERR);
+        }
+    };
+
+    exec(success, fail, "File", "resolveLocalFileSystemURI", {uri:uri});
+};
\ No newline at end of file
diff --git a/package.json b/package.json
index fd07dd1..86a4f6f 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     },
     {
       "name":"Jesse MacFadyen",
-      "email":""
+      "email":"purplecabbage@apache.org"
     },
     {
       "name":"Bryce Curtis",
diff --git a/test/test.compass.js b/test/test.compass.js
index f7d4a75..5d0b8cc 100644
--- a/test/test.compass.js
+++ b/test/test.compass.js
@@ -3,7 +3,9 @@
         utils = require('cordova/utils'),
         exec = require('cordova/exec');
 
-    exec.reset();
+    beforeEach(function() {
+        exec.reset();
+    });
 
     describe("when getting the current heading", function () {
         it("logs an error and doesn't call exec when no success callback given", function () {
@@ -33,7 +35,7 @@
                 f = function () {};
 
             compass.getCurrentHeading(s, f);
-            expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Compass", "getHeading", []);
+            expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Compass", "getHeading", [undefined]);
         });
     });
 
@@ -96,7 +98,7 @@
 
             //exec the interval callback!
             window.setInterval.mostRecentCall.args[0]();
-            expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Compass", "getHeading", []);
+            expect(exec).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), "Compass", "getHeading", [undefined]);
         });
     });