diff --git a/framework/assets/js/network.js b/framework/assets/js/network.js
index b17a3434..f71bad50 100755
--- a/framework/assets/js/network.js
+++ b/framework/assets/js/network.js
@@ -27,7 +27,7 @@ var Connection = function() {
                 // set a timer if still offline at the end of timer send the offline event
                 me._timer = setTimeout(function(){
                     me.type = type;
-                    PhoneGap.fireEvent('offline');
+                    PhoneGap.fireDocumentEvent('offline');
                     me._timer = null;
                     }, me.timeout);
             } else {
@@ -37,7 +37,7 @@ var Connection = function() {
                     me._timer = null;
                 }
                 me.type = type;
-                PhoneGap.fireEvent('online');
+                PhoneGap.fireDocumentEvent('online');
             }
             
             // should only fire this once
diff --git a/framework/assets/js/phonegap.js.base b/framework/assets/js/phonegap.js.base
index f9ec35d2..f171a0cb 100755
--- a/framework/assets/js/phonegap.js.base
+++ b/framework/assets/js/phonegap.js.base
@@ -46,7 +46,9 @@ var PhoneGap = {
         ready: true,
         commands: [],
         timer: null
-    }
+    },
+    documentEventHandler: {},   // Collection of custom document event handlers
+    windowEventHandler: {}      // Collection of custom window event handlers
 };
 
 /**
@@ -381,6 +383,36 @@ document.addEventListener('DOMContentLoaded', function() {
 // Intercept calls to document.addEventListener and watch for deviceready
 PhoneGap.m_document_addEventListener = document.addEventListener;
 
+// Intercept calls to window.addEventListener
+PhoneGap.m_window_addEventListener = window.addEventListener;
+
+/**
+ * Add a custom window event handler.
+ *
+ * @param {String} event            The event name that callback handles
+ * @param {Function} callback       The event handler
+ */
+PhoneGap.addWindowEventHandler = function(event, callback) {
+    PhoneGap.windowEventHandler[event] = callback;
+}
+
+/**
+ * Add a custom document event handler.
+ *
+ * @param {String} event            The event name that callback handles
+ * @param {Function} callback       The event handler
+ */
+PhoneGap.addDocumentEventHandler = function(event, callback) {
+    PhoneGap.documentEventHandler[event] = callback;
+}
+
+/**
+ * Intercept adding document event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
 document.addEventListener = function(evt, handler, capture) {
     var e = evt.toLowerCase();
     if (e === 'deviceready') {
@@ -398,15 +430,52 @@ document.addEventListener = function(evt, handler, capture) {
         if (e === 'backbutton') {
             PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]);
         }
-
+        
+        // If subscribing to an event that is handled by a plugin
+        else if (typeof PhoneGap.documentEventHandler[e] !== "undefined") {
+            if (PhoneGap.documentEventHandler[e](e, handler, true)) {
+                return; // Stop default behavior
+            }
+        }
+        
         PhoneGap.m_document_addEventListener.call(document, evt, handler, capture);
     }
 };
 
+/**
+ * Intercept adding window event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+        
+    // If subscribing to an event that is handled by a plugin
+    if (typeof PhoneGap.windowEventHandler[e] !== "undefined") {
+        if (PhoneGap.windowEventHandler[e](e, handler, true)) {
+            return; // Stop default behavior
+        }
+    }
+        
+    PhoneGap.m_window_addEventListener.call(window, evt, handler, capture);
+};
+
 // Intercept calls to document.removeEventListener and watch for events that
 // are generated by PhoneGap native code
 PhoneGap.m_document_removeEventListener = document.removeEventListener;
 
+// Intercept calls to window.removeEventListener
+PhoneGap.m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Intercept removing document event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
+ */
 document.removeEventListener = function(evt, handler, capture) {
     var e = evt.toLowerCase();
 
@@ -415,18 +484,70 @@ document.removeEventListener = function(evt, handler, capture) {
         PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]);
     }
 
