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 21:43:06 UTC
[1/7] git commit: [CB-280] Improve layout of cordova-js scripts
Updated Branches:
refs/heads/master 3b06e03e6 -> e50b7ef68
[CB-280] Improve layout of cordova-js scripts
https://issues.apache.org/jira/browse/CB-280
This was basically a refactoring of the lib subdirectories,
without changing any of the code IN the lib subdirectories.
Other changes:
- build/packager.js
modified to build based on the new lib subdirectories
- Jakefile
modified to call a new method in build/packager
- README.md
updated file/directory locations
- test/runner.js
changed the runner to run off a new 'test' platform,
instead of using an on-the-fly platform it pieced
together when the tests run.
also changed the static() URL bits for the
browser-based test - it was pulling in more
than it needed
- test/suite.html
changed the urls of the jasmine files, based
on the updates to the static() URL bits in the
runner.js
-
- lib/common/exec.js
- lib/common/platform.js
stub files that will be replaced with platform-specific
ones
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/commit/e50b7ef6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/tree/e50b7ef6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/diff/e50b7ef6
Branch: refs/heads/master
Commit: e50b7ef686e865cc8b6bb7caa5e05292de4148a6
Parents: 3b06e03
Author: Patrick Mueller <pm...@apache.org>
Authored: Thu Mar 22 16:28:17 2012 -0400
Committer: Patrick Mueller <pm...@apache.org>
Committed: Thu Mar 22 16:28:17 2012 -0400
----------------------------------------------------------------------
Jakefile | 13 +-
README.md | 72 ++--
build/packager.js | 279 ++++++-----
lib/android/exec.js | 82 +++
lib/android/platform.js | 131 +++++
lib/android/plugin/android/app.js | 71 +++
lib/android/plugin/android/callback.js | 85 ++++
lib/android/plugin/android/device.js | 92 ++++
lib/android/plugin/android/polling.js | 33 ++
lib/android/plugin/android/storage.js | 377 ++++++++++++++
lib/blackberry/exec.js | 56 +++
lib/blackberry/platform.js | 179 +++++++
lib/blackberry/plugin/blackberry/Contact.js | 386 +++++++++++++++
lib/blackberry/plugin/blackberry/ContactUtils.js | 353 +++++++++++++
lib/blackberry/plugin/blackberry/DirectoryEntry.js | 239 +++++++++
lib/blackberry/plugin/blackberry/Entry.js | 87 ++++
lib/blackberry/plugin/blackberry/app.js | 51 ++
lib/blackberry/plugin/blackberry/contacts.js | 62 +++
lib/blackberry/plugin/blackberry/device.js | 23 +
lib/blackberry/plugin/blackberry/manager.js | 87 ++++
lib/blackberry/plugin/blackberry/notification.js | 53 ++
lib/bootstrap.js | 69 ---
lib/bootstrap/errgen.js | 20 -
lib/bootstrap/playbook.js | 1 -
lib/builder.js | 86 ----
lib/channel.js | 207 --------
lib/common/builder.js | 86 ++++
lib/common/channel.js | 207 ++++++++
lib/common/common.js | 180 +++++++
lib/common/exec.js | 1 +
lib/common/platform.js | 1 +
lib/common/plugin/Acceleration.js | 8 +
lib/common/plugin/Camera.js | 84 ++++
lib/common/plugin/CameraConstants.js | 20 +
lib/common/plugin/CaptureAudioOptions.js | 13 +
lib/common/plugin/CaptureError.js | 19 +
lib/common/plugin/CaptureImageOptions.js | 11 +
lib/common/plugin/CaptureVideoOptions.js | 13 +
lib/common/plugin/CompassError.js | 13 +
lib/common/plugin/CompassHeading.js | 8 +
lib/common/plugin/ConfigurationData.js | 15 +
lib/common/plugin/Connection.js | 12 +
lib/common/plugin/Contact.js | 177 +++++++
lib/common/plugin/ContactAddress.js | 25 +
lib/common/plugin/ContactError.js | 21 +
lib/common/plugin/ContactField.js | 16 +
lib/common/plugin/ContactFindOptions.js | 13 +
lib/common/plugin/ContactName.js | 20 +
lib/common/plugin/ContactOrganization.js | 23 +
lib/common/plugin/Coordinates.js | 43 ++
lib/common/plugin/DirectoryEntry.js | 80 +++
lib/common/plugin/DirectoryReader.js | 41 ++
lib/common/plugin/Entry.js | 198 ++++++++
lib/common/plugin/File.js | 18 +
lib/common/plugin/FileEntry.js | 63 +++
lib/common/plugin/FileError.js | 25 +
lib/common/plugin/FileReader.js | 249 ++++++++++
lib/common/plugin/FileSystem.js | 17 +
lib/common/plugin/FileTransfer.js | 68 +++
lib/common/plugin/FileTransferError.js | 13 +
lib/common/plugin/FileUploadOptions.js | 16 +
lib/common/plugin/FileUploadResult.js | 11 +
lib/common/plugin/FileWriter.js | 253 ++++++++++
lib/common/plugin/Flags.js | 15 +
lib/common/plugin/LocalFileSystem.js | 13 +
lib/common/plugin/Media.js | 187 +++++++
lib/common/plugin/MediaError.js | 16 +
lib/common/plugin/MediaFile.js | 56 +++
lib/common/plugin/MediaFileData.js | 18 +
lib/common/plugin/Metadata.js | 10 +
lib/common/plugin/Position.js | 8 +
lib/common/plugin/PositionError.js | 17 +
lib/common/plugin/ProgressEvent.js | 46 ++
lib/common/plugin/accelerometer.js | 95 ++++
lib/common/plugin/battery.js | 87 ++++
lib/common/plugin/capture.js | 72 +++
lib/common/plugin/compass.js | 90 ++++
lib/common/plugin/contacts.js | 57 +++
lib/common/plugin/geolocation.js | 94 ++++
lib/common/plugin/network.js | 59 +++
lib/common/plugin/notification.js | 56 +++
lib/common/plugin/requestFileSystem.js | 40 ++
lib/common/plugin/resolveLocalFileSystemURI.js | 41 ++
lib/common/utils.js | 94 ++++
lib/errgen/exec.js | 107 ++++
lib/errgen/platform.js | 28 +
lib/errgen/plugin/errgen/device.js | 42 ++
lib/exec/android.js | 82 ---
lib/exec/blackberry.js | 56 ---
lib/exec/errgen.js | 107 ----
lib/exec/ios.js | 93 ----
lib/exec/playbook.js | 56 ---
lib/exec/test.js | 1 -
lib/exec/wp7.js | 57 ---
lib/ios/exec.js | 93 ++++
lib/ios/platform.js | 41 ++
lib/ios/plugin/ios/Entry.js | 7 +
lib/ios/plugin/ios/FileReader.js | 87 ++++
lib/ios/plugin/ios/console.js | 102 ++++
lib/ios/plugin/ios/device.js | 30 ++
lib/ios/plugin/ios/nativecomm.js | 10 +
lib/ios/plugin/ios/notification.js | 7 +
lib/platform/android.js | 131 -----
lib/platform/blackberry.js | 179 -------
lib/platform/common.js | 180 -------
lib/platform/errgen.js | 28 -
lib/platform/ios.js | 41 --
lib/platform/playbook.js | 16 -
lib/platform/wp7.js | 16 -
lib/playbook/exec.js | 56 +++
lib/playbook/platform.js | 16 +
lib/playbook/plugin/playbook/device.js | 23 +
lib/playbook/plugin/playbook/manager.js | 321 ++++++++++++
lib/plugin/Acceleration.js | 8 -
lib/plugin/Camera.js | 84 ----
lib/plugin/CameraConstants.js | 20 -
lib/plugin/CaptureAudioOptions.js | 13 -
lib/plugin/CaptureError.js | 19 -
lib/plugin/CaptureImageOptions.js | 11 -
lib/plugin/CaptureVideoOptions.js | 13 -
lib/plugin/CompassError.js | 13 -
lib/plugin/CompassHeading.js | 8 -
lib/plugin/ConfigurationData.js | 15 -
lib/plugin/Connection.js | 12 -
lib/plugin/Contact.js | 177 -------
lib/plugin/ContactAddress.js | 25 -
lib/plugin/ContactError.js | 21 -
lib/plugin/ContactField.js | 16 -
lib/plugin/ContactFindOptions.js | 13 -
lib/plugin/ContactName.js | 20 -
lib/plugin/ContactOrganization.js | 23 -
lib/plugin/Coordinates.js | 43 --
lib/plugin/DirectoryEntry.js | 80 ---
lib/plugin/DirectoryReader.js | 41 --
lib/plugin/Entry.js | 198 --------
lib/plugin/File.js | 18 -
lib/plugin/FileEntry.js | 63 ---
lib/plugin/FileError.js | 25 -
lib/plugin/FileReader.js | 249 ----------
lib/plugin/FileSystem.js | 17 -
lib/plugin/FileTransfer.js | 68 ---
lib/plugin/FileTransferError.js | 13 -
lib/plugin/FileUploadOptions.js | 16 -
lib/plugin/FileUploadResult.js | 11 -
lib/plugin/FileWriter.js | 253 ----------
lib/plugin/Flags.js | 15 -
lib/plugin/LocalFileSystem.js | 13 -
lib/plugin/Media.js | 187 -------
lib/plugin/MediaError.js | 16 -
lib/plugin/MediaFile.js | 56 ---
lib/plugin/MediaFileData.js | 18 -
lib/plugin/Metadata.js | 10 -
lib/plugin/Position.js | 8 -
lib/plugin/PositionError.js | 17 -
lib/plugin/ProgressEvent.js | 46 --
lib/plugin/accelerometer.js | 95 ----
lib/plugin/android/app.js | 71 ---
lib/plugin/android/callback.js | 85 ----
lib/plugin/android/device.js | 92 ----
lib/plugin/android/polling.js | 33 --
lib/plugin/android/storage.js | 377 --------------
lib/plugin/battery.js | 87 ----
lib/plugin/blackberry/Contact.js | 386 ---------------
lib/plugin/blackberry/ContactUtils.js | 353 -------------
lib/plugin/blackberry/DirectoryEntry.js | 239 ---------
lib/plugin/blackberry/Entry.js | 87 ----
lib/plugin/blackberry/app.js | 51 --
lib/plugin/blackberry/contacts.js | 62 ---
lib/plugin/blackberry/device.js | 23 -
lib/plugin/blackberry/manager.js | 87 ----
lib/plugin/blackberry/notification.js | 53 --
lib/plugin/capture.js | 72 ---
lib/plugin/compass.js | 90 ----
lib/plugin/contacts.js | 57 ---
lib/plugin/errgen/device.js | 42 --
lib/plugin/geolocation.js | 94 ----
lib/plugin/ios/Entry.js | 7 -
lib/plugin/ios/FileReader.js | 87 ----
lib/plugin/ios/console.js | 102 ----
lib/plugin/ios/device.js | 30 --
lib/plugin/ios/nativecomm.js | 10 -
lib/plugin/ios/notification.js | 7 -
lib/plugin/network.js | 59 ---
lib/plugin/notification.js | 56 ---
lib/plugin/playbook/device.js | 23 -
lib/plugin/playbook/manager.js | 321 ------------
lib/plugin/requestFileSystem.js | 40 --
lib/plugin/resolveLocalFileSystemURI.js | 41 --
lib/plugin/webworks/manager.js | 14 -
lib/plugin/wp7/device.js | 26 -
lib/require.js | 43 --
lib/scripts/bootstrap-errgen.js | 20 +
lib/scripts/bootstrap-playbook.js | 1 +
lib/scripts/bootstrap.js | 69 +++
lib/scripts/require.js | 43 ++
lib/test/exec.js | 1 +
lib/utils.js | 94 ----
lib/webworks/plugin/webworks/manager.js | 14 +
lib/wp7/exec.js | 57 +++
lib/wp7/platform.js | 16 +
lib/wp7/plugin/wp7/device.js | 26 +
test/runner.js | 25 +-
test/suite.html | 8 +-
203 files changed, 7150 insertions(+), 7077 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/Jakefile
----------------------------------------------------------------------
diff --git a/Jakefile b/Jakefile
index af05af6..f8cd07a 100644
--- a/Jakefile
+++ b/Jakefile
@@ -22,12 +22,13 @@ task('build', ['clean'], function () {
var packager = require("./build/packager");
- packager.write("blackberry");
- packager.write("playbook");
- packager.write("ios");
- packager.write("wp7");
- packager.write("android");
- packager.write("errgen");
+ packager.generate("blackberry");
+ packager.generate("playbook");
+ packager.generate("ios");
+ packager.generate("wp7");
+ packager.generate("android");
+ packager.generate("errgen");
+ packager.generate("test");
});
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 5dd4e92..b8ca0dc 100644
--- a/README.md
+++ b/README.md
@@ -5,45 +5,57 @@ A unified JavaScript layer for [Apache Cordova](http://incubator.apache.org/proj
cordova-js
|
|-build/
- | Will contain any build modules (currently nothing here as it is all hacked
- | into the JakeFile)
+ | Will contain any build modules (currently nothing here as it is all
+ | hacked into the JakeFile)
|
|-lib
- | |-bootstrap.js
- | | Code to bootstrap the Cordova platform, inject APIs and fire events
- | |
- | |-builder.js
- | | Injects in our classes onto window and navigator (or wherever else is needed)
- | |
- | |-channel.js
- | | A pub/sub implementation to handle custom framework events
- | |
| |-cordova.js
| | Common Cordova stuff such as callback handling and
| | window/document add/removeEventListener hijacking
| |
- | |-require.js
- | | Our own module definition and require implementation.
+ | |-common/
+ | | Contains the common-across-platforms base modules
+ | |
+ | |-common/builder.js
+ | | Injects in our classes onto window and navigator (or wherever else
+ | | is needed)
+ | |
+ | |-common/channel.js
+ | | A pub/sub implementation to handle custom framework events
| |
- | |-utils.js
+ | |-common/common.js
+ | | Common locations to add Cordova objects to browser globals.
+ | |
+ | |-common/exec.js
+ | | Stub for platform's specific version of exec.js
+ | |
+ | |-common/platform.js
+ | | Stub for platform's specific version of platform.js
+ | |
+ | |-common/utils.js
| | General purpose JS utility stuff: closures, uuids, object
| | cloning, extending prototypes
| |
- | |-exec/
- | | Contains the platform specific definitions of the exec method
+ | |-common/plugin
+ | | Contains the common-across-platforms plugin modules
| |
- | |-platform/
- | | Definitions of each platform that help us describe where
- | | and what to put on the window object, and what to run to
- | | initialize the platform. A common set of globals are also
- | | defined (common.js)
+ | |-scripts/
+ | | Contains non-module JavaScript source that gets added to the
+ | | resulting cordova.<platform>.js files closures, uuids, object
+ | |
+ | |-scripts/bootstrap.js
+ | | Code to bootstrap the Cordova platform, inject APIs and fire events
| |
- | |-plugin/
- | | | All API definitions as plugins, ones common to all
- | | | platforms reside at the top level...
- | | `-<platform>
- | | ... and platform-specific ones reside in their respective
- | | folders
+ | |-scripts/require.js
+ | | Our own module definition and require implementation.
+ | |
+ | |-<platform>/
+ | | Contains the platform-specific base modules.
+ | |
+ | |-<platform>/plugin
+ | | Contains the platform-specific plugin modules.
+
+The way the resulting `cordova.<platform>.js` files will be built is by combining the scripts in the `lib/scripts` directory with modules from the `lib/common` and `lib/<platform>` directories. For cases where there is the same named module in `lib/common` and `lib/<platform>`, the `lib/<platform>` version wins. For instance, every `lib/<platform>` includes an `exec.js`, and there is also a version in `lib/common`, so the `lib/<platform>` version will always be used. In fact, the `lib/common` one will throw errors, so if you build a new platform and forget `exec.js`, the resulting `cordova.<platform>.js` file will also throw errors.
# Building
@@ -77,13 +89,13 @@ This will run the `build` and `test` tasks by default. All of the available task
The `build/packager.js` tool is a node.js script that concatenates all of the core Cordova plugins in this repository into a `cordova.<platform>.js` file under the `pkg/` folder. It also wraps the plugins with a RequireJS-compatible module syntax that works in both browser and node environments. We end up with a cordova.js file that wraps each Cordova plugin into its own module.
-Cordova defines a `channel` module under `lib/channel.js`, which is a publish/subscribe implementation that the project uses for event management.
+Cordova defines a `channel` module under `lib/common/channel.js`, which is a publish/subscribe implementation that the project uses for event management.
-The Cordova native-to-webview bridge is initialized in `lib/bootstrap.js`. This file attaches the `boot` function to the `channel.onNativeReady` event - fired by native with a call to:
+The Cordova native-to-webview bridge is initialized in `lib/scripts/bootstrap.js`. This file attaches the `boot` function to the `channel.onNativeReady` event - fired by native with a call to:
cordova.require('cordova/channel).onNativeReady.fire()
-The `boot` method does all the work. First, it grabs the common platform definition (under `lib/platform/common.js`) and injects all of the objects defined there onto `window` and other global namespaces. Next, it grabs all of the platform-specific object definitions (as defined under `lib/platform/<platform>.js`) and overrides those onto `window`. Finally, it calls the platform-specific `initialize` function (located in the platform definition). At this point, Cordova is fully initialized and ready to roll. Last thing we do is wait for the `DOMContentLoaded` event to fire to make sure the page has loaded properly. Once that is done, Cordova fires the `deviceready` event where you can safely attach functions that consume the Cordova APIs.
+The `boot` method does all the work. First, it grabs the common platform definition (under `lib/common/common.js`) and injects all of the objects defined there onto `window` and other global namespaces. Next, it grabs all of the platform-specific object definitions (as defined under `lib/<platform>/platform.js`) and overrides those onto `window`. Finally, it calls the platform-specific `initialize` function (located in the platform definition). At this point, Cordova is fully initialized and ready to roll. Last thing we do is wait for the `DOMContentLoaded` event to fire to make sure the page has loaded properly. Once that is done, Cordova fires the `deviceready` event where you can safely attach functions that consume the Cordova APIs.
# Testing
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/build/packager.js
----------------------------------------------------------------------
diff --git a/build/packager.js b/build/packager.js
index 39a9704..6569020 100644
--- a/build/packager.js
+++ b/build/packager.js
@@ -1,135 +1,184 @@
-var util = require('util'),
- debug = false,
- fs = require('fs');
-
-// (Recursively) list contents of a directory
-function walk(dir, doRecursive) {
- var results = [];
- try {
- var list = fs.readdirSync(dir);
- for (var i = 0, l = list.length; i < l; i++) {
- var file = list[i];
- file = dir + '/' + file;
- var stat = fs.statSync(file);
- if (stat && doRecursive && stat.isDirectory()) {
- results = results.concat(walk(file,doRecursive));
- } else {
- if (list[i] != ".DS_Store") {
- results.push(file);
- }
- }
- }
- } catch (e) {
- //do nothing
- }
- return results;
-}
-
-// Simply inline includes the specified file(s) with an optional transform function.
-function include(files, transform) {
- files = files.map ? files : [files];
- return files.map(function (file) {
- try {
- var str = fs.readFileSync(file, "utf-8") + "\n";
- str = transform ? transform(str, file) : str;
- str = debug ? "try {" + str + "} catch (e) { alert('" + file + ":' + e);}" : str;
- return str;
- } catch (e) {
- //do nothing
- }
- }).join('\n');
+var fs = require('fs')
+var util = require('util')
+var path = require('path')
+
+var packager = module.exports
+
+//------------------------------------------------------------------------------
+packager.generate = function(platform) {
+ var time = new Date().valueOf()
+
+ var libraryRelease = packager.bundle(platform, false)
+ var libraryDebug = packager.bundle(platform, true)
+
+ time = new Date().valueOf() - time
+
+ var outFile
+
+ outFile = path.join('pkg', 'cordova.' + platform + '.js')
+ fs.writeFileSync(outFile, libraryRelease, 'utf8')
+
+ outFile = path.join('pkg', 'cordova.' + platform + '-debug.js')
+ fs.writeFileSync(outFile, libraryDebug, 'utf8')
+
+ console.log('generated platform: ' + platform + ' in ' + time + 'ms')
}
-// Includes the specified file(s) with optional overriding id
-// Wraps the specified file(s) in a define statement which implicitly
-// creates a closure as well.
-function drop(files, id) {
- return include(files, function(file, path) {
- var define_id = (typeof id != 'undefined' && id.length > 0 ? id : path.replace(/lib\//, "cordova/").replace(/\.js$/, ''));
- return "define('" + define_id + "', function(require, exports, module) {\n" + file + "});\n";
- });
-}
-
-module.exports = {
- modules: function (platform) {
- var baseFiles = [
- "lib/utils.js",
- "lib/builder.js"
- ],
- platformFiles = walk('lib/plugin/' + platform, true),
- output = "";
-
- //HACK: ummm .... we really need to figure out this webworks common file stuff
- if (platform === "blackberry" || platform === "playbook") {
- platformFiles = platformFiles.concat(walk('lib/plugin/webworks', true));
- }
-
- //include all common platform files that are under lib/plugin
- baseFiles = baseFiles.concat(walk('lib/plugin'));
-
- //include require
- output += include("lib/require.js");
-
- //include channel
- output += drop('lib/channel.js', 'cordova/channel');
+//------------------------------------------------------------------------------
+packager.bundle = function(platform, debug) {
+ var modules = collectFiles('lib/common')
+ var scripts = collectFiles('lib/scripts')
+
+ modules[''] = 'lib/cordova.js'
+
+ if (['playbook', 'blackberry'].indexOf(platform) > -1) {
+ copyProps(modules, collectFiles(path.join('lib', 'webworks')))
+ }
+
+ copyProps(modules, collectFiles(path.join('lib', platform)))
+
+ var output = []
+
+ // write header
+ output.push('/*\n' + getContents('LICENSE-for-js-file.txt') + '\n*/')
+ output.push('\n;(function() {\n')
+
+ // write initial scripts
+ if (!scripts['require']) {
+ throw new Error("didn't find a script for 'require'")
+ }
+
+ writeScript(output, scripts['require'], debug)
+
+ // write modules
+ var moduleIds = Object.keys(modules)
+ moduleIds.sort()
+
+ for (var i=0; i<moduleIds.length; i++) {
+ var moduleId = moduleIds[i]
+
+ writeModule(output, modules[moduleId], moduleId, debug)
+ }
- //include cordova
- output += drop('lib/cordova.js', 'cordova');
+ output.push("\nwindow.cordova = require('cordova');\n")
- //include exec
- output += drop('lib/exec/' + platform + '.js', 'cordova/exec');
+ // write final scripts
+ if (!scripts['bootstrap']) {
+ throw new Error("didn't find a script for 'bootstrap'")
+ }
+
+ writeScript(output, scripts['bootstrap'], debug)
+
+ var bootstrapPlatform = 'bootstrap-' + platform
+ if (scripts[bootstrapPlatform]) {
+ writeScript(output, scripts[bootstrapPlatform], debug)
+ }
- //include common platform defn
- output += drop('lib/platform/common.js', 'cordova/common');
+ // write trailer
+ output.push('\n})();')
- //include platform defn
- output += drop('lib/platform/' + platform + '.js', 'cordova/platform');
+ return output.join('\n')
+}
- //include common modules
- output += drop(baseFiles);
+//------------------------------------------------------------------------------
+var CollectedFiles = {}
- //include platform specific modules
- output += drop(platformFiles);
+function collectFiles(dir, id) {
+ if (!id) id = ''
+
+ if (CollectedFiles[dir]) {
+ return copyProps({}, CollectedFiles[dir])
+ }
- return output;
- },
+ var result = {}
+
+ var entries = fs.readdirSync(dir)
+
+ entries = entries.filter(function(entry) {
+ if (entry.match(/\.js$/)) return true
+
+ var stat = fs.statSync(path.join(dir, entry))
+ if (stat.isDirectory()) return true
+ })
- bundle: function (platform) {
- console.log("building platform: " + platform);
+ entries.forEach(function(entry) {
+ var moduleId = path.join(id, entry)
+ var fileName = path.join(dir, entry)
- var output = "";
+ var stat = fs.statSync(fileName)
+ if (stat.isDirectory()) {
+ copyProps(result, collectFiles(fileName, moduleId))
+ }
+ else {
+ moduleId = getModuleId(moduleId)
+ result[moduleId] = fileName
+ }
+ })
+
+ CollectedFiles[dir] = result
+
+ return copyProps({}, result)
+}
- //include LICENSE
- output += include("LICENSE-for-js-file.txt", function (file) {
- return "/*\n" + file + "*/\n";
- });
+//------------------------------------------------------------------------------
+function writeScript(oFile, fileName, debug) {
+ var contents = getContents(fileName, 'utf8')
+
+ writeContents(oFile, fileName, contents, debug)
+}
- // wrap the entire thing in one more closure
- // closure closure closure
- output += "(function() {\n";
+//------------------------------------------------------------------------------
+function writeModule(oFile, fileName, moduleId, debug) {
+ var contents = '\n' + getContents(fileName, 'utf8') + '\n'
+
+ moduleId = path.join('cordova', moduleId)
+
+ var signature = 'function(require, exports, module)'
+
+ contents = 'define("' + moduleId + '", ' + signature + ' {' + contents + '})\n'
+
+ writeContents(oFile, fileName, contents, debug)
+}
- //include modules
- output += this.modules(platform);
+//------------------------------------------------------------------------------
+var FileContents = {}
- // HACK: this gets done in bootstrap.js anyways, once native side is ready + domcontentloaded is fired.
- // TODO: Do we need it?
- output += "window.cordova = require('cordova');\n";
+function getContents(file) {
+ if (!FileContents.hasOwnProperty(file)) {
+ FileContents[file] = fs.readFileSync(file, 'utf8')
+ }
+
+ return FileContents[file]
+}
- //include bootstrap
- output += include('lib/bootstrap.js');
+//------------------------------------------------------------------------------
+function writeContents(oFile, fileName, contents, debug) {
+
+ if (debug) {
+ contents += '\n//@ sourceURL=' + fileName
+
+ contents = 'eval(' + JSON.stringify(contents) + ')'
+ }
+
+ else {
+ contents = '// file: ' + fileName + '\n' + contents
+ }
- // TODO/HACK: we don't need platform-specific bootstrap.
- // those can go into the init function inside the platform/*.js
- // files
- output += include('lib/bootstrap/' + platform + '.js');
+ oFile.push(contents)
+}
- // closing the closure har har
- output += "})();";
+//------------------------------------------------------------------------------
+function getModuleId(fileName) {
+ return fileName.match(/(.*)\.js$/)[1]
+}
- return output;
- },
- write: function (platform) {
- var output = this.bundle(platform);
- fs.writeFileSync(__dirname + "/../pkg/cordova." + platform + ".js", output);
+//------------------------------------------------------------------------------
+function copyProps(target, source) {
+ for (var key in source) {
+ if (!source.hasOwnProperty(key)) continue
+
+ target[key] = source[key]
}
-};
+
+ return target
+}
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/lib/android/exec.js
----------------------------------------------------------------------
diff --git a/lib/android/exec.js b/lib/android/exec.js
new file mode 100644
index 0000000..b3410bb
--- /dev/null
+++ b/lib/android/exec.js
@@ -0,0 +1,82 @@
+/**
+ * Execute a cordova command. It is up to the native side whether this action
+ * is synchronous or asynchronous. The native side can return:
+ * Synchronous: PluginResult object as a JSON string
+ * Asynchrounous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} success The success callback
+ * @param {Function} fail The fail callback
+ * @param {String} service The name of the service to use
+ * @param {String} action Action to be run in cordova
+ * @param {String[]} [args] Zero or more arguments to pass to the method
+ */
+var cordova = require('cordova');
+
+module.exports = function(success, fail, service, action, args) {
+ try {
+ var callbackId = service + cordova.callbackId++;
+ if (success || fail) {
+ cordova.callbacks[callbackId] = {success:success, fail:fail};
+ }
+
+ var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, action, callbackId, true]));
+
+ // If a result was returned
+ if (r.length > 0) {
+ eval("var v="+r+";");
+
+ // If status is OK, then return value back to caller
+ if (v.status === cordova.callbackStatus.OK) {
+
+ // If there is a success callback, then call it now with
+ // returned value
+ if (success) {
+ try {
+ success(v.message);
+ } catch (e) {
+ console.log("Error in success callback: " + callbackId + " = " + e);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ return v.message;
+ }
+
+ // If no result
+ else if (v.status === cordova.callbackStatus.NO_RESULT) {
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+
+ // If error, then display error
+ else {
+ console.log("Error: Status="+v.status+" Message="+v.message);
+
+ // If there is a fail callback, then call it now with returned value
+ if (fail) {
+ try {
+ fail(v.message);
+ }
+ catch (e1) {
+ console.log("Error in error callback: "+callbackId+" = "+e1);
+ }
+
+ // Clear callback if not expecting any more results
+ if (!v.keepCallback) {
+ delete cordova.callbacks[callbackId];
+ }
+ }
+ return null;
+ }
+ }
+ } catch (e2) {
+ console.log("Error: "+e2);
+ }
+};
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/lib/android/platform.js
----------------------------------------------------------------------
diff --git a/lib/android/platform.js b/lib/android/platform.js
new file mode 100644
index 0000000..6c71820
--- /dev/null
+++ b/lib/android/platform.js
@@ -0,0 +1,131 @@
+module.exports = {
+ id: "android",
+ initialize:function() {
+ var channel = require("cordova/channel"),
+ cordova = require('cordova'),
+ callback = require('cordova/plugin/android/callback'),
+ polling = require('cordova/plugin/android/polling'),
+ exec = require('cordova/exec');
+
+ channel.onDestroy.subscribe(function() {
+ cordova.shuttingDown = true;
+ });
+
+ // Start listening for XHR callbacks
+ // Figure out which bridge approach will work on this Android
+ // device: polling or XHR-based callbacks
+ setTimeout(function() {
+ if (cordova.UsePolling) {
+ polling();
+ }
+ else {
+ var isPolling = prompt("usePolling", "gap_callbackServer:");
+ cordova.UsePolling = isPolling;
+ if (isPolling == "true") {
+ cordova.UsePolling = true;
+ polling();
+ } else {
+ cordova.UsePolling = false;
+ callback();
+ }
+ }
+ }, 1);
+
+ // Inject a listener for the backbutton on the document.
+ var backButtonChannel = cordova.addDocumentEventHandler('backbutton', {
+ onSubscribe:function() {
+ // If we just attached the first handler, let native know we need to override the back button.
+ if (this.numHandlers === 1) {
+ exec(null, null, "App", "overrideBackbutton", [true]);
+ }
+ },
+ onUnsubscribe:function() {
+ // If we just detached the last handler, let native know we no longer override the back button.
+ if (this.numHandlers === 0) {
+ exec(null, null, "App", "overrideBackbutton", [false]);
+ }
+ }
+ });
+
+ // Add hardware MENU and SEARCH button handlers
+ cordova.addDocumentEventHandler('menubutton');
+ cordova.addDocumentEventHandler('searchbutton');
+
+ // Figure out if we need to shim-in localStorage and WebSQL
+ // support from the native side.
+ var storage = require('cordova/plugin/android/storage');
+
+ // First patch WebSQL if necessary
+ if (typeof window.openDatabase == 'undefined') {
+ // Not defined, create an openDatabase function for all to use!
+ window.openDatabase = storage.openDatabase;
+ } else {
+ // Defined, but some Android devices will throw a SECURITY_ERR -
+ // so we wrap the whole thing in a try-catch and shim in our own
+ // if the device has Android bug 16175.
+ var originalOpenDatabase = window.openDatabase;
+ window.openDatabase = function(name, version, desc, size) {
+ var db = null;
+ try {
+ db = originalOpenDatabase(name, version, desc, size);
+ }
+ catch (ex) {
+ db = null;
+ }
+
+ if (db === null) {
+ return storage.openDatabase(name, version, desc, size);
+ }
+ else {
+ return db;
+ }
+
+ };
+ }
+
+ // Patch localStorage if necessary
+ if (typeof window.localStorage == 'undefined' || window.localStorage === null) {
+ window.localStorage = new storage.CupCakeLocalStorage();
+ }
+
+ // Let native code know we are all done on the JS side.
+ // Native code will then un-hide the WebView.
+ channel.join(function() {
+ prompt("", "gap_init:");
+ }, [channel.onCordovaReady]);
+ },
+ objects: {
+ cordova: {
+ children: {
+ JSCallback:{
+ path:"cordova/plugin/android/callback"
+ },
+ JSCallbackPolling:{
+ path:"cordova/plugin/android/polling"
+ }
+ }
+ },
+ navigator: {
+ children: {
+ app:{
+ path: "cordova/plugin/android/app"
+ }
+ }
+ },
+ device:{
+ path: "cordova/plugin/android/device"
+ },
+ File: { // exists natively on Android WebView, override
+ path: "cordova/plugin/File"
+ },
+ FileReader: { // exists natively on Android WebView, override
+ path: "cordova/plugin/FileReader"
+ },
+ FileError: { //exists natively on Android WebView on Android 4.x
+ path: "cordova/plugin/FileError"
+ },
+ MediaError: { // exists natively on Android WebView on Android 4.x
+ path: "cordova/plugin/MediaError"
+ }
+ }
+};
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/lib/android/plugin/android/app.js
----------------------------------------------------------------------
diff --git a/lib/android/plugin/android/app.js b/lib/android/plugin/android/app.js
new file mode 100644
index 0000000..f858829
--- /dev/null
+++ b/lib/android/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/e50b7ef6/lib/android/plugin/android/callback.js
----------------------------------------------------------------------
diff --git a/lib/android/plugin/android/callback.js b/lib/android/plugin/android/callback.js
new file mode 100644
index 0000000..d5768a7
--- /dev/null
+++ b/lib/android/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/e50b7ef6/lib/android/plugin/android/device.js
----------------------------------------------------------------------
diff --git a/lib/android/plugin/android/device.js b/lib/android/plugin/android/device.js
new file mode 100644
index 0000000..1defbbe
--- /dev/null
+++ b/lib/android/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/e50b7ef6/lib/android/plugin/android/polling.js
----------------------------------------------------------------------
diff --git a/lib/android/plugin/android/polling.js b/lib/android/plugin/android/polling.js
new file mode 100644
index 0000000..36b8e86
--- /dev/null
+++ b/lib/android/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/e50b7ef6/lib/android/plugin/android/storage.js
----------------------------------------------------------------------
diff --git a/lib/android/plugin/android/storage.js b/lib/android/plugin/android/storage.js
new file mode 100644
index 0000000..000612b
--- /dev/null
+++ b/lib/android/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/e50b7ef6/lib/blackberry/exec.js
----------------------------------------------------------------------
diff --git a/lib/blackberry/exec.js b/lib/blackberry/exec.js
new file mode 100644
index 0000000..a5fb81f
--- /dev/null
+++ b/lib/blackberry/exec.js
@@ -0,0 +1,56 @@
+/**
+ * Execute a cordova command. It is up to the native side whether this action
+ * is synchronous or asynchronous. The native side can return:
+ * Synchronous: PluginResult object as a JSON string
+ * Asynchrounous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} success The success callback
+ * @param {Function} fail The fail callback
+ * @param {String} service The name of the service to use
+ * @param {String} action Action to be run in cordova
+ * @param {String[]} [args] Zero or more arguments to pass to the method
+ */
+var blackberry = require('cordova/plugin/blackberry/manager'),
+ cordova = require('cordova');
+
+module.exports = function(success, fail, service, action, args) {
+ try {
+ var v = blackberry.exec(success, fail, service, action, args);
+
+ // If status is OK, then return value back to caller
+ if (v.status == cordova.callbackStatus.OK) {
+
+ // If there is a success callback, then call it now with returned value
+ if (success) {
+ try {
+ success(v.message);
+ }
+ catch (e) {
+ console.log("Error in success callback: "+ service + "." + action + " = "+e);
+ }
+
+ }
+ return v.message;
+ } else if (v.status == cordova.callbackStatus.NO_RESULT) {
+
+ } else {
+ // If error, then display error
+ console.log("Error: " + service + "." + action + " Status="+v.status+" Message="+v.message);
+
+ // If there is a fail callback, then call it now with returned value
+ if (fail) {
+ try {
+ fail(v.message);
+ }
+ catch (e) {
+ console.log("Error in error callback: " + service + "." + action + " = "+e);
+ }
+ }
+ return null;
+ }
+ } catch (e) {
+ alert("Error: "+e);
+ }
+};
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/lib/blackberry/platform.js
----------------------------------------------------------------------
diff --git a/lib/blackberry/platform.js b/lib/blackberry/platform.js
new file mode 100644
index 0000000..d98f0e6
--- /dev/null
+++ b/lib/blackberry/platform.js
@@ -0,0 +1,179 @@
+module.exports = {
+ id: "blackberry",
+ initialize:function() {
+ var cordova = require('cordova'),
+ exec = require('cordova/exec'),
+ channel = require('cordova/channel'),
+ blackberryManager = require('cordova/plugin/blackberry/manager'),
+ app = require('cordova/plugin/blackberry/app');
+
+ // BB OS 5 does not define window.console.
+ if (typeof window.console === 'undefined') {
+ window.console = {};
+ }
+
+ // Override console.log with native logging ability.
+ // BB OS 7 devices define console.log for use with web inspector
+ // debugging. If console.log is already defined, invoke it in addition
+ // to native logging.
+ var origLog = window.console.log;
+ window.console.log = function(msg) {
+ if (typeof origLog === 'function') {
+ origLog.call(window.console, msg);
+ }
+ org.apache.cordova.Logger.log(''+msg);
+ };
+
+ // Mapping of button events to BlackBerry key identifier.
+ var buttonMapping = {
+ 'backbutton' : blackberry.system.event.KEY_BACK,
+ 'conveniencebutton1' : blackberry.system.event.KEY_CONVENIENCE_1,
+ 'conveniencebutton2' : blackberry.system.event.KEY_CONVENIENCE_2,
+ 'endcallbutton' : blackberry.system.event.KEY_ENDCALL,
+ 'menubutton' : blackberry.system.event.KEY_MENU,
+ 'startcallbutton' : blackberry.system.event.KEY_STARTCALL,
+ 'volumedownbutton' : blackberry.system.event.KEY_VOLUMEDOWN,
+ 'volumeupbutton' : blackberry.system.event.KEY_VOLUMEUP
+ };
+
+ // Generates a function which fires the specified event.
+ var fireEvent = function(event) {
+ return function() {
+ cordova.fireDocumentEvent(event, null);
+ };
+ };
+
+ var eventHandler = function(event) {
+ return { onSubscribe : function() {
+ // If we just attached the first handler, let native know we
+ // need to override the back button.
+ if (this.numHandlers === 1) {
+ blackberry.system.event.onHardwareKey(
+ buttonMapping[event], fireEvent(event));
+ }
+ },
+ onUnsubscribe : function() {
+ // If we just detached the last handler, let native know we
+ // no longer override the back button.
+ if (this.numHandlers === 0) {
+ blackberry.system.event.onHardwareKey(
+ buttonMapping[event], null);
+ }
+ }};
+ };
+
+ // Inject listeners for buttons on the document.
+ for (var button in buttonMapping) {
+ if (buttonMapping.hasOwnProperty(button)) {
+ cordova.addDocumentEventHandler(button, eventHandler(button));
+ }
+ }
+
+ // Fires off necessary code to pause/resume app
+ var resume = function() {
+ cordova.fireDocumentEvent('resume');
+ blackberryManager.resume();
+ };
+ var pause = function() {
+ cordova.fireDocumentEvent('pause');
+ blackberryManager.pause();
+ };
+
+ /************************************************
+ * Patch up the generic pause/resume listeners. *
+ ************************************************/
+
+ // Unsubscribe handler - turns off native backlight change
+ // listener
+ var onUnsubscribe = function() {
+ if (channel.onResume.numHandlers === 0 && channel.onPause.numHandlers === 0) {
+ exec(null, null, 'App', 'ignoreBacklight', []);
+ }
+ };
+
+ // Native backlight detection win/fail callbacks
+ var backlightWin = function(isOn) {
+ if (isOn === true) {
+ resume();
+ } else {
+ pause();
+ }
+ };
+ var backlightFail = function(e) {
+ console.log("Error detecting backlight on/off.");
+ };
+
+ // Override stock resume and pause listeners so we can trigger
+ // some native methods during attach/remove
+ channel.onResume = cordova.addDocumentEventHandler('resume', {
+ onSubscribe:function() {
+ // If we just attached the first handler and there are
+ // no pause handlers, start the backlight system
+ // listener on the native side.
+ if (channel.onResume.numHandlers === 1 && channel.onPause.numHandlers === 0) {
+ exec(backlightWin, backlightFail, "App", "detectBacklight", []);
+ }
+ },
+ onUnsubscribe:onUnsubscribe
+ });
+ channel.onPause = cordova.addDocumentEventHandler('pause', {
+ onSubscribe:function() {
+ // If we just attached the first handler and there are
+ // no resume handlers, start the backlight system
+ // listener on the native side.
+ if (channel.onResume.numHandlers === 0 && channel.onPause.numHandlers === 1) {
+ exec(backlightWin, backlightFail, "App", "detectBacklight", []);
+ }
+ },
+ onUnsubscribe:onUnsubscribe
+ });
+
+ // Fire resume event when application brought to foreground.
+ blackberry.app.event.onForeground(resume);
+
+ // Fire pause event when application sent to background.
+ blackberry.app.event.onBackground(pause);
+
+ // Trap BlackBerry WebWorks exit. Allow plugins to clean up before exiting.
+ blackberry.app.event.onExit(app.exitApp);
+ },
+ objects: {
+ navigator: {
+ children: {
+ app: {
+ path: "cordova/plugin/blackberry/app"
+ }
+ }
+ },
+ device: {
+ path: "cordova/plugin/blackberry/device"
+ },
+ File: { // exists natively on BlackBerry OS 7, override
+ path: "cordova/plugin/File"
+ }
+ },
+ merges: {
+ navigator: {
+ children: {
+ contacts: {
+ path: 'cordova/plugin/blackberry/contacts'
+ },
+ device: {
+ path: 'cordova/plugin/blackberry/device'
+ },
+ notification: {
+ path: 'cordova/plugin/blackberry/notification'
+ }
+ }
+ },
+ Contact: {
+ path: 'cordova/plugin/blackberry/Contact'
+ },
+ DirectoryEntry: {
+ path: 'cordova/plugin/blackberry/DirectoryEntry'
+ },
+ Entry: {
+ path: 'cordova/plugin/blackberry/Entry'
+ }
+ }
+};
http://git-wip-us.apache.org/repos/asf/incubator-cordova-js/blob/e50b7ef6/lib/blackberry/plugin/blackberry/Contact.js
----------------------------------------------------------------------
diff --git a/lib/blackberry/plugin/blackberry/Contact.js b/lib/blackberry/plugin/blackberry/Contact.js
new file mode 100644
index 0000000..bbc0937
--- /dev/null
+++ b/lib/blackberry/plugin/blackberry/Contact.js
@@ -0,0 +1,386 @@
+var ContactError = require('cordova/plugin/ContactError'),
+ ContactUtils = require('cordova/plugin/blackberry/ContactUtils'),
+ exec = require('cordova/exec');
+
+// ------------------
+// Utility functions
+// ------------------
+
+/**
+ * Retrieves a BlackBerry contact from the device by unique id.
+ *
+ * @param uid
+ * Unique id of the contact on the device
+ * @return {blackberry.pim.Contact} BlackBerry contact or null if contact with
+ * specified id is not found
+ */
+var findByUniqueId = function(uid) {
+ if (!uid) {
+ return null;
+ }
+ var bbContacts = blackberry.pim.Contact
+ .find(new blackberry.find.FilterExpression("uid", "==", uid));
+ return bbContacts[0] || null;
+};
+
+/**
+ * Creates a BlackBerry contact object from the W3C Contact object and persists
+ * it to device storage.
+ *
+ * @param {Contact}
+ * contact The contact to save
+ * @return a new contact object with all properties set
+ */
+var saveToDevice = function(contact) {
+
+ if (!contact) {
+ return;
+ }
+
+ var bbContact = null;
+ var update = false;
+
+ // if the underlying BlackBerry contact already exists, retrieve it for
+ // update
+ if (contact.id) {
+ // we must attempt to retrieve the BlackBerry contact from the device
+ // because this may be an update operation
+ bbContact = findByUniqueId(contact.id);
+ }
+
+ // contact not found on device, create a new one
+ if (!bbContact) {
+ bbContact = new blackberry.pim.Contact();
+ }
+ // update the existing contact
+ else {
+ update = true;
+ }
+
+ // NOTE: The user may be working with a partial Contact object, because only
+ // user-specified Contact fields are returned from a find operation (blame
+ // the W3C spec). If this is an update to an existing Contact, we don't
+ // want to clear an attribute from the contact database simply because the
+ // Contact object that the user passed in contains a null value for that
+ // attribute. So we only copy the non-null Contact attributes to the
+ // BlackBerry contact object before saving.
+ //
+ // This means that a user must explicitly set a Contact attribute to a
+ // non-null value in order to update it in the contact database.
+ //
+ // name
+ if (contact.name !== null) {
+ if (contact.name.givenName) {
+ bbContact.firstName = contact.name.givenName;
+ }
+ if (contact.name.familyName) {
+ bbContact.lastName = contact.name.familyName;
+ }
+ if (contact.name.honorificPrefix) {
+ bbContact.title = contact.name.honorificPrefix;
+ }
+ }
+
+ // display name
+ if (contact.displayName !== null) {
+ bbContact.user1 = contact.displayName;
+ }
+
+ // note
+ if (contact.note !== null) {
+ bbContact.note = contact.note;
+ }
+
+ // birthday
+ //
+ // user may pass in Date object or a string representation of a date
+ // if it is a string, we don't know the date format, so try to create a
+ // new Date with what we're given
+ //
+ // NOTE: BlackBerry's Date.parse() does not work well, so use new Date()
+ //
+ if (contact.birthday !== null) {
+ if (contact.birthday instanceof Date) {
+ bbContact.birthday = contact.birthday;
+ } else {
+ var bday = contact.birthday.toString();
+ bbContact.birthday = (bday.length > 0) ? new Date(bday) : "";
+ }
+ }
+
+ // BlackBerry supports three email addresses
+ if (contact.emails && contact.emails instanceof Array) {
+
+ // if this is an update, re-initialize email addresses
+ if (update) {
+ bbContact.email1 = "";
+ bbContact.email2 = "";
+ bbContact.email3 = "";
+ }
+
+ // copy the first three email addresses found
+ var email = null;
+ for ( var i = 0; i < contact.emails.length; i += 1) {
+ email = contact.emails[i];
+ if (!email || !email.value) {
+ continue;
+ }
+ if (bbContact.email1 === "") {
+ bbContact.email1 = email.value;
+ } else if (bbContact.email2 === "") {
+ bbContact.email2 = email.value;
+ } else if (bbContact.email3 === "") {
+ bbContact.email3 = email.value;
+ }
+ }
+ }
+
+ // BlackBerry supports a finite number of phone numbers
+ // copy into appropriate fields based on type
+ if (contact.phoneNumbers && contact.phoneNumbers instanceof Array) {
+
+ // if this is an update, re-initialize phone numbers
+ if (update) {
+ bbContact.homePhone = "";
+ bbContact.homePhone2 = "";
+ bbContact.workPhone = "";
+ bbContact.workPhone2 = "";
+ bbContact.mobilePhone = "";
+ bbContact.faxPhone = "";
+ bbContact.pagerPhone = "";
+ bbContact.otherPhone = "";
+ }
+
+ var type = null;
+ var number = null;
+ for ( var i = 0; i < contact.phoneNumbers.length; i += 1) {
+ if (!contact.phoneNumbers[i] || !contact.phoneNumbers[i].value) {
+ continue;
+ }
+ type = contact.phoneNumbers[i].type;
+ number = contact.phoneNumbers[i].value;
+ if (type === 'home') {
+ if (bbContact.homePhone === "") {
+ bbContact.homePhone = number;
+ } else if (bbContact.homePhone2 === "") {
+ bbContact.homePhone2 = number;
+ }
+ } else if (type === 'work') {
+ if (bbContact.workPhone === "") {
+ bbContact.workPhone = number;
+ } else if (bbContact.workPhone2 === "") {
+ bbContact.workPhone2 = number;
+ }
+ } else if (type === 'mobile' && bbContact.mobilePhone === "") {
+ bbContact.mobilePhone = number;
+ } else if (type === 'fax' && bbContact.faxPhone === "") {
+ bbContact.faxPhone = number;
+ } else if (type === 'pager' && bbContact.pagerPhone === "") {
+ bbContact.pagerPhone = number;
+ } else if (bbContact.otherPhone === "") {
+ bbContact.otherPhone = number;
+ }
+ }
+ }
+
+ // BlackBerry supports two addresses: home and work
+ // copy the first two addresses found from Contact
+ if (contact.addresses && contact.addresses instanceof Array) {
+
+ // if this is an update, re-initialize addresses
+ if (update) {
+ bbContact.homeAddress = null;
+ bbContact.workAddress = null;
+ }
+
+ var address = null;
+ var bbHomeAddress = null;
+ var bbWorkAddress = null;
+ for ( var i = 0; i < contact.addresses.length; i += 1) {
+ address = contact.addresses[i];
+ if (!address || address instanceof ContactAddress === false) {
+ continue;
+ }
+
+ if (bbHomeAddress === null
+ && (!address.type || address.type === "home")) {
+ bbHomeAddress = createBlackBerryAddress(address);
+ bbContact.homeAddress = bbHomeAddress;
+ } else if (bbWorkAddress === null
+ && (!address.type || address.type === "work")) {
+ bbWorkAddress = createBlackBerryAddress(address);
+ bbContact.workAddress = bbWorkAddress;
+ }
+ }
+ }
+
+ // copy first url found to BlackBerry 'webpage' field
+ if (contact.urls && contact.urls instanceof Array) {
+
+ // if this is an update, re-initialize web page
+ if (update) {
+ bbContact.webpage = "";
+ }
+
+ var url = null;
+ for ( var i = 0; i < contact.urls.length; i += 1) {
+ url = contact.urls[i];
+ if (!url || !url.value) {
+ continue;
+ }
+ if (bbContact.webpage === "") {
+ bbContact.webpage = url.value;
+ break;
+ }
+ }
+ }
+
+ // copy fields from first organization to the
+ // BlackBerry 'company' and 'jobTitle' fields
+ if (contact.organizations && contact.organizations instanceof Array) {
+
+ // if this is an update, re-initialize org attributes
+ if (update) {
+ bbContact.company = "";
+ }
+
+ var org = null;
+ for ( var i = 0; i < contact.organizations.length; i += 1) {
+ org = contact.organizations[i];
+ if (!org) {
+ continue;
+ }
+ if (bbContact.company === "") {
+ bbContact.company = org.name || "";
+ bbContact.jobTitle = org.title || "";
+ break;
+ }
+ }
+ }
+
+ // categories
+ if (contact.categories && contact.categories instanceof Array) {
+ bbContact.categories = [];
+ var category = null;
+ for ( var i = 0; i < contact.categories.length; i += 1) {
+ category = contact.categories[i];
+ if (typeof category == "string") {
+ bbContact.categories.push(category);
+ }
+ }
+ }
+
+ // save to device
+ bbContact.save();
+
+ // invoke native side to save photo
+ // fail gracefully if photo URL is no good, but log the error
+ if (contact.photos && contact.photos instanceof Array) {
+ var photo = null;
+ for ( var i = 0; i < contact.photos.length; i += 1) {
+ photo = contact.photos[i];
+ if (!photo || !photo.value) {
+ continue;
+ }
+ exec(
+ // success
+ function() {
+ },
+ // fail
+ function(e) {
+ console.log('Contact.setPicture failed:' + e);
+ }, "Contact", "setPicture", [ bbContact.uid, photo.type,
+ photo.value ]);
+ break;
+ }
+ }
+
+ // Use the fully populated BlackBerry contact object to create a
+ // corresponding W3C contact object.
+ return ContactUtils.createContact(bbContact, [ "*" ]);
+};
+
+/**
+ * Creates a BlackBerry Address object from a W3C ContactAddress.
+ *
+ * @return {blackberry.pim.Address} a BlackBerry address object
+ */
+var createBlackBerryAddress = function(address) {
+ var bbAddress = new blackberry.pim.Address();
+
+ if (!address) {
+ return bbAddress;
+ }
+
+ bbAddress.address1 = address.streetAddress || "";
+ bbAddress.city = address.locality || "";
+ bbAddress.stateProvince = address.region || "";
+ bbAddress.zipPostal = address.postalCode || "";
+ bbAddress.country = address.country || "";
+
+ return bbAddress;
+};
+
+module.exports = {
+ /**
+ * Persists contact to device storage.
+ */
+ save : function(success, fail) {
+ try {
+ // save the contact and store it's unique id
+ var fullContact = saveToDevice(this);
+ this.id = fullContact.id;
+
+ // This contact object may only have a subset of properties
+ // if the save was an update of an existing contact. This is
+ // because the existing contact was likely retrieved using a
+ // subset of properties, so only those properties were set in the
+ // object. For this reason, invoke success with the contact object
+ // returned by saveToDevice since it is fully populated.
+ if (typeof success === 'function') {
+ success(fullContact);
+ }
+ } catch (e) {
+ console.log('Error saving contact: ' + e);
+ if (typeof fail === 'function') {
+ fail(new ContactError(ContactError.UNKNOWN_ERROR));
+ }
+ }
+ },
+
+ /**
+ * Removes contact from device storage.
+ *
+ * @param success
+ * success callback
+ * @param fail
+ * error callback
+ */
+ remove : function(success, fail) {
+ try {
+ // retrieve contact from device by id
+ var bbContact = null;
+ if (this.id) {
+ bbContact = findByUniqueId(this.id);
+ }
+
+ // if contact was found, remove it
+ if (bbContact) {
+ console.log('removing contact: ' + bbContact.uid);
+ bbContact.remove();
+ if (typeof success === 'function') {
+ success(this);
+ }
+ }
+ // attempting to remove a contact that hasn't been saved
+ else if (typeof fail === 'function') {
+ fail(new ContactError(ContactError.UNKNOWN_ERROR));
+ }
+ } catch (e) {
+ console.log('Error removing contact ' + this.id + ": " + e);
+ if (typeof fail === 'function') {
+ fail(new ContactError(ContactError.UNKNOWN_ERROR));
+ }
+ }
+ }
+};
\ No newline at end of file