You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by pm...@apache.org on 2012/03/22 13:50:23 UTC

[11/12] 2nd try at file refactoring

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/FileUploadOptions.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/FileUploadOptions.js b/lib0/plugin/FileUploadOptions.js
new file mode 100644
index 0000000..ff16661
--- /dev/null
+++ b/lib0/plugin/FileUploadOptions.js
@@ -0,0 +1,16 @@
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String}   Name of file request parameter.
+ * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object}    Object with key: value params to send to the server.
+ */
+var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
+    this.fileKey = fileKey || null;
+    this.fileName = fileName || null;
+    this.mimeType = mimeType || null;
+    this.params = params || null;
+};
+
+module.exports = FileUploadOptions;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/FileUploadResult.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/FileUploadResult.js b/lib0/plugin/FileUploadResult.js
new file mode 100644
index 0000000..740babd
--- /dev/null
+++ b/lib0/plugin/FileUploadResult.js
@@ -0,0 +1,11 @@
+/**
+ * FileUploadResult
+ * @constructor
+ */
+var FileUploadResult = function() {
+    this.bytesSent = 0;
+    this.responseCode = null;
+    this.response = null;
+};
+
+module.exports = FileUploadResult;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/FileWriter.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/FileWriter.js b/lib0/plugin/FileWriter.js
new file mode 100644
index 0000000..e0e5626
--- /dev/null
+++ b/lib0/plugin/FileWriter.js
@@ -0,0 +1,253 @@
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError');
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function(file) {
+    this.fileName = "";
+    this.length = 0;
+    if (file) {
+        this.fileName = file.fullPath || file;
+        this.length = file.size || 0;
+    }
+    // default is to write at the beginning of the file
+    this.position = 0;
+
+    this.readyState = 0; // EMPTY
+
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onwritestart = null;   // When writing starts
+    this.onprogress = null;     // While writing the file, and reporting partial file data
+    this.onwrite = null;        // When the write has successfully completed.
+    this.onwriteend = null;     // When the request has completed (either in success or failure).
+    this.onabort = null;        // When the write has been aborted. For instance, by invoking the abort() method.
+    this.onerror = null;        // When the write has failed (see errors).
+};
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function() {
+    // check for invalid state
+    if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // set error
+    this.error = new FileError(FileError.ABORT_ERR);
+
+    this.readyState = FileWriter.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === "function") {
+        this.onabort(new ProgressEvent("abort", {"target":this}));
+    }
+
+    // If write end callback
+    if (typeof this.onwriteend === "function") {
+        this.onwriteend(new ProgressEvent("writeend", {"target":this}));
+    }
+};
+
+/**
+ * Writes data to the file
+ *
+ * @param text to be written
+ */
+FileWriter.prototype.write = function(text) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        me.onwritestart(new ProgressEvent("writestart", {"target":me}));
+    }
+
+    // Write file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // position always increases by bytes written because file would be extended
+            me.position += r;
+            // The length of the file is now where we are done writing.
+
+            me.length = me.position;
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                me.onwrite(new ProgressEvent("write", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        }, "File", "write", [this.fileName, text, this.position]);
+};
+
+/**
+ * Moves the file pointer to the location specified.
+ *
+ * If the offset is a negative number the position of the file
+ * pointer is rewound.  If the offset is greater than the file
+ * size the position is set to the end of the file.
+ *
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function(offset) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    if (!offset) {
+        return;
+    }
+
+    // See back from end of file.
+    if (offset < 0) {
+        this.position = Math.max(offset + this.length, 0);
+    }
+    // Offset is bigger then file size so set position
+    // to the end of the file.
+    else if (offset > this.length) {
+        this.position = this.length;
+    }
+    // Offset is between 0 and file size so set the position
+    // to start writing.
+    else {
+        this.position = offset;
+    }
+};
+
+/**
+ * Truncates the file to the size specified.
+ *
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function(size) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        me.onwritestart(new ProgressEvent("writestart", {"target":this}));
+    }
+
+    // Write file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Update the length of the file
+            me.length = r;
+            me.position = Math.min(me.position, r);
+
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                me.onwrite(new ProgressEvent("write", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        }, "File", "truncate", [this.fileName, size]);
+};
+
+module.exports = FileWriter;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/Flags.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/Flags.js b/lib0/plugin/Flags.js
new file mode 100644
index 0000000..d918c38
--- /dev/null
+++ b/lib0/plugin/Flags.js
@@ -0,0 +1,15 @@
+/**
+ * Supplies arguments to methods that lookup or create files and directories.
+ * 
+ * @param create
+ *            {boolean} file or directory if it doesn't exist
+ * @param exclusive
+ *            {boolean} used with create; if true the command will fail if
+ *            target path exists
+ */
+function Flags(create, exclusive) {
+    this.create = create || false;
+    this.exclusive = exclusive || false;
+}
+
+module.exports = Flags;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/LocalFileSystem.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/LocalFileSystem.js b/lib0/plugin/LocalFileSystem.js
new file mode 100644
index 0000000..f71ae62
--- /dev/null
+++ b/lib0/plugin/LocalFileSystem.js
@@ -0,0 +1,13 @@
+var exec = require('cordova/exec');
+
+/**
+ * Represents a local file system.
+ */
+var LocalFileSystem = function() {
+
+};
+
+LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence
+LocalFileSystem.PERSISTENT = 1; //persistent
+
+module.exports = LocalFileSystem;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/Media.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/Media.js b/lib0/plugin/Media.js
new file mode 100644
index 0000000..8d56da9
--- /dev/null
+++ b/lib0/plugin/Media.js
@@ -0,0 +1,186 @@
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec');
+
+var mediaObjects = {};
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ *
+ * @constructor
+ * @param src                   The file name or url to play
+ * @param successCallback       The callback to be called when the file is done playing or recording.
+ *                                  successCallback()
+ * @param errorCallback         The callback to be called if there is an error.
+ *                                  errorCallback(int errorCode) - OPTIONAL
+ * @param statusCallback        The callback to be called when media status has changed.
+ *                                  statusCallback(int statusCode) - OPTIONAL
+ */
+var Media = function(src, successCallback, errorCallback, statusCallback) {
+
+    // successCallback optional
+    if (successCallback && (typeof successCallback !== "function")) {
+        console.log("Media Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Media Error: errorCallback is not a function");
+        return;
+    }
+
+    // statusCallback optional
+    if (statusCallback && (typeof statusCallback !== "function")) {
+        console.log("Media Error: statusCallback is not a function");
+        return;
+    }
+
+    this.id = utils.createUUID();
+    mediaObjects[this.id] = this;
+    this.src = src;
+    this.successCallback = successCallback;
+    this.errorCallback = errorCallback;
+    this.statusCallback = statusCallback;
+    this._duration = -1;
+    this._position = -1;
+    exec(null, this.errorCallback, "Media", "create", [this.id, this.src]);
+};
+
+// Media messages
+Media.MEDIA_STATE = 1;
+Media.MEDIA_DURATION = 2;
+Media.MEDIA_POSITION = 3;
+Media.MEDIA_ERROR = 9;
+
+// Media states
+Media.MEDIA_NONE = 0;
+Media.MEDIA_STARTING = 1;
+Media.MEDIA_RUNNING = 2;
+Media.MEDIA_PAUSED = 3;
+Media.MEDIA_STOPPED = 4;
+Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
+
+// "static" function to return existing objs.
+Media.get = function(id) {
+    return mediaObjects[id];
+};
+
+/**
+ * Start or resume playing audio file.
+ */
+Media.prototype.play = function() {
+    exec(this.successCallback, this.errorCallback, "Media", "startPlayingAudio", [this.id, this.src]);
+};
+
+/**
+ * Stop playing audio file.
+ */
+Media.prototype.stop = function() {
+    var me = this;
+    exec(function() {
+        me._position = 0;
+        me.successCallback();
+    }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]);
+};
+
+/**
+ * Seek or jump to a new time in the track..
+ */
+Media.prototype.seekTo = function(milliseconds) {
+    var me = this;
+    exec(function(p) {
+        me._position = p;
+    }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]);
+};
+
+/**
+ * Pause playing audio file.
+ */
+Media.prototype.pause = function() {
+    exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]);
+};
+
+/**
+ * Get duration of an audio file.
+ * The duration is only set for audio that is playing, paused or stopped.
+ *
+ * @return      duration or -1 if not known.
+ */
+Media.prototype.getDuration = function() {
+    return this._duration;
+};
+
+/**
+ * Get position of audio.
+ */
+Media.prototype.getCurrentPosition = function(success, fail) {
+    var me = this;
+    exec(function(p) {
+        me._position = p;
+        success(p);
+    }, fail, "Media", "getCurrentPositionAudio", [this.id]);
+};
+
+/**
+ * Start recording audio file.
+ */
+Media.prototype.startRecord = function() {
+    exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
+};
+
+/**
+ * Stop recording audio file.
+ */
+Media.prototype.stopRecord = function() {
+    exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
+};
+
+/**
+ * Release the resources.
+ */
+Media.prototype.release = function() {
+    exec(null, this.errorCallback, "Media", "release", [this.id]);
+};
+
+/**
+ * Adjust the volume.
+ */
+Media.prototype.setVolume = function(volume) {
+    exec(null, null, "Media", "setVolume", [this.id, volume]);
+};
+
+/**
+ * Audio has status update.
+ * PRIVATE
+ *
+ * @param id            The media object id (string)
+ * @param status        The status code (int)
+ * @param msg           The status message (string)
+ */
+Media.onStatus = function(id, msg, value) {
+    var media = mediaObjects[id];
+    // If state update
+    if (msg === Media.MEDIA_STATE) {
+        if (value === Media.MEDIA_STOPPED) {
+            if (media.successCallback) {
+                media.successCallback();
+            }
+        }
+        if (media.statusCallback) {
+            media.statusCallback(value);
+        }
+    }
+    else if (msg === Media.MEDIA_DURATION) {
+        media._duration = value;
+    }
+    else if (msg === Media.MEDIA_ERROR) {
+        if (media.errorCallback) {
+            media.errorCallback({"code":value});
+        }
+    }
+    else if (msg === Media.MEDIA_POSITION) {
+        media._position = value;
+    }
+};
+
+module.exports = Media;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/MediaError.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/MediaError.js b/lib0/plugin/MediaError.js
new file mode 100644
index 0000000..40ce8f1
--- /dev/null
+++ b/lib0/plugin/MediaError.js
@@ -0,0 +1,16 @@
+/**
+ * This class contains information about any Media errors.
+ * @constructor
+ */
+var MediaError = function(code, msg) {
+    this.code = (code !== undefined ? code : null);
+    this.message = msg || "";
+};
+
+MediaError.MEDIA_ERR_NONE_ACTIVE    = 0;
+MediaError.MEDIA_ERR_ABORTED        = 1;
+MediaError.MEDIA_ERR_NETWORK        = 2;
+MediaError.MEDIA_ERR_DECODE         = 3;
+MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
+
+module.exports = MediaError;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/MediaFile.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/MediaFile.js b/lib0/plugin/MediaFile.js
new file mode 100644
index 0000000..e73716b
--- /dev/null
+++ b/lib0/plugin/MediaFile.js
@@ -0,0 +1,56 @@
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    File = require('cordova/plugin/File'),
+    CaptureError = require('cordova/plugin/CaptureError');
+/**
+ * Represents a single file.
+ *
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
+  MediaFile.__super__.constructor.apply(this, arguments);
+};
+
+utils.extend(MediaFile, File);
+
+/**
+ * Request capture format data for a specific file and type
+ * 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ */
+MediaFile.prototype.getFormatData = function(successCallback, errorCallback) {
+	if (typeof this.fullPath === "undefined" || this.fullPath === null) {
+		errorCallback(new CaptureError(CaptureError.CAPTURE_INVALID_ARGUMENT));
+	} else {
+    exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
+	}	
+};
+
+/**
+ * Casts a PluginResult message property  (array of objects) to an array of MediaFile objects
+ * (used in Objective-C and Android)
+ *
+ * @param {PluginResult} pluginResult
+ */
+MediaFile.cast = function(pluginResult) {
+  var mediaFiles = [];
+  var i;
+  for (i=0; i<pluginResult.message.length; i++) {
+    var mediaFile = new MediaFile();
+    mediaFile.name = pluginResult.message[i].name;
+    mediaFile.fullPath = pluginResult.message[i].fullPath;
+    mediaFile.type = pluginResult.message[i].type;
+    mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
+    mediaFile.size = pluginResult.message[i].size;
+    mediaFiles.push(mediaFile);
+  }
+  pluginResult.message = mediaFiles;
+  return pluginResult;
+};
+
+module.exports = MediaFile;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/MediaFileData.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/MediaFileData.js b/lib0/plugin/MediaFileData.js
new file mode 100644
index 0000000..ea5c2a3
--- /dev/null
+++ b/lib0/plugin/MediaFileData.js
@@ -0,0 +1,18 @@
+/**
+ * MediaFileData encapsulates format information of a media file.
+ *
+ * @param {DOMString} codecs
+ * @param {long} bitrate
+ * @param {long} height
+ * @param {long} width
+ * @param {float} duration
+ */
+var MediaFileData = function(codecs, bitrate, height, width, duration){
+	this.codecs = codecs || null;
+	this.bitrate = bitrate || 0;
+	this.height = height || 0;
+	this.width = width || 0;
+	this.duration = duration || 0;
+};
+
+module.exports = MediaFileData;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/Metadata.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/Metadata.js b/lib0/plugin/Metadata.js
new file mode 100644
index 0000000..b29cc7c
--- /dev/null
+++ b/lib0/plugin/Metadata.js
@@ -0,0 +1,10 @@
+/**
+ * Information about the state of the file or directory
+ * 
+ * {Date} modificationTime (readonly)
+ */
+var Metadata = function(time) {
+    this.modificationTime = (typeof time != 'undefined'?new Date(time):null);
+};
+
+module.exports = Metadata;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/Position.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/Position.js b/lib0/plugin/Position.js
new file mode 100644
index 0000000..25096c4
--- /dev/null
+++ b/lib0/plugin/Position.js
@@ -0,0 +1,8 @@
+var Coordinates = require('cordova/plugin/Coordinates');
+
+var Position = function(coords, timestamp) {
+	this.coords = new Coordinates(coords.latitude, coords.longitude, coords.altitude, coords.accuracy, coords.heading, coords.velocity, coords.altitudeAccuracy);
+	this.timestamp = (timestamp !== undefined) ? timestamp : new Date().getTime();
+};
+
+module.exports = Position;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/PositionError.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/PositionError.js b/lib0/plugin/PositionError.js
new file mode 100644
index 0000000..07012d6
--- /dev/null
+++ b/lib0/plugin/PositionError.js
@@ -0,0 +1,17 @@
+/**
+ * Position error object
+ *
+ * @constructor
+ * @param code
+ * @param message
+ */
+var PositionError = function(code, message) {
+    this.code = code || null;
+    this.message = message || '';
+};
+
+PositionError.PERMISSION_DENIED = 1;
+PositionError.POSITION_UNAVAILABLE = 2;
+PositionError.TIMEOUT = 3;
+
+module.exports = PositionError;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/ProgressEvent.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/ProgressEvent.js b/lib0/plugin/ProgressEvent.js
new file mode 100644
index 0000000..d8e41c1
--- /dev/null
+++ b/lib0/plugin/ProgressEvent.js
@@ -0,0 +1,46 @@
+// If ProgressEvent exists in global context, use it already, otherwise use our own polyfill
+// Feature test: See if we can instantiate a native ProgressEvent;
+// if so, use that approach,
+// otherwise fill-in with our own implementation.
+//
+// NOTE: right now we always fill in with our own. Down the road would be nice if we can use whatever is native in the webview.
+var ProgressEvent = (function() {
+    /*
+    var createEvent = function(data) {
+        var event = document.createEvent('Events');
+        event.initEvent('ProgressEvent', false, false);
+        if (data) {
+            for (var i in data) {
+                if (data.hasOwnProperty(i)) {
+                    event[i] = data[i];
+                }
+            }
+            if (data.target) {
+                // TODO: cannot call <some_custom_object>.dispatchEvent
+                // need to first figure out how to implement EventTarget
+            }
+        }
+        return event;
+    };
+    try {
+        var ev = createEvent({type:"abort",target:document});
+        return function ProgressEvent(type, data) {
+            data.type = type;
+            return createEvent(data);
+        };
+    } catch(e){
+    */
+        return function ProgressEvent(type, dict) {
+            this.type = type;
+            this.bubbles = false;
+            this.cancelBubble = false;
+            this.cancelable = false;
+            this.lengthComputable = false;
+            this.loaded = dict && dict.loaded ? dict.loaded : 0;
+            this.total = dict && dict.total ? dict.total : 0;
+            this.target = dict && dict.target ? dict.target : null;
+        };
+    //}
+})();
+
+module.exports = ProgressEvent;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/accelerometer.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/accelerometer.js b/lib0/plugin/accelerometer.js
new file mode 100644
index 0000000..6f01ab8
--- /dev/null
+++ b/lib0/plugin/accelerometer.js
@@ -0,0 +1,95 @@
+/**
+ * This class provides access to device accelerometer data.
+ * @constructor
+ */
+var utils = require("cordova/utils"),
+    exec = require("cordova/exec");
+
+// Local singleton variables.
+var timers = {};
+
+var accelerometer = {
+    /**
+     * 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. (OPTIONAL)
+     * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
+     */
+    getCurrentAcceleration: function(successCallback, errorCallback, options) {
+
+        // successCallback required
+        if (typeof successCallback !== "function") {
+            console.log("Accelerometer Error: successCallback is not a function");
+            return;
+        }
+
+        // errorCallback optional
+        if (errorCallback && (typeof errorCallback !== "function")) {
+            console.log("Accelerometer Error: errorCallback is not a function");
+            return;
+        }
+
+        // Get acceleration
+        exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
+    },
+
+    /**
+     * 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. (OPTIONAL)
+     * @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
+     * @return String                       The watch id that must be passed to #clearWatch to stop watching.
+     */
+    watchAcceleration: function(successCallback, errorCallback, options) {
+
+        // Default interval (10 sec)
+        var frequency = (options !== undefined && options.frequency !== undefined)? options.frequency : 10000;
+
+        // successCallback required
+        if (typeof successCallback !== "function") {
+            console.log("Accelerometer Error: successCallback is not a function");
+            return;
+        }
+
+        // errorCallback optional
+        if (errorCallback && (typeof errorCallback !== "function")) {
+            console.log("Accelerometer Error: errorCallback is not a function");
+            return;
+        }
+
+        // Make sure accelerometer timeout > frequency + 10 sec
+        exec(
+            function(timeout) {
+                if (timeout < (frequency + 10000)) {
+                    exec(null, null, "Accelerometer", "setTimeout", [frequency + 10000]);
+                }
+            },
+            function(e) { }, "Accelerometer", "getTimeout", []);
+
+        // Start watch timer
+        var id = utils.createUUID();
+        timers[id] = window.setInterval(function() {
+            exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
+        }, (frequency ? frequency : 1));
+
+        return id;
+    },
+
+    /**
+     * Clears the specified accelerometer watch.
+     *
+     * @param {String} id       The id of the watch returned from #watchAcceleration.
+     */
+    clearWatch: function(id) {
+
+        // Stop javascript timer & remove from timer list
+        if (id && timers[id] !== undefined) {
+            window.clearInterval(timers[id]);
+            delete timers[id];
+        }
+    }
+};
+
+module.exports = accelerometer;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/android/app.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/android/app.js b/lib0/plugin/android/app.js
new file mode 100644
index 0000000..f858829
--- /dev/null
+++ b/lib0/plugin/android/app.js
@@ -0,0 +1,71 @@
+var exec = require('cordova/exec');
+
+module.exports = {
+  /**
+   * Clear the resource cache.
+   */
+  clearCache:function() {
+    exec(null, null, "App", "clearCache", []);
+  },
+
+  /**
+   * Load the url into the webview or into new browser instance.
+   *
+   * @param url           The URL to load
+   * @param props         Properties that can be passed in to the activity:
+   *      wait: int                           => wait msec before loading URL
+   *      loadingDialog: "Title,Message"      => display a native loading dialog
+   *      loadUrlTimeoutValue: int            => time in msec to wait before triggering a timeout error
+   *      clearHistory: boolean              => clear webview history (default=false)
+   *      openExternal: boolean              => open in a new browser (default=false)
+   *
+   * Example:
+   *      navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
+   */
+  loadUrl:function(url, props) {
+    exec(null, null, "App", "loadUrl", [url, props]);
+  },
+
+  /**
+   * Cancel loadUrl that is waiting to be loaded.
+   */
+  cancelLoadUrl:function() {
+    exec(null, null, "App", "cancelLoadUrl", []);
+  },
+
+  /**
+   * Clear web history in this web view.
+   * Instead of BACK button loading the previous web page, it will exit the app.
+   */
+  clearHistory:function() {
+    exec(null, null, "App", "clearHistory", []);
+  },
+
+  /**
+   * Go to previous page displayed.
+   * This is the same as pressing the backbutton on Android device.
+   */
+  backHistory:function() {
+    exec(null, null, "App", "backHistory", []);
+  },
+
+  /**
+   * Override the default behavior of the Android back button.
+   * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
+   *
+   * Note: The user should not have to call this method.  Instead, when the user
+   *       registers for the "backbutton" event, this is automatically done.
+   *
+   * @param override		T=override, F=cancel override
+   */
+  overrideBackbutton:function(override) {
+    exec(null, null, "App", "overrideBackbutton", [override]);
+  },
+
+  /**
+   * Exit and terminate the application.
+   */
+  exitApp:function() {
+    return exec(null, null, "App", "exitApp", []);
+  } 
+};

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/android/callback.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/android/callback.js b/lib0/plugin/android/callback.js
new file mode 100644
index 0000000..d5768a7
--- /dev/null
+++ b/lib0/plugin/android/callback.js
@@ -0,0 +1,85 @@
+var port = null,
+    token = null,
+    cordova = require('cordova'),
+    polling = require('cordova/plugin/android/polling'),
+    callback = function() {
+      // Exit if shutting down app
+      if (cordova.shuttingDown) {
+          return;
+      }
+
+      // If polling flag was changed, start using polling from now on
+      if (cordova.UsePolling) {
+          polling();
+          return;
+      }
+
+      var xmlhttp = new XMLHttpRequest();
+
+      // Callback function when XMLHttpRequest is ready
+      xmlhttp.onreadystatechange=function(){
+          if(xmlhttp.readyState === 4){
+
+              // Exit if shutting down app
+              if (cordova.shuttingDown) {
+                  return;
+              }
+
+              // If callback has JavaScript statement to execute
+              if (xmlhttp.status === 200) {
+
+                  // Need to url decode the response
+                  var msg = decodeURIComponent(xmlhttp.responseText);
+                  setTimeout(function() {
+                      try {
+                          var t = eval(msg);
+                      }
+                      catch (e) {
+                          // If we're getting an error here, seeing the message will help in debugging
+                          console.log("JSCallback: Message from Server: " + msg);
+                          console.log("JSCallback Error: "+e);
+                      }
+                  }, 1);
+                  setTimeout(callback, 1);
+              }
+
+              // If callback ping (used to keep XHR request from timing out)
+              else if (xmlhttp.status === 404) {
+                  setTimeout(callback, 10);
+              }
+
+              // If security error
+              else if (xmlhttp.status === 403) {
+                  console.log("JSCallback Error: Invalid token.  Stopping callbacks.");
+              }
+
+              // If server is stopping
+              else if (xmlhttp.status === 503) {
+                  console.log("JSCallback Server Closed: Stopping callbacks.");
+              }
+
+              // If request wasn't GET
+              else if (xmlhttp.status === 400) {
+                  console.log("JSCallback Error: Bad request.  Stopping callbacks.");
+              }
+
+              // If error, revert to polling
+              else {
+                  console.log("JSCallback Error: Request failed.");
+                  cordova.UsePolling = true;
+                  polling();
+              }
+          }
+      };
+
+      if (port === null) {
+          port = prompt("getPort", "gap_callbackServer:");
+      }
+      if (token === null) {
+          token = prompt("getToken", "gap_callbackServer:");
+      }
+      xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
+      xmlhttp.send();
+};
+
+module.exports = callback;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/android/device.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/android/device.js b/lib0/plugin/android/device.js
new file mode 100644
index 0000000..1defbbe
--- /dev/null
+++ b/lib0/plugin/android/device.js
@@ -0,0 +1,92 @@
+var channel = require('cordova/channel'),
+    exec = require('cordova/exec');
+
+/**
+ * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+function Device() {
+    this.available = false;
+    this.platform = null;
+    this.version = null;
+    this.name = null;
+    this.uuid = null;
+    this.cordova = null;
+
+    var me = this;
+    this.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;
+            console.log("Error initializing Cordova: " + e);
+            alert("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;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Device Error: errorCallback is not a function");
+        return;
+    }
+
+    // Get info
+    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
+};
+
+/*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * You must explicitly override the back button.
+ */
+Device.prototype.overrideBackButton = function() {
+	console.log("Device.overrideBackButton() is deprecated.  Use App.overrideBackbutton(true).");
+	navigator.app.overrideBackbutton(true);
+};
+
+/*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * This resets the back button to the default behaviour
+ */
+Device.prototype.resetBackButton = function() {
+	console.log("Device.resetBackButton() is deprecated.  Use App.overrideBackbutton(false).");
+	navigator.app.overrideBackbutton(false);
+};
+
+/*
+ * DEPRECATED
+ * This is only for Android.
+ *
+ * This terminates the activity!
+ */
+Device.prototype.exitApp = function() {
+	console.log("Device.exitApp() is deprecated.  Use App.exitApp().");
+	navigator.app.exitApp();
+};
+
+module.exports = new Device();

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/android/polling.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/android/polling.js b/lib0/plugin/android/polling.js
new file mode 100644
index 0000000..36b8e86
--- /dev/null
+++ b/lib0/plugin/android/polling.js
@@ -0,0 +1,33 @@
+var cordova = require('cordova'),
+    period = 50,
+    polling = function() {
+      // Exit if shutting down app
+      if (cordova.shuttingDown) {
+          return;
+      }
+
+      // If polling flag was changed, stop using polling from now on and switch to XHR server / callback
+      if (!cordova.UsePolling) {
+          require('cordova/plugin/android/callback')();
+          return;
+      }
+
+      var msg = prompt("", "gap_poll:");
+      if (msg) {
+          setTimeout(function() {
+              try {
+                  var t = eval(""+msg);
+              }
+              catch (e) {
+                  console.log("JSCallbackPolling: Message from Server: " + msg);
+                  console.log("JSCallbackPolling Error: "+e);
+              }
+          }, 1);
+          setTimeout(polling, 1);
+      }
+      else {
+          setTimeout(polling, period);
+      }
+};
+
+module.exports = polling;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/android/storage.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/android/storage.js b/lib0/plugin/android/storage.js
new file mode 100644
index 0000000..000612b
--- /dev/null
+++ b/lib0/plugin/android/storage.js
@@ -0,0 +1,377 @@
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec');
+    channel = require('cordova/channel');
+
+var queryQueue = {};
+
+/**
+ * SQL result set object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Rows = function() {
+    this.resultSet = [];    // results array
+    this.length = 0;        // number of rows
+};
+
+/**
+ * Get item from SQL result set
+ *
+ * @param row           The row number to return
+ * @return              The row object
+ */
+DroidDB_Rows.prototype.item = function(row) {
+    return this.resultSet[row];
+};
+
+/**
+ * SQL result set that is returned to user.
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Result = function() {
+    this.rows = new DroidDB_Rows();
+};
+
+/**
+ * Callback from native code when query is complete.
+ * PRIVATE METHOD
+ *
+ * @param id   Query id
+ */
+function completeQuery(id, data) {
+    var query = queryQueue[id];
+    if (query) {
+        try {
+            delete queryQueue[id];
+
+            // Get transaction
+            var tx = query.tx;
+
+            // If transaction hasn't failed
+            // Note: We ignore all query results if previous query
+            //       in the same transaction failed.
+            if (tx && tx.queryList[id]) {
+
+                // Save query results
+                var r = new DroidDB_Result();
+                r.rows.resultSet = data;
+                r.rows.length = data.length;
+                try {
+                    if (typeof query.successCallback === 'function') {
+                        query.successCallback(query.tx, r);
+                    }
+                } catch (ex) {
+                    console.log("executeSql error calling user success callback: "+ex);
+                }
+
+                tx.queryComplete(id);
+            }
+        } catch (e) {
+            console.log("executeSql error: "+e);
+        }
+    }
+}
+
+/**
+ * Callback from native code when query fails
+ * PRIVATE METHOD
+ *
+ * @param reason            Error message
+ * @param id                Query id
+ */
+function failQuery(reason, id) {
+    var query = queryQueue[id];
+    if (query) {
+        try {
+            delete queryQueue[id];
+
+            // Get transaction
+            var tx = query.tx;
+
+            // If transaction hasn't failed
+            // Note: We ignore all query results if previous query
+            //       in the same transaction failed.
+            if (tx && tx.queryList[id]) {
+                tx.queryList = {};
+
+                try {
+                    if (typeof query.errorCallback === 'function') {
+                        query.errorCallback(query.tx, reason);
+                    }
+                } catch (ex) {
+                    console.log("executeSql error calling user error callback: "+ex);
+                }
+
+                tx.queryFailed(id, reason);
+            }
+
+        } catch (e) {
+            console.log("executeSql error: "+e);
+        }
+    }
+}
+
+/**
+ * SQL query object
+ * PRIVATE METHOD
+ *
+ * @constructor
+ * @param tx                The transaction object that this query belongs to
+ */
+var DroidDB_Query = function(tx) {
+
+    // Set the id of the query
+    this.id = utils.createUUID();
+
+    // Add this query to the queue
+    queryQueue[this.id] = this;
+
+    // Init result
+    this.resultSet = [];
+
+    // Set transaction that this query belongs to
+    this.tx = tx;
+
+    // Add this query to transaction list
+    this.tx.queryList[this.id] = this;
+
+    // Callbacks
+    this.successCallback = null;
+    this.errorCallback = null;
+
+};
+
+/**
+ * Transaction object
+ * PRIVATE METHOD
+ * @constructor
+ */
+var DroidDB_Tx = function() {
+
+    // Set the id of the transaction
+    this.id = utils.createUUID();
+
+    // Callbacks
+    this.successCallback = null;
+    this.errorCallback = null;
+
+    // Query list
+    this.queryList = {};
+};
+
+/**
+ * Mark query in transaction as complete.
+ * If all queries are complete, call the user's transaction success callback.
+ *
+ * @param id                Query id
+ */
+DroidDB_Tx.prototype.queryComplete = function(id) {
+    delete this.queryList[id];
+
+    // If no more outstanding queries, then fire transaction success
+    if (this.successCallback) {
+        var count = 0;
+        var i;
+        for (i in this.queryList) {
+            if (this.queryList.hasOwnProperty(i)) {
+                count++;
+            }
+        }
+        if (count === 0) {
+            try {
+                this.successCallback();
+            } catch(e) {
+                console.log("Transaction error calling user success callback: " + e);
+            }
+        }
+    }
+};
+
+/**
+ * Mark query in transaction as failed.
+ *
+ * @param id                Query id
+ * @param reason            Error message
+ */
+DroidDB_Tx.prototype.queryFailed = function(id, reason) {
+
+    // The sql queries in this transaction have already been run, since
+    // we really don't have a real transaction implemented in native code.
+    // However, the user callbacks for the remaining sql queries in transaction
+    // will not be called.
+    this.queryList = {};
+
+    if (this.errorCallback) {
+        try {
+            this.errorCallback(reason);
+        } catch(e) {
+            console.log("Transaction error calling user error callback: " + e);
+        }
+    }
+};
+
+/**
+ * Execute SQL statement
+ *
+ * @param sql                   SQL statement to execute
+ * @param params                Statement parameters
+ * @param successCallback       Success callback
+ * @param errorCallback         Error callback
+ */
+DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
+
+    // Init params array
+    if (typeof params === 'undefined') {
+        params = [];
+    }
+
+    // Create query and add to queue
+    var query = new DroidDB_Query(this);
+    queryQueue[query.id] = query;
+
+    // Save callbacks
+    query.successCallback = successCallback;
+    query.errorCallback = errorCallback;
+
+    // Call native code
+    exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
+};
+
+var DatabaseShell = function() {
+};
+
+/**
+ * Start a transaction.
+ * Does not support rollback in event of failure.
+ *
+ * @param process {Function}            The transaction function
+ * @param successCallback {Function}
+ * @param errorCallback {Function}
+ */
+DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
+    var tx = new DroidDB_Tx();
+    tx.successCallback = successCallback;
+    tx.errorCallback = errorCallback;
+    try {
+        process(tx);
+    } catch (e) {
+        console.log("Transaction error: "+e);
+        if (tx.errorCallback) {
+            try {
+                tx.errorCallback(e);
+            } catch (ex) {
+                console.log("Transaction error calling user error callback: "+e);
+            }
+        }
+    }
+};
+
+/**
+ * Open database
+ *
+ * @param name              Database name
+ * @param version           Database version
+ * @param display_name      Database display name
+ * @param size              Database size in bytes
+ * @return                  Database object
+ */
+var DroidDB_openDatabase = function(name, version, display_name, size) {
+    exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
+    var db = new DatabaseShell();
+    return db;
+};
+
+/**
+ * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
+ * TODO: Do similar for sessionStorage.
+ * @constructor
+ */
+var CupcakeLocalStorage = function() {
+    channel.waitForInitialization("cupcakeStorage");
+
+    try {
+
+      this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
+      var storage = {};
+      this.length = 0;
+      function setLength (length) {
+        this.length = length;
+        localStorage.length = length;
+      }
+      this.db.transaction(
+        function (transaction) {
+            var i;
+          transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+          transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
+            for(var i = 0; i < result.rows.length; i++) {
+              storage[result.rows.item(i)['id']] =  result.rows.item(i)['body'];
+            }
+            setLength(result.rows.length);
+            channel.initializationComplete("cupcakeStorage");
+          });
+
+        },
+        function (err) {
+          alert(err.message);
+        }
+      );
+      this.setItem = function(key, val) {
+        if (typeof(storage[key])=='undefined') {
+          this.length++;
+        }
+        storage[key] = val;
+        this.db.transaction(
+          function (transaction) {
+            transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+            transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
+          }
+        );
+      };
+      this.getItem = function(key) {
+        return storage[key];
+      };
+      this.removeItem = function(key) {
+        delete storage[key];
+        this.length--;
+        this.db.transaction(
+          function (transaction) {
+            transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+            transaction.executeSql('DELETE FROM storage where id=?', [key]);
+          }
+        );
+      };
+      this.clear = function() {
+        storage = {};
+        this.length = 0;
+        this.db.transaction(
+          function (transaction) {
+            transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
+            transaction.executeSql('DELETE FROM storage', []);
+          }
+        );
+      };
+      this.key = function(index) {
+        var i = 0;
+        for (var j in storage) {
+          if (i==index) {
+            return j;
+          } else {
+            i++;
+          }
+        }
+        return null;
+      };
+
+    } catch(e) {
+      alert("Database error "+e+".");
+        return;
+    }
+};
+
+module.exports = {
+  openDatabase:DroidDB_openDatabase,
+  CupcakeLocalStorage:CupcakeLocalStorage,
+  failQuery:failQuery,
+  completeQuery:completeQuery
+};

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/battery.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/battery.js b/lib0/plugin/battery.js
new file mode 100644
index 0000000..5649356
--- /dev/null
+++ b/lib0/plugin/battery.js
@@ -0,0 +1,87 @@
+/**
+ * This class contains information about the current battery status.
+ * @constructor
+ */
+var cordova = require('cordova'),
+    exec = require('cordova/exec');
+
+function handlers() {
+  return battery.channels.batterystatus.numHandlers + 
+         battery.channels.batterylow.numHandlers +
+         battery.channels.batterycritical.numHandlers;
+}
+
+var Battery = function() {
+    this._level = null;
+    this._isPlugged = null;
+    // Create new event handlers on the window (returns a channel instance)
+    var subscriptionEvents = {
+      onSubscribe:this.onSubscribe,
+      onUnsubscribe:this.onUnsubscribe
+    };
+    this.channels = {
+      batterystatus:cordova.addWindowEventHandler("batterystatus", subscriptionEvents),
+      batterylow:cordova.addWindowEventHandler("batterylow", subscriptionEvents),
+      batterycritical:cordova.addWindowEventHandler("batterycritical", subscriptionEvents)
+    };
+};
+/**
+ * Event handlers for when callbacks get registered for the battery.
+ * Keep track of how many handlers we have so we can start and stop the native battery listener
+ * appropriately (and hopefully save on battery life!).
+ */
+Battery.prototype.onSubscribe = function() {
+  var me = battery;
+  // If we just registered the first handler, make sure native listener is started.
+  if (handlers() === 1) {
+    exec(me._status, me._error, "Battery", "start", []);
+  }
+};
+
+Battery.prototype.onUnsubscribe = function() {
+  var me = battery;
+
+  // If we just unregistered the last handler, make sure native listener is stopped.
+  if (handlers() === 0) {
+      exec(null, null, "Battery", "stop", []);
+  }
+};
+
+/**
+ * Callback for battery status
+ * 
+ * @param {Object} info			keys: level, isPlugged
+ */
+Battery.prototype._status = function(info) {
+	if (info) {
+		var me = battery;
+    var level = info.level;
+		if (me._level !== level || me._isPlugged !== info.isPlugged) {
+			// Fire batterystatus event
+			cordova.fireWindowEvent("batterystatus", info);
+
+			// Fire low battery event
+			if (level === 20 || level === 5) {
+				if (level === 20) {
+					cordova.fireWindowEvent("batterylow", info);
+				}
+				else {
+					cordova.fireWindowEvent("batterycritical", info);
+				}
+			}
+		}
+		me._level = level;
+		me._isPlugged = info.isPlugged;	
+	}
+};
+
+/**
+ * Error callback for battery start
+ */
+Battery.prototype._error = function(e) {
+    console.log("Error initializing Battery: " + e);
+};
+
+var battery = new Battery();
+
+module.exports = battery;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/blackberry/DirectoryEntry.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/blackberry/DirectoryEntry.js b/lib0/plugin/blackberry/DirectoryEntry.js
new file mode 100644
index 0000000..76acaf7
--- /dev/null
+++ b/lib0/plugin/blackberry/DirectoryEntry.js
@@ -0,0 +1,239 @@
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry'),
+    FileEntry = require('cordova/plugin/FileEntry'),
+    FileError = require('cordova/plugin/FileError'),
+    exec = require('cordova/exec');
+
+module.exports = {
+    /**
+     * Creates or looks up a directory; override for BlackBerry.
+     *
+     * @param path
+     *            {DOMString} either a relative or absolute path from this
+     *            directory in which to look up or create a directory
+     * @param options
+     *            {Flags} options to create or exclusively create the directory
+     * @param successCallback
+     *            {Function} called with the new DirectoryEntry
+     * @param errorCallback
+     *            {Function} called with a FileError
+     */
+    getDirectory : function(path, options, successCallback, errorCallback) {
+        // create directory if it doesn't exist
+        var create = (options && options.create === true) ? true : false,
+        // if true, causes failure if create is true and path already exists
+        exclusive = (options && options.exclusive === true) ? true : false,
+        // directory exists
+        exists,
+        // create a new DirectoryEntry object and invoke success callback
+        createEntry = function() {
+            var path_parts = path.split('/'),
+                name = path_parts[path_parts.length - 1],
+                dirEntry = new DirectoryEntry(name, path);
+
+            // invoke success callback
+            if (typeof successCallback === 'function') {
+                successCallback(dirEntry);
+            }
+        };
+
+        var fail = function(error) {
+            if (typeof errorCallback === 'function') {
+                errorCallback(new FileError(error));
+            }
+        };
+
+        // determine if path is relative or absolute
+        if (!path) {
+            fail(FileError.ENCODING_ERR);
+            return;
+        } else if (path.indexOf(this.fullPath) !== 0) {
+            // path does not begin with the fullPath of this directory
+            // therefore, it is relative
+            path = this.fullPath + '/' + path;
+        }
+
+        // determine if directory exists
+        try {
+            // will return true if path exists AND is a directory
+            exists = blackberry.io.dir.exists(path);
+        } catch (e) {
+            // invalid path
+            fail(FileError.ENCODING_ERR);
+            return;
+        }
+
+        // path is a directory
+        if (exists) {
+            if (create && exclusive) {
+                // can't guarantee exclusivity
+                fail(FileError.PATH_EXISTS_ERR);
+            } else {
+                // create entry for existing directory
+                createEntry();
+            }
+        }
+        // will return true if path exists AND is a file
+        else if (blackberry.io.file.exists(path)) {
+            // the path is a file
+            fail(FileError.TYPE_MISMATCH_ERR);
+        }
+        // path does not exist, create it
+        else if (create) {
+            try {
+                // directory path must have trailing slash
+                var dirPath = path;
+                if (dirPath.substr(-1) !== '/') {
+                    dirPath += '/';
+                }
+                blackberry.io.dir.createNewDir(dirPath);
+                createEntry();
+            } catch (eone) {
+                // unable to create directory
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        }
+        // path does not exist, don't create
+        else {
+            // directory doesn't exist
+            fail(FileError.NOT_FOUND_ERR);
+        }
+    },
+    /**
+     * Create or look up a file.
+     *
+     * @param path {DOMString}
+     *            either a relative or absolute path from this directory in
+     *            which to look up or create a file
+     * @param options {Flags}
+     *            options to create or exclusively create the file
+     * @param successCallback {Function}
+     *            called with the new FileEntry object
+     * @param errorCallback {Function}
+     *            called with a FileError object if error occurs
+     */
+    getFile:function(path, options, successCallback, errorCallback) {
+        // create file if it doesn't exist
+        var create = (options && options.create === true) ? true : false,
+            // if true, causes failure if create is true and path already exists
+            exclusive = (options && options.exclusive === true) ? true : false,
+            // file exists
+            exists,
+            // create a new FileEntry object and invoke success callback
+            createEntry = function() {
+                var path_parts = path.split('/'),
+                    name = path_parts[path_parts.length - 1],
+                    fileEntry = new FileEntry(name, path);
+
+                // invoke success callback
+                if (typeof successCallback === 'function') {
+                    successCallback(fileEntry);
+                }
+            };
+
+        var fail = function(error) {
+            if (typeof errorCallback === 'function') {
+                errorCallback(new FileError(error));
+            }
+        };
+
+        // determine if path is relative or absolute
+        if (!path) {
+            fail(FileError.ENCODING_ERR);
+            return;
+        }
+        else if (path.indexOf(this.fullPath) !== 0) {
+            // path does not begin with the fullPath of this directory
+            // therefore, it is relative
+            path = this.fullPath + '/' + path;
+        }
+
+        // determine if file exists
+        try {
+            // will return true if path exists AND is a file
+            exists = blackberry.io.file.exists(path);
+        }
+        catch (e) {
+            // invalid path
+            fail(FileError.ENCODING_ERR);
+            return;
+        }
+
+        // path is a file
+        if (exists) {
+            if (create && exclusive) {
+                // can't guarantee exclusivity
+                fail(FileError.PATH_EXISTS_ERR);
+            }
+            else {
+                // create entry for existing file
+                createEntry();
+            }
+        }
+        // will return true if path exists AND is a directory
+        else if (blackberry.io.dir.exists(path)) {
+            // the path is a directory
+            fail(FileError.TYPE_MISMATCH_ERR);
+        }
+        // path does not exist, create it
+        else if (create) {
+            // create empty file
+            exec(
+                function(result) {
+                    // file created
+                    createEntry();
+                },
+                fail, "File", "write", [ path, "", 0 ]);
+        }
+        // path does not exist, don't create
+        else {
+            // file doesn't exist
+            fail(FileError.NOT_FOUND_ERR);
+        }
+    },
+
+    /**
+     * Delete a directory and all of it's contents.
+     *
+     * @param successCallback {Function} called with no parameters
+     * @param errorCallback {Function} called with a FileError
+     */
+    removeRecursively : function(successCallback, errorCallback) {
+        // we're removing THIS directory
+        var path = this.fullPath;
+
+        var fail = function(error) {
+            if (typeof errorCallback === 'function') {
+                errorCallback(new FileError(error));
+            }
+        };
+
+        // attempt to delete directory
+        if (blackberry.io.dir.exists(path)) {
+            // it is an error to attempt to remove the file system root
+            if (exec(null, null, "File", "isFileSystemRoot", [ path ]) === true) {
+                fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+            }
+            else {
+                try {
+                    // delete the directory, setting recursive flag to true
+                    blackberry.io.dir.deleteDirectory(path, true);
+                    if (typeof successCallback === "function") {
+                        successCallback();
+                    }
+                } catch (e) {
+                    // permissions don't allow deletion
+                    console.log(e);
+                    fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+                }
+            }
+        }
+        // it's a file, not a directory
+        else if (blackberry.io.file.exists(path)) {
+            fail(FileError.TYPE_MISMATCH_ERR);
+        }
+        // not found
+        else {
+            fail(FileError.NOT_FOUND_ERR);
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/blackberry/Entry.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/blackberry/Entry.js b/lib0/plugin/blackberry/Entry.js
new file mode 100644
index 0000000..cc9626d
--- /dev/null
+++ b/lib0/plugin/blackberry/Entry.js
@@ -0,0 +1,87 @@
+var FileError = require('cordova/plugin/FileError'),
+    LocalFileSystem = require('cordova/plugin/LocalFileSystem'),
+    resolveLocalFileSystemURI = require('cordova/plugin/resolveLocalFileSystemURI'),
+    exec = require('cordova/exec');
+
+module.exports = {
+    remove : function(successCallback, errorCallback) {
+        var path = this.fullPath,
+            // directory contents
+            contents = [];
+
+        var fail = function(error) {
+            if (typeof errorCallback === 'function') {
+                errorCallback(new FileError(error));
+            }
+        };
+
+        // file
+        if (blackberry.io.file.exists(path)) {
+            try {
+                blackberry.io.file.deleteFile(path);
+                if (typeof successCallback === "function") {
+                    successCallback();
+                }
+            } catch (e) {
+                // permissions don't allow
+                fail(FileError.INVALID_MODIFICATION_ERR);
+            }
+        }
+        // directory
+        else if (blackberry.io.dir.exists(path)) {
+            // it is an error to attempt to remove the file system root
+            if (exec(null, null, "File", "isFileSystemRoot", [ path ]) === true) {
+                fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+            } else {
+                // check to see if directory is empty
+                contents = blackberry.io.dir.listFiles(path);
+                if (contents.length !== 0) {
+                    fail(FileError.INVALID_MODIFICATION_ERR);
+                } else {
+                    try {
+                        // delete
+                        blackberry.io.dir.deleteDirectory(path, false);
+                        if (typeof successCallback === "function") {
+                            successCallback();
+                        }
+                    } catch (eone) {
+                        // permissions don't allow
+                        fail(FileError.NO_MODIFICATION_ALLOWED_ERR);
+                    }
+                }
+            }
+        }
+        // not found
+        else {
+            fail(FileError.NOT_FOUND_ERR);
+        }
+    },
+    getParent : function(successCallback, errorCallback) {
+        var that = this;
+
+        try {
+            // On BlackBerry, the TEMPORARY file system is actually a temporary
+            // directory that is created on a per-application basis. This is
+            // to help ensure that applications do not share the same temporary
+            // space. So we check to see if this is the TEMPORARY file system
+            // (directory). If it is, we must return this Entry, rather than
+            // the Entry for its parent.
+            requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+                    function(fileSystem) {
+                        if (fileSystem.root.fullPath === that.fullPath) {
+                            if (typeof successCallback === 'function') {
+                                successCallback(fileSystem.root);
+                            }
+                        } else {
+                            resolveLocalFileSystemURI(blackberry.io.dir
+                                    .getParentDirectory(that.fullPath),
+                                    successCallback, errorCallback);
+                        }
+                    }, errorCallback);
+        } catch (e) {
+            if (typeof errorCallback === 'function') {
+                errorCallback(new FileError(FileError.NOT_FOUND_ERR));
+            }
+        }
+    }
+};

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/blackberry/app.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/blackberry/app.js b/lib0/plugin/blackberry/app.js
new file mode 100644
index 0000000..6c58e30
--- /dev/null
+++ b/lib0/plugin/blackberry/app.js
@@ -0,0 +1,51 @@
+var exec = require('cordova/exec');
+var manager = require('cordova/plugin/blackberry/manager');
+
+module.exports = {
+  /**
+   * Clear the resource cache.
+   */
+  clearCache:function() {
+      if (typeof blackberry.widgetcache === "undefined"
+          || blackberry.widgetcache === null) {
+          console.log("blackberry.widgetcache permission not found. Cache clear denied.");
+          return;
+      }
+      blackberry.widgetcache.clearAll();
+  },
+
+  /**
+   * Clear web history in this web view.
+   * Instead of BACK button loading the previous web page, it will exit the app.
+   */
+  clearHistory:function() {
+    exec(null, null, "App", "clearHistory", []);
+  },
+
+  /**
+   * Go to previous page displayed.
+   * This is the same as pressing the backbutton on Android device.
+   */
+  backHistory:function() {
+    // window.history.back() behaves oddly on BlackBerry, so use
+    // native implementation.
+    exec(null, null, "App", "backHistory", []);
+  },
+
+  /**
+   * Exit and terminate the application.
+   */
+  exitApp:function() {
+      // Call onunload if it is defined since BlackBerry does not invoke
+      // on application exit.
+      if (typeof window.onunload === "function") {
+          window.onunload();
+      }
+
+      // allow Cordova JavaScript Extension opportunity to cleanup
+      manager.destroy();
+
+      // exit the app
+      blackberry.app.exit();
+  }
+};

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/blackberry/device.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/blackberry/device.js b/lib0/plugin/blackberry/device.js
new file mode 100644
index 0000000..42a0647
--- /dev/null
+++ b/lib0/plugin/blackberry/device.js
@@ -0,0 +1,23 @@
+var me = {},
+    channel = require('cordova/channel'),
+    exec = require('cordova/exec');
+
+exec(
+    function (device) {
+        me.platform = device.platform;
+        me.version  = device.version;
+        me.name     = device.name;
+        me.uuid     = device.uuid;
+        me.cordova  = device.cordova;
+
+        channel.onCordovaInfoReady.fire();
+    },
+    function (e) {
+        console.log("error initializing cordova: " + e);
+    },
+    "Device",
+    "getDeviceInfo",
+    []
+);
+
+module.exports = me;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/blackberry/manager.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/blackberry/manager.js b/lib0/plugin/blackberry/manager.js
new file mode 100644
index 0000000..ef353bb
--- /dev/null
+++ b/lib0/plugin/blackberry/manager.js
@@ -0,0 +1,87 @@
+var webworks = require('cordova/plugin/webworks/manager'),
+    Cordova = require('cordova'),
+    plugins = {};
+
+function _exec(win, fail, clazz, action, args) {
+    var callbackId = clazz + Cordova.callbackId++,
+        origResult,
+        evalResult,
+        execResult;
+
+    try {
+
+        if (win || fail) {
+            Cordova.callbacks[callbackId] = {success: win, fail: fail};
+        }
+
+        // Note: Device returns string, but for some reason emulator returns object - so convert to string.
+        origResult = "" + org.apache.cordova.JavaPluginManager.exec(clazz, action, callbackId, JSON.stringify(args), true);
+
+        // If a result was returned
+        if (origResult.length > 0) {
+            eval("evalResult = " + origResult + ";");
+
+            // If status is OK, then return evalResultalue back to caller
+            if (evalResult.status === Cordova.callbackStatus.OK) {
+
+                // If there is a success callback, then call it now with returned evalResultalue
+                if (win) {
+                    // Clear callback if not expecting any more results
+                    if (!evalResult.keepCallback) {
+                        delete Cordova.callbacks[callbackId];
+                    }
+                }
+            } else if (evalResult.status === Cordova.callbackStatus.NO_RESULT) {
+
+                // Clear callback if not expecting any more results
+                if (!evalResult.keepCallback) {
+                    delete Cordova.callbacks[callbackId];
+                }
+            } else {
+                // If there is a fail callback, then call it now with returned evalResultalue
+                if (fail) {
+
+                    // Clear callback if not expecting any more results
+                    if (!evalResult.keepCallback) {
+                        delete Cordova.callbacks[callbackId];
+                    }
+                }
+            }
+            execResult = evalResult;
+        } else {
+            // Asynchronous calls return an empty string. Return a NO_RESULT
+            // status for those executions.
+            execResult = {"status" : Cordova.callbackStatus.NO_RESULT,
+                    "message" : ""};
+        }
+    } catch (e) {
+        console.log("BlackBerryPluginManager Error: " + e);
+        execResult = {"status" : Cordova.callbackStatus.ERROR,
+                      "message" : e.message};
+    }
+
+    return execResult;
+}
+
+module.exports = {
+    exec: function (win, fail, clazz, action, args) {
+        var result = webworks.exec(win, fail, clazz, action, args);
+
+        //We got a sync result or a not found from WW that we can pass on to get a native mixin
+        //For async calls there's nothing to do
+        if (result.status === Cordova.callbackStatus.CLASS_NOT_FOUND_EXCEPTION  ||
+                result.status === Cordova.callbackStatus.INVALID_ACTION ||
+                result.status === Cordova.callbackStatus.OK) {
+            if (plugins[clazz]) {
+                return plugins[clazz].execute(result.message, action, args, win, fail);
+            } else {
+                result = _exec(win, fail, clazz, action, args);
+            }
+        }
+
+        return result;
+    },
+    resume: org.apache.cordova.JavaPluginManager.resume,
+    pause: org.apache.cordova.JavaPluginManager.pause,
+    destroy: org.apache.cordova.JavaPluginManager.destroy
+};

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/blackberry/notification.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/blackberry/notification.js b/lib0/plugin/blackberry/notification.js
new file mode 100644
index 0000000..83bc77c
--- /dev/null
+++ b/lib0/plugin/blackberry/notification.js
@@ -0,0 +1,53 @@
+var exec = require('cordova/exec');
+
+/**
+ * Provides BlackBerry enhanced notification API.
+ */
+module.exports = {
+    activityStart : function(title, message) {
+        // If title and message not specified then mimic Android behavior of
+        // using default strings.
+        if (typeof title === "undefined" && typeof message == "undefined") {
+            title = "Busy";
+            message = 'Please wait...';
+        }
+
+        exec(null, null, 'Notification', 'activityStart', [ title, message ]);
+    },
+
+    /**
+     * Close an activity dialog
+     */
+    activityStop : function() {
+        exec(null, null, 'Notification', 'activityStop', []);
+    },
+
+    /**
+     * Display a progress dialog with progress bar that goes from 0 to 100.
+     *
+     * @param {String}
+     *            title Title of the progress dialog.
+     * @param {String}
+     *            message Message to display in the dialog.
+     */
+    progressStart : function(title, message) {
+        exec(null, null, 'Notification', 'progressStart', [ title, message ]);
+    },
+
+    /**
+     * Close the progress dialog.
+     */
+    progressStop : function() {
+        exec(null, null, 'Notification', 'progressStop', []);
+    },
+
+    /**
+     * Set the progress dialog value.
+     *
+     * @param {Number}
+     *            value 0-100
+     */
+    progressValue : function(value) {
+        exec(null, null, 'Notification', 'progressValue', [ value ]);
+    },
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/capture.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/capture.js b/lib0/plugin/capture.js
new file mode 100644
index 0000000..e088ba6
--- /dev/null
+++ b/lib0/plugin/capture.js
@@ -0,0 +1,72 @@
+var exec = require('cordova/exec'),
+    MediaFile = require('cordova/plugin/MediaFile');
+
+/**
+ * Launches a capture of different types.
+ *
+ * @param (DOMString} type 
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+function _capture(type, successCallback, errorCallback, options) {
+    var win = function(pluginResult) {
+        var mediaFiles = [];
+        var i;
+        for (i = 0; i < pluginResult.length; i++) {
+            var mediaFile = new MediaFile();
+            mediaFile.name = pluginResult[i].name;
+            mediaFile.fullPath = pluginResult[i].fullPath;
+            mediaFile.type = pluginResult[i].type;
+            mediaFile.lastModifiedDate = pluginResult[i].lastModifiedDate;
+            mediaFile.size = pluginResult[i].size;
+            mediaFiles.push(mediaFile);
+        }
+        successCallback(mediaFiles);
+    };
+    exec(win, errorCallback, "Capture", type, [options]);
+}
+/**
+ * The Capture interface exposes an interface to the camera and microphone of the hosting device.
+ */
+function Capture() {
+	this.supportedAudioModes = [];
+	this.supportedImageModes = [];
+	this.supportedVideoModes = [];
+}
+
+/**
+ * Launch audio recorder application for recording audio clip(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureAudioOptions} options
+ */
+Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
+    _capture("captureAudio", successCallback, errorCallback, options);
+};
+
+/**
+ * Launch camera application for taking image(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureImageOptions} options
+ */
+Capture.prototype.captureImage = function(successCallback, errorCallback, options){
+    _capture("captureImage", successCallback, errorCallback, options);
+};
+
+/**
+ * Launch device camera application for recording video(s).
+ *
+ * @param {Function} successCB
+ * @param {Function} errorCB
+ * @param {CaptureVideoOptions} options
+ */
+Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
+    _capture("captureVideo", successCallback, errorCallback, options);
+};
+
+
+module.exports = new Capture();

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/compass.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/compass.js b/lib0/plugin/compass.js
new file mode 100644
index 0000000..1685f2d
--- /dev/null
+++ b/lib0/plugin/compass.js
@@ -0,0 +1,90 @@
+var exec = require('cordova/exec'),
+    utils = require('cordova/utils'),
+    CompassHeading = require('cordova/plugin/CompassHeading'),
+    CompassError = require('cordova/plugin/CompassError'),
+    timers = {},
+    compass = {
+        /**
+         * Asynchronously acquires 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 {CompassOptions} options The options for getting the heading data (not used).
+         */
+        getCurrentHeading:function(successCallback, errorCallback) {
+            // successCallback required
+            if (typeof successCallback !== "function") {
+              console.log("Compass Error: successCallback is not a function");
+              return;
+            }
+
+            // errorCallback optional
+            if (errorCallback && (typeof errorCallback !== "function")) {
+              console.log("Compass Error: errorCallback is not a function");
+              return;
+            }
+
+            var win = function(result) {
+                var ch = new CompassHeading(result.magneticHeading, result.trueHeading, result.headingAccuracy, result.timestamp);
+                successCallback(ch);
+            };
+            var fail = function(code) {
+                var ce = new CompassError(code);
+                errorCallback(ce);
+            }
+            
+            // Get heading
+            exec(win, fail, "Compass", "getHeading", []);
+        },
+
+        /**
+         * Asynchronously acquires 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.
+         */
+        watchHeading:function(successCallback, errorCallback, options) {
+            // Default interval (100 msec)
+            var frequency = (options !== undefined && options.frequency !== undefined) ? options.frequency : 100;
+
+            // successCallback required
+            if (typeof successCallback !== "function") {
+              console.log("Compass Error: successCallback is not a function");
+              return;
+            }
+
+            // errorCallback optional
+            if (errorCallback && (typeof errorCallback !== "function")) {
+              console.log("Compass Error: errorCallback is not a function");
+              return;
+            }
+
+            // Start watch timer to get headings
+            var id = utils.createUUID();
+
+            timers[id] = window.setInterval(function() {
+                compass.getCurrentHeading(successCallback, errorCallback);
+            }, frequency);
+
+            return id;
+        },
+
+        /**
+         * Clears the specified heading watch.
+         * @param {String} watchId The ID of the watch returned from #watchHeading.
+         */
+        clearWatch:function(id) {
+            // Stop javascript timer & remove from timer list
+            if (id && timers[id]) {
+              clearInterval(timers[id]);
+              delete timers[id];
+            }
+        }
+        // TODO: add the filter-based iOS-only methods
+    };
+
+module.exports = compass;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/contacts.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/contacts.js b/lib0/plugin/contacts.js
new file mode 100644
index 0000000..7212f7b
--- /dev/null
+++ b/lib0/plugin/contacts.js
@@ -0,0 +1,57 @@
+var exec = require('cordova/exec'),
+    ContactError = require('cordova/plugin/ContactError'),
+    Contact = require('cordova/plugin/Contact');
+
+/**
+* Represents a group of Contacts.
+* @constructor
+*/
+var contacts = {
+    /**
+     * Returns an array of Contacts matching the search criteria.
+     * @param fields that should be searched
+     * @param successCB success callback
+     * @param errorCB error callback
+     * @param {ContactFindOptions} options that can be applied to contact searching
+     * @return array of Contacts matching search criteria
+     */
+    find:function(fields, successCB, errorCB, options) {
+        if (!successCB) {
+            throw new TypeError("You must specify a success callback for the find command.");
+        }
+        if (!fields || (fields instanceof Array && fields.length === 0)) {
+            if (typeof errorCB === "function") {
+                errorCB(new ContactError(ContactError.INVALID_ARGUMENT_ERROR));
+            }
+        } else {
+            var win = function(result) {
+                var cs = [];
+                for (var i = 0, l = result.length; i < l; i++) {
+                    cs.push(contacts.create(result[i]));
+                }
+                successCB(cs);
+            };
+            exec(win, errorCB, "Contacts", "search", [fields, options]);
+        }
+    },
+
+    /**
+     * This function creates a new contact, but it does not persist the contact
+     * to device storage. To persist the contact to device storage, invoke
+     * contact.save().
+     * @param properties an object who's properties will be examined to create a new Contact
+     * @returns new Contact object
+     */
+    create:function(properties) {
+        var i;
+        var contact = new Contact();
+        for (i in properties) {
+            if (typeof contact[i] !== 'undefined' && properties.hasOwnProperty(i)) {
+                contact[i] = properties[i];
+            }
+        }
+        return contact;
+    }
+};
+
+module.exports = contacts;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/errgen/device.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/errgen/device.js b/lib0/plugin/errgen/device.js
new file mode 100644
index 0000000..4e05777
--- /dev/null
+++ b/lib0/plugin/errgen/device.js
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
+ * phone, etc.
+ * @constructor
+ */
+
+//------------------------------------------------------------------------------
+function Device() {
+    window.DeviceInfo = {}
+    
+    this.platform  = "errgen"
+    this.version   = "any"
+    this.name      = "errgen"
+    this.phonegap  = {}
+    this.gap       = this.phonegap
+    this.uuid      = "1234-5678-9012-3456"
+    this.available = true
+    
+    require('cordova/channel').onCordovaInfoReady.fire()
+}
+
+//------------------------------------------------------------------------------
+module.exports = window.DeviceInfo = new Device()

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/geolocation.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/geolocation.js b/lib0/plugin/geolocation.js
new file mode 100644
index 0000000..e1169f6
--- /dev/null
+++ b/lib0/plugin/geolocation.js
@@ -0,0 +1,94 @@
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    PositionError = require('cordova/plugin/PositionError'),
+    Position = require('cordova/plugin/Position');
+
+var timers = {};   // list of timers in use
+
+// Returns default params, overrides if provided with values
+function parseParameters(options) {
+    var opt = {
+        maximumAge: 10000,
+        enableHighAccuracy: false,
+        timeout: 10000
+    };
+
+    if (options) {
+        if (options.maximumAge !== undefined) {
+            opt.maximumAge = options.maximumAge;
+        }
+        if (options.enableHighAccuracy !== undefined) {
+            opt.enableHighAccuracy = options.enableHighAccuracy;
+        }
+        if (options.timeout !== undefined) {
+            opt.timeout = options.timeout;
+        }
+    }
+
+    return opt;
+}
+
+var geolocation = {
+    /**
+   * Asynchronously aquires the current position.
+   *
+   * @param {Function} successCallback    The function to call when the position data is available
+   * @param {Function} errorCallback      The function to call when there is an error getting the heading position. (OPTIONAL)
+   * @param {PositionOptions} options     The options for getting the position data. (OPTIONAL)
+   */
+    getCurrentPosition:function(successCallback, errorCallback, options) {
+        options = parseParameters(options);
+
+        var win = function(p) {
+            successCallback(new Position(
+                {
+                    latitude:p.latitude,
+                    longitude:p.longitude,
+                    altitude:p.altitude,
+                    accuracy:p.accuracy,
+                    heading:p.heading,
+                    velocity:p.velocity,
+                    altitudeAccuracy:p.altitudeAccuracy
+                },
+                p.timestamp || new Date()
+            ));
+        };
+        var fail = function(e) {
+            errorCallback(new PositionError(e.code, e.message));
+        };
+
+        exec(win, fail, "Geolocation", "getLocation", [options.enableHighAccuracy, options.timeout, options.maximumAge]); 
+    },
+    /**
+     * Asynchronously watches the geolocation for changes to geolocation.  When a change occurs,
+     * the successCallback is called with the new location.
+     *
+     * @param {Function} successCallback    The function to call each time the location data is available
+     * @param {Function} errorCallback      The function to call when there is an error getting the location data. (OPTIONAL)
+     * @param {PositionOptions} options     The options for getting the location data such as frequency. (OPTIONAL)
+     * @return String                       The watch id that must be passed to #clearWatch to stop watching.
+     */
+    watchPosition:function(successCallback, errorCallback, options) {
+        options = parseParameters(options);
+
+        var id = utils.createUUID();
+        timers[id] = window.setInterval(function() {
+            geolocation.getCurrentPosition(successCallback, errorCallback, options);
+        }, options.timeout);
+
+        return id;
+    },
+    /**
+     * Clears the specified heading watch.
+     *
+     * @param {String} id       The ID of the watch returned from #watchPosition
+     */
+    clearWatch:function(id) {
+        if (id && timers[id] !== undefined) {
+            window.clearInterval(timers[id]);
+            delete timers[id];
+        }
+    }
+};
+
+module.exports = geolocation;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/76f64673/lib0/plugin/ios/Entry.js
----------------------------------------------------------------------
diff --git a/lib0/plugin/ios/Entry.js b/lib0/plugin/ios/Entry.js
new file mode 100644
index 0000000..230da8a
--- /dev/null
+++ b/lib0/plugin/ios/Entry.js
@@ -0,0 +1,7 @@
+module.exports = {
+    toURL:function() {
+        // TODO: refactor path in a cross-platform way so we can eliminate 
+        // these kinds of platform-specific hacks.
+        return "file://localhost" + this.fullPath;
+    }
+}