+    // If unsubcribing from an event that is handled by a plugin
+    if (typeof PhoneGap.documentEventHandler[e] !== "undefined") {
+        if (PhoneGap.documentEventHandler[e](e, handler, false)) {
+            return; // Stop default behavior
+        }
+    }
+
     PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
 };
 
 /**
- * Method to fire event from native code
+ * Intercept removing window event listeners and handle our own
+ *
+ * @param {Object} evt
+ * @param {Function} handler
+ * @param capture
  */
-PhoneGap.fireEvent = function(type) {
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+
+    // If unsubcribing from an event that is handled by a plugin
+    if (typeof PhoneGap.windowEventHandler[e] !== "undefined") {
+        if (PhoneGap.windowEventHandler[e](e, handler, false)) {
+            return; // Stop default behavior
+        }
+    }
+
+    PhoneGap.m_window_removeEventListener.call(window, evt, handler, capture);
+};
+
+/**
+ * Method to fire document event
+ *
+ * @param {String} type             The event type to fire
+ * @param {Object} data             Data to send with event
+ */
+PhoneGap.fireDocumentEvent = function(type, data) {
     var e = document.createEvent('Events');
     e.initEvent(type);
+    if (data) {
+        for (var i in data) {
+            e[i] = data[i];
+        }
+    }
     document.dispatchEvent(e);
 };
 
+/**
+ * Method to fire window event
+ *
+ * @param {String} type             The event type to fire
+ * @param {Object} data             Data to send with event
+ */
+PhoneGap.fireWindowEvent = function(type, data) {
+    var e = document.createEvent('Events');
+    e.initEvent(type);
+    if (data) {
+        for (var i in data) {
+            e[i] = data[i];
+        }
+    }
+    window.dispatchEvent(e);
+};
+
 /**
  * If JSON not included, use our own stringify. (Android 1.6)
  * The restriction on ours is that it must be an array of simple types.
diff --git a/framework/src/com/phonegap/DroidGap.java b/framework/src/com/phonegap/DroidGap.java
old mode 100644
new mode 100755
index d9ecccd9..f9aca61c
--- a/framework/src/com/phonegap/DroidGap.java
+++ b/framework/src/com/phonegap/DroidGap.java
@@ -1256,7 +1256,7 @@ public class DroidGap extends PhonegapActivity {
 
     		// If back key is bound, then send event to JavaScript
     		if (this.bound) {
-    			this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');");
+    			this.appView.loadUrl("javascript:PhoneGap.fireDocumentEvent('backbutton');");
     			return true;
     		}
 
@@ -1278,13 +1278,13 @@ public class DroidGap extends PhonegapActivity {
 
     	// If menu key
     	else if (keyCode == KeyEvent.KEYCODE_MENU) {
-    		this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');");
+    		this.appView.loadUrl("javascript:PhoneGap.fireDocumentEvent('menubutton');");
     		return true;
     	}
 
     	// If search key
     	else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
-    		this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');");
+    		this.appView.loadUrl("javascript:PhoneGap.fireDocumentEvent('searchbutton');");
     		return true;
     	}
 
@@ -1478,13 +1478,13 @@ public class DroidGap extends PhonegapActivity {
     	        // gone away.
     	        else if (height > oldHeight) {
     	        	Log.d(LOG_TAG, "Throw hide keyboard event");
-    	        	callbackServer.sendJavascript("PhoneGap.fireEvent('hidekeyboard');");
+    	        	callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('hidekeyboard');");
     	        } 
     	        // If the height as gotten smaller then we will assume the soft keyboard has 
     	        // been displayed.
     	        else if (height < oldHeight) {
     	        	Log.d(LOG_TAG, "Throw show keyboard event");
-    	        	callbackServer.sendJavascript("PhoneGap.fireEvent('showkeyboard');");
+    	        	callbackServer.sendJavascript("PhoneGap.fireDocumentEvent('showkeyboard');");
     	        }
 
     	        // Update the old height for the next event