From 003e3d4d85e5bd982e8798676810db40bf72c7c5 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 25 Jan 2012 14:10:22 -0800 Subject: [PATCH 01/20] Adding unsupported action plugin result return if invalid action string is specified to accel and compass listener plugins --- .../src/org/apache/cordova/AccelListener.java | 7 +++++-- .../org/apache/cordova/CompassListener.java | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java index 6eb917f7..f751e4e2 100755 --- a/framework/src/org/apache/cordova/AccelListener.java +++ b/framework/src/org/apache/cordova/AccelListener.java @@ -148,7 +148,10 @@ public class AccelListener extends Plugin implements SensorEventListener { else if (action.equals("getTimeout")) { float f = this.getTimeout(); return new PluginResult(status, f); - } + } else { + // Unsupported action + return new PluginResult(PluginResult.Status.INVALID_ACTION); + } return new PluginResult(status, result); } catch (JSONException e) { return new PluginResult(PluginResult.Status.JSON_EXCEPTION); @@ -167,7 +170,7 @@ public class AccelListener extends Plugin implements SensorEventListener { } else if (action.equals("getAcceleration")) { // Can only return value if RUNNING - if (this.status == RUNNING) { + if (this.status == AccelListener.RUNNING) { return true; } } diff --git a/framework/src/org/apache/cordova/CompassListener.java b/framework/src/org/apache/cordova/CompassListener.java index 49f0bb8e..97a8b6f4 100755 --- a/framework/src/org/apache/cordova/CompassListener.java +++ b/framework/src/org/apache/cordova/CompassListener.java @@ -59,6 +59,7 @@ public class CompassListener extends Plugin implements SensorEventListener { * Constructor. */ public CompassListener() { + this.heading = 0.0; this.timeStamp = 0; this.setStatus(CompassListener.STOPPED); } @@ -99,10 +100,10 @@ public class CompassListener extends Plugin implements SensorEventListener { } else if (action.equals("getHeading")) { // If not running, then this is an async call, so don't worry about waiting - if (this.status != RUNNING) { + if (this.status != CompassListener.RUNNING) { int r = this.start(); - if (r == ERROR_FAILED_TO_START) { - return new PluginResult(PluginResult.Status.IO_EXCEPTION, ERROR_FAILED_TO_START); + if (r == CompassListener.ERROR_FAILED_TO_START) { + return new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START); } // Wait until running long timeout = 2000; @@ -115,10 +116,9 @@ public class CompassListener extends Plugin implements SensorEventListener { } } if (timeout == 0) { - return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START); + return new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START); } } - //float f = this.getHeading(); return new PluginResult(status, getCompassHeading()); } else if (action.equals("setTimeout")) { @@ -127,6 +127,9 @@ public class CompassListener extends Plugin implements SensorEventListener { else if (action.equals("getTimeout")) { long l = this.getTimeout(); return new PluginResult(status, l); + } else { + // Unsupported action + return new PluginResult(PluginResult.Status.INVALID_ACTION); } return new PluginResult(status, result); } catch (JSONException e) { @@ -147,7 +150,7 @@ public class CompassListener extends Plugin implements SensorEventListener { } else if (action.equals("getHeading")) { // Can only return value if RUNNING - if (this.status == RUNNING) { + if (this.status == CompassListener.RUNNING) { return true; } } @@ -180,11 +183,11 @@ public class CompassListener extends Plugin implements SensorEventListener { return this.status; } - // Get accelerometer from sensor manager + // Get compass sensor from sensor manager List list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION); // If found, then register as listener - if (list.size() > 0) { + if (list != null && list.size() > 0) { this.mSensor = list.get(0); this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL); this.lastAccessTime = System.currentTimeMillis(); From c7d4d8347642bda0c8c73e0951558a219666bbcc Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 25 Jan 2012 14:12:38 -0800 Subject: [PATCH 02/20] woops java type checks! love it! --- framework/src/org/apache/cordova/CompassListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/CompassListener.java b/framework/src/org/apache/cordova/CompassListener.java index 97a8b6f4..01d431a5 100755 --- a/framework/src/org/apache/cordova/CompassListener.java +++ b/framework/src/org/apache/cordova/CompassListener.java @@ -59,7 +59,7 @@ public class CompassListener extends Plugin implements SensorEventListener { * Constructor. */ public CompassListener() { - this.heading = 0.0; + this.heading = 0; this.timeStamp = 0; this.setStatus(CompassListener.STOPPED); } From 52213541a375d5f43a4d554900f821acfe02687b Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 25 Jan 2012 14:28:47 -0800 Subject: [PATCH 03/20] changing JS invoked from native to work with modular js --- .../src/org/apache/cordova/CordovaWebViewClient.java | 2 +- framework/src/org/apache/cordova/DroidGap.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 4946cc42..319a944b 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -218,7 +218,7 @@ public class CordovaWebViewClient extends WebViewClient { // not loaded yet then just set a flag so that the onNativeReady can be fired // from the JS side when the JS gets to that code. if (!url.equals("about:blank")) { - ctx.appView.loadUrl("javascript:try{ Cordova.onNativeReady.fire();}catch(e){_nativeReady = true;}"); + ctx.appView.loadUrl("javascript:try{ require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}"); } // Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 61aba0db..d326e956 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -847,7 +847,7 @@ public class DroidGap extends CordovaInterface { } // Send pause event to JavaScript - this.appView.loadUrl("javascript:try{Cordova.fireDocumentEvent('pause');}catch(e){};"); + this.appView.loadUrl("javascript:try{require('cordova/channel').onPause.fire();}catch(e){console.log('exception firing pause event from native');};"); // Forward to plugins this.pluginManager.onPause(this.keepRunning); @@ -888,7 +888,7 @@ public class DroidGap extends CordovaInterface { } // Send resume event to JavaScript - this.appView.loadUrl("javascript:try{Cordova.fireDocumentEvent('resume');}catch(e){};"); + this.appView.loadUrl("javascript:try{require('cordova/channel').onResume.fire();}catch(e){console.log('exception firing resume event from native');};"); // Forward to plugins this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning); @@ -918,7 +918,7 @@ public class DroidGap extends CordovaInterface { // Send destroy event to JavaScript - this.appView.loadUrl("javascript:try{Cordova.onDestroy.fire();}catch(e){};"); + this.appView.loadUrl("javascript:try{require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};"); // Load blank page so that JavaScript onunload is called this.appView.loadUrl("about:blank"); @@ -1080,7 +1080,7 @@ public class DroidGap extends CordovaInterface { // If back key is bound, then send event to JavaScript if (this.bound) { - this.appView.loadUrl("javascript:Cordova.fireDocumentEvent('backbutton');"); + this.appView.loadUrl("javascript:require('cordova').fireDocumentEvent('backbutton');"); return true; } @@ -1102,13 +1102,13 @@ public class DroidGap extends CordovaInterface { // If menu key else if (keyCode == KeyEvent.KEYCODE_MENU) { - this.appView.loadUrl("javascript:Cordova.fireDocumentEvent('menubutton');"); + this.appView.loadUrl("javascript:require('cordova').fireDocumentEvent('menubutton');"); return super.onKeyDown(keyCode, event); } // If search key else if (keyCode == KeyEvent.KEYCODE_SEARCH) { - this.appView.loadUrl("javascript:Cordova.fireDocumentEvent('searchbutton');"); + this.appView.loadUrl("javascript:require('cordova').fireDocumentEvent('searchbutton');"); return true; } From a84d2eab02890015d9301649d6ede3a6a4c176ca Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 25 Jan 2012 15:31:09 -0800 Subject: [PATCH 04/20] Changed JavaScript invoked from PluginResult native code to use new modular JS approach --- framework/src/org/apache/cordova/api/PluginResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/org/apache/cordova/api/PluginResult.java b/framework/src/org/apache/cordova/api/PluginResult.java index 89ee1b5f..3a12491f 100755 --- a/framework/src/org/apache/cordova/api/PluginResult.java +++ b/framework/src/org/apache/cordova/api/PluginResult.java @@ -84,11 +84,11 @@ public class PluginResult { } public String toSuccessCallbackString(String callbackId) { - return "Cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");"; + return "require('cordova').callbackSuccess('"+callbackId+"',"+this.getJSONString()+");"; } public String toErrorCallbackString(String callbackId) { - return "Cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");"; + return "require('cordova').callbackError('"+callbackId+"', " + this.getJSONString()+ ");"; } public static String[] StatusMessages = new String[] { From 40637c14867d236029d48ac4236781da03eb0002 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 15 Feb 2012 16:47:32 -0800 Subject: [PATCH 05/20] Unifying File API error code/response style --- .../src/org/apache/cordova/FileUtils.java | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 47bafee3..b397f046 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -199,32 +199,23 @@ public class FileUtils extends Plugin { } return new PluginResult(status, result); } catch (FileNotFoundException e) { - JSONObject error = new JSONObject().put("code", FileUtils.NOT_FOUND_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR); } catch (FileExistsException e) { - JSONObject error = new JSONObject().put("code", FileUtils.PATH_EXISTS_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.PATH_EXISTS_ERR); } catch (NoModificationAllowedException e) { - JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR); } catch (JSONException e) { - JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR); } catch (InvalidModificationException e) { - JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR); } catch (MalformedURLException e) { - JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR); } catch (IOException e) { - JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR); } catch (EncodingException e) { - JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR); } catch (TypeMismatchException e) { - JSONObject error = new JSONObject().put("code", FileUtils.TYPE_MISMATCH_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.TYPE_MISMATCH_ERR); } } catch (JSONException e) { e.printStackTrace(); From 357c0ee1caa290c3234f1719dc1035aba0031e54 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 15 Feb 2012 16:57:40 -0800 Subject: [PATCH 06/20] removing unnecessary try/catch --- .../src/org/apache/cordova/FileUtils.java | 241 +++++++++--------- 1 file changed, 118 insertions(+), 123 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index b397f046..2bf9620d 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -95,131 +95,126 @@ public class FileUtils extends Plugin { //System.out.println("FileUtils.execute("+action+")"); try { - try { - if (action.equals("testSaveLocationExists")) { - boolean b = DirectoryManager.testSaveLocationExists(); - return new PluginResult(status, b); - } - else if (action.equals("getFreeDiskSpace")) { - long l = DirectoryManager.getFreeDiskSpace(false); - return new PluginResult(status, l); - } - else if (action.equals("testFileExists")) { - boolean b = DirectoryManager.testFileExists(args.getString(0)); - return new PluginResult(status, b); - } - else if (action.equals("testDirectoryExists")) { - boolean b = DirectoryManager.testFileExists(args.getString(0)); - return new PluginResult(status, b); - } - else if (action.equals("readAsText")) { - String s = this.readAsText(args.getString(0), args.getString(1)); - return new PluginResult(status, s); - } - else if (action.equals("readAsDataURL")) { - String s = this.readAsDataURL(args.getString(0)); - return new PluginResult(status, s); - } - else if (action.equals("write")) { - long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2)); - return new PluginResult(status, fileSize); - } - else if (action.equals("truncate")) { - long fileSize = this.truncateFile(args.getString(0), args.getLong(1)); - return new PluginResult(status, fileSize); - } - else if (action.equals("requestFileSystem")) { - long size = args.optLong(1); - if (size != 0) { - if (size > (DirectoryManager.getFreeDiskSpace(true)*1024)) { - JSONObject error = new JSONObject().put("code", FileUtils.QUOTA_EXCEEDED_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); - } - } - JSONObject obj = requestFileSystem(args.getInt(0)); - return new PluginResult(status, obj); - } - else if (action.equals("resolveLocalFileSystemURI")) { - JSONObject obj = resolveLocalFileSystemURI(args.getString(0)); - return new PluginResult(status, obj); - } - else if (action.equals("getMetadata")) { - JSONObject obj = getMetadata(args.getString(0)); - return new PluginResult(status, obj); - } - else if (action.equals("getFileMetadata")) { - JSONObject obj = getFileMetadata(args.getString(0)); - return new PluginResult(status, obj); - } - else if (action.equals("getParent")) { - JSONObject obj = getParent(args.getString(0)); - return new PluginResult(status, obj); - } - else if (action.equals("getDirectory")) { - JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true); - return new PluginResult(status, obj); - } - else if (action.equals("getFile")) { - JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false); - return new PluginResult(status, obj); - } - else if (action.equals("remove")) { - boolean success; - - success = remove(args.getString(0)); - - if (success) { - notifyDelete(args.getString(0)); - return new PluginResult(status); - } else { - JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); - } - } - else if (action.equals("removeRecursively")) { - boolean success = removeRecursively(args.getString(0)); - if (success) { - return new PluginResult(status); - } else { - JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR); - return new PluginResult(PluginResult.Status.ERROR, error); - } - } - else if (action.equals("moveTo")) { - JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), true); - return new PluginResult(status, entry); - } - else if (action.equals("copyTo")) { - JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), false); - return new PluginResult(status, entry); - } - else if (action.equals("readEntries")) { - JSONArray entries = readEntries(args.getString(0)); - return new PluginResult(status, entries); - } - return new PluginResult(status, result); - } catch (FileNotFoundException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR); - } catch (FileExistsException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.PATH_EXISTS_ERR); - } catch (NoModificationAllowedException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } catch (JSONException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR); - } catch (InvalidModificationException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR); - } catch (MalformedURLException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR); - } catch (IOException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR); - } catch (EncodingException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR); - } catch (TypeMismatchException e) { - return new PluginResult(PluginResult.Status.ERROR, FileUtils.TYPE_MISMATCH_ERR); + if (action.equals("testSaveLocationExists")) { + boolean b = DirectoryManager.testSaveLocationExists(); + return new PluginResult(status, b); } + else if (action.equals("getFreeDiskSpace")) { + long l = DirectoryManager.getFreeDiskSpace(false); + return new PluginResult(status, l); + } + else if (action.equals("testFileExists")) { + boolean b = DirectoryManager.testFileExists(args.getString(0)); + return new PluginResult(status, b); + } + else if (action.equals("testDirectoryExists")) { + boolean b = DirectoryManager.testFileExists(args.getString(0)); + return new PluginResult(status, b); + } + else if (action.equals("readAsText")) { + String s = this.readAsText(args.getString(0), args.getString(1)); + return new PluginResult(status, s); + } + else if (action.equals("readAsDataURL")) { + String s = this.readAsDataURL(args.getString(0)); + return new PluginResult(status, s); + } + else if (action.equals("write")) { + long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2)); + return new PluginResult(status, fileSize); + } + else if (action.equals("truncate")) { + long fileSize = this.truncateFile(args.getString(0), args.getLong(1)); + return new PluginResult(status, fileSize); + } + else if (action.equals("requestFileSystem")) { + long size = args.optLong(1); + if (size != 0) { + if (size > (DirectoryManager.getFreeDiskSpace(true)*1024)) { + JSONObject error = new JSONObject().put("code", FileUtils.QUOTA_EXCEEDED_ERR); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } + JSONObject obj = requestFileSystem(args.getInt(0)); + return new PluginResult(status, obj); + } + else if (action.equals("resolveLocalFileSystemURI")) { + JSONObject obj = resolveLocalFileSystemURI(args.getString(0)); + return new PluginResult(status, obj); + } + else if (action.equals("getMetadata")) { + JSONObject obj = getMetadata(args.getString(0)); + return new PluginResult(status, obj); + } + else if (action.equals("getFileMetadata")) { + JSONObject obj = getFileMetadata(args.getString(0)); + return new PluginResult(status, obj); + } + else if (action.equals("getParent")) { + JSONObject obj = getParent(args.getString(0)); + return new PluginResult(status, obj); + } + else if (action.equals("getDirectory")) { + JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true); + return new PluginResult(status, obj); + } + else if (action.equals("getFile")) { + JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false); + return new PluginResult(status, obj); + } + else if (action.equals("remove")) { + boolean success; + + success = remove(args.getString(0)); + + if (success) { + notifyDelete(args.getString(0)); + return new PluginResult(status); + } else { + JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } + else if (action.equals("removeRecursively")) { + boolean success = removeRecursively(args.getString(0)); + if (success) { + return new PluginResult(status); + } else { + JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR); + return new PluginResult(PluginResult.Status.ERROR, error); + } + } + else if (action.equals("moveTo")) { + JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), true); + return new PluginResult(status, entry); + } + else if (action.equals("copyTo")) { + JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), false); + return new PluginResult(status, entry); + } + else if (action.equals("readEntries")) { + JSONArray entries = readEntries(args.getString(0)); + return new PluginResult(status, entries); + } + return new PluginResult(status, result); + } catch (FileNotFoundException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR); + } catch (FileExistsException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.PATH_EXISTS_ERR); + } catch (NoModificationAllowedException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR); } catch (JSONException e) { - e.printStackTrace(); - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR); + } catch (InvalidModificationException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR); + } catch (MalformedURLException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR); + } catch (IOException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR); + } catch (EncodingException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR); + } catch (TypeMismatchException e) { + return new PluginResult(PluginResult.Status.ERROR, FileUtils.TYPE_MISMATCH_ERR); } } From fd69095d3d27e4d45bf9438f3dab9b9b842a652d Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 15 Feb 2012 17:30:32 -0800 Subject: [PATCH 07/20] Return string for root location in requestFileSystem --- framework/src/org/apache/cordova/FileUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 2bf9620d..5c217c23 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -782,14 +782,14 @@ public class FileUtils extends Plugin { File fp; fs.put("name", "temporary"); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() + - "/Android/data/" + ctx.getPackageName() + "/cache/")); + fs.put("root", Environment.getExternalStorageDirectory().getAbsolutePath() + + "/Android/data/" + ctx.getPackageName() + "/cache/"); // Create the cache dir if it doesn't exist. fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache/"); } else { - fs.put("root", getEntry("/data/data/" + ctx.getPackageName() + "/cache/")); + fs.put("root", "/data/data/" + ctx.getPackageName() + "/cache/"); // Create the cache dir if it doesn't exist. fp = new File("/data/data/" + ctx.getPackageName() + "/cache/"); } @@ -798,9 +798,9 @@ public class FileUtils extends Plugin { else if (type == PERSISTENT) { fs.put("name", "persistent"); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - fs.put("root", getEntry(Environment.getExternalStorageDirectory())); + fs.put("root", Environment.getExternalStorageDirectory()); } else { - fs.put("root", getEntry("/data/data/" + ctx.getPackageName())); + fs.put("root", "/data/data/" + ctx.getPackageName()); } } else { From 7ee135e51666cef0ccf465763599b7462b5e43bd Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 15:18:13 -0800 Subject: [PATCH 08/20] Prefix URIs for file API with file:/// and persistent storage should never point to SD card --- framework/src/org/apache/cordova/FileUtils.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 5c217c23..c3478065 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -782,14 +782,14 @@ public class FileUtils extends Plugin { File fp; fs.put("name", "temporary"); if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - fs.put("root", Environment.getExternalStorageDirectory().getAbsolutePath() + + fs.put("root", "file://" + Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache/"); // Create the cache dir if it doesn't exist. fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache/"); } else { - fs.put("root", "/data/data/" + ctx.getPackageName() + "/cache/"); + fs.put("root", "file:///data/data/" + ctx.getPackageName() + "/cache/"); // Create the cache dir if it doesn't exist. fp = new File("/data/data/" + ctx.getPackageName() + "/cache/"); } @@ -797,11 +797,7 @@ public class FileUtils extends Plugin { } else if (type == PERSISTENT) { fs.put("name", "persistent"); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - fs.put("root", Environment.getExternalStorageDirectory()); - } else { - fs.put("root", "/data/data/" + ctx.getPackageName()); - } + fs.put("root", "file:///data/data/" + ctx.getPackageName()); } else { throw new IOException("No filesystem of type requested"); From 6c48c6a3a1fef6bb29c4d75da65ef97ba2971c01 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 15:33:19 -0800 Subject: [PATCH 09/20] Need to add/trim file:// where applicable --- framework/src/org/apache/cordova/FileUtils.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index c3478065..ec81fc71 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -687,6 +687,9 @@ public class FileUtils extends Plugin { if (fileName.startsWith("/")) { fp = new File(fileName); } else { + if (dirPath.startsWith("file://")) { + dirPath = dirPath.substring(7); + } fp = new File(dirPath + File.separator + fileName); } return fp; @@ -819,7 +822,7 @@ public class FileUtils extends Plugin { entry.put("isFile", file.isFile()); entry.put("isDirectory", file.isDirectory()); entry.put("name", file.getName()); - entry.put("fullPath", file.getAbsolutePath()); + entry.put("fullPath", "file://" + file.getAbsolutePath()); // I can't add the next thing it as it would be an infinite loop //entry.put("filesystem", null); From 0ba803ff46fd577913040e67dc83125889382921 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 17:21:03 -0800 Subject: [PATCH 10/20] truncating file:// protocol where applicable in metadata func --- framework/src/org/apache/cordova/FileUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index ec81fc71..3646be6b 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -755,6 +755,9 @@ public class FileUtils extends Plugin { * @throws JSONException */ private JSONObject getFileMetadata(String filePath) throws FileNotFoundException, JSONException { + if (filePath.startsWith("file://")) { + filePath = filePath.substring(7); + } File file = new File(filePath); if (!file.exists()) { From 7e4158ce94f59e15ba9a22778c404d1b388c3c98 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 17:31:07 -0800 Subject: [PATCH 11/20] remove file:// protocol from various fileutils remove methods --- framework/src/org/apache/cordova/FileUtils.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 3646be6b..87be54f9 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -555,6 +555,10 @@ public class FileUtils extends Plugin { * @throws FileExistsException */ private boolean removeRecursively(String filePath) throws FileExistsException { + if (filePath.startsWith("file://")) { + filePath = filePath.substring(7); + } + File fp = new File(filePath); // You can't delete the root directory. @@ -596,6 +600,10 @@ public class FileUtils extends Plugin { * @throws InvalidModificationException */ private boolean remove(String filePath) throws NoModificationAllowedException, InvalidModificationException { + if (filePath.startsWith("file://")) { + filePath = filePath.substring(7); + } + File fp = new File(filePath); // You can't delete the root directory. From cd667d6af5f1382747440ba5312ad740925758ec Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 17:46:16 -0800 Subject: [PATCH 12/20] remove file:// protocol from various fileutils methods --- .../src/org/apache/cordova/FileUtils.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 87be54f9..31a4f526 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -712,6 +712,10 @@ public class FileUtils extends Plugin { * @throws JSONException */ private JSONObject getParent(String filePath) throws JSONException { + if (filePath.startsWith("file://")) { + filePath = filePath.substring(7); + } + if (atRootDirectory(filePath)) { return getEntry(filePath); } @@ -726,8 +730,13 @@ public class FileUtils extends Plugin { * @return true if we are at the root, false otherwise. */ private boolean atRootDirectory(String filePath) { + if (filePath.startsWith("file://")) { + filePath = filePath.substring(7); + } + if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache") || - filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { + filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath()) || + filePath.equals("/data/data/" + ctx.getPackageName() + "/")) { return true; } return false; @@ -742,6 +751,10 @@ public class FileUtils extends Plugin { * @throws JSONException */ private JSONObject getMetadata(String filePath) throws FileNotFoundException, JSONException { + if (filePath.startsWith("file://")) { + filePath = filePath.substring(7); + } + File file = new File(filePath); if (!file.exists()) { @@ -766,6 +779,7 @@ public class FileUtils extends Plugin { if (filePath.startsWith("file://")) { filePath = filePath.substring(7); } + File file = new File(filePath); if (!file.exists()) { @@ -811,7 +825,7 @@ public class FileUtils extends Plugin { } else if (type == PERSISTENT) { fs.put("name", "persistent"); - fs.put("root", "file:///data/data/" + ctx.getPackageName()); + fs.put("root", "file:///data/data/" + ctx.getPackageName() + "/"); } else { throw new IOException("No filesystem of type requested"); From a52ba37cf79157b15bbd76a35c866798603df1c5 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 17:48:48 -0800 Subject: [PATCH 13/20] remove trailing slash --- framework/src/org/apache/cordova/FileUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 31a4f526..73a14863 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -736,7 +736,7 @@ public class FileUtils extends Plugin { if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache") || filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath()) || - filePath.equals("/data/data/" + ctx.getPackageName() + "/")) { + filePath.equals("/data/data/" + ctx.getPackageName())) { return true; } return false; @@ -825,7 +825,7 @@ public class FileUtils extends Plugin { } else if (type == PERSISTENT) { fs.put("name", "persistent"); - fs.put("root", "file:///data/data/" + ctx.getPackageName() + "/"); + fs.put("root", "file:///data/data/" + ctx.getPackageName()); } else { throw new IOException("No filesystem of type requested"); From d87ee719a301e8603518b75cb3a3c17666d18f63 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Thu, 16 Feb 2012 18:17:25 -0800 Subject: [PATCH 14/20] Use strings not objects --- .../src/org/apache/cordova/FileUtils.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 73a14863..c1489679 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -185,11 +185,11 @@ public class FileUtils extends Plugin { } } else if (action.equals("moveTo")) { - JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), true); + JSONObject entry = transferTo(args.getString(0), args.getString(1), args.getString(2), true); return new PluginResult(status, entry); } else if (action.equals("copyTo")) { - JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), false); + JSONObject entry = transferTo(args.getString(0), args.getString(1), args.getString(2), false); return new PluginResult(status, entry); } else if (action.equals("readEntries")) { @@ -281,6 +281,10 @@ public class FileUtils extends Plugin { * @throws JSONException */ private JSONArray readEntries(String fileName) throws FileNotFoundException, JSONException { + if (fileName.startsWith("file://")) { + fileName = fileName.substring(7); + } + File fp = new File(fileName); if (!fp.exists()) { @@ -314,7 +318,15 @@ public class FileUtils extends Plugin { * @throws EncodingException * @throws JSONException */ - private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException { + private JSONObject transferTo(String fileName, String newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException { + if (fileName.startsWith("file://")) { + fileName = fileName.substring(7); + } + if (newParent.startsWith("file://")) { + newParent = newParent.substring(7); + } + + // Check for invalid file name if (newName != null && newName.contains(":")) { throw new EncodingException("Bad file name"); @@ -327,7 +339,7 @@ public class FileUtils extends Plugin { throw new FileNotFoundException("The source does not exist"); } - File destinationDir = new File(newParent.getString("fullPath")); + File destinationDir = new File(newParent); if (!destinationDir.exists()) { // The destination does not exist so we should fail. throw new FileNotFoundException("The source does not exist"); From 7c1eb7da9b1d00aec92ffd153b5d561f4abde266 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Fri, 17 Feb 2012 10:55:04 -0800 Subject: [PATCH 15/20] trim file:// URI from uri->path conversion method --- framework/src/org/apache/cordova/FileUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index c1489679..51fc4ab3 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -914,8 +914,8 @@ public class FileUtils extends Plugin { */ public String readAsText(String filename, String encoding) throws FileNotFoundException, IOException { byte[] bytes = new byte[1000]; - BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); int numRead = 0; while ((numRead = bis.read(bytes, 0, 1000)) >= 0) { bos.write(bytes, 0, numRead); @@ -1025,6 +1025,9 @@ public class FileUtils extends Plugin { return ctx.getContentResolver().openInputStream(uri); } else { + if (path.startsWith("file://")) { + path = path.substring(7); + } return new FileInputStream(path); } } From d6fef8d6e350f1539c38313f7a026053fdc36fd7 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Fri, 17 Feb 2012 11:40:22 -0800 Subject: [PATCH 16/20] more file:// URI truncation :D --- .../src/org/apache/cordova/FileUtils.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 51fc4ab3..7dbf9054 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -932,8 +932,8 @@ public class FileUtils extends Plugin { */ public String readAsDataURL(String filename) throws FileNotFoundException, IOException { byte[] bytes = new byte[1000]; - BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); int numRead = 0; while ((numRead = bis.read(bytes, 0, 1000)) >= 0) { bos.write(bytes, 0, numRead); @@ -975,20 +975,24 @@ public class FileUtils extends Plugin { */ /**/ public long write(String filename, String data, int offset) throws FileNotFoundException, IOException { + if (filename.startsWith("file://")) { + filename = filename.substring(7); + } + boolean append = false; if (offset > 0) { this.truncateFile(filename, offset); append = true; } - byte [] rawData = data.getBytes(); - ByteArrayInputStream in = new ByteArrayInputStream(rawData); - FileOutputStream out = new FileOutputStream(filename, append); - byte buff[] = new byte[rawData.length]; - in.read(buff, 0, buff.length); - out.write(buff, 0, rawData.length); - out.flush(); - out.close(); + byte [] rawData = data.getBytes(); + ByteArrayInputStream in = new ByteArrayInputStream(rawData); + FileOutputStream out = new FileOutputStream(filename, append); + byte buff[] = new byte[rawData.length]; + in.read(buff, 0, buff.length); + out.write(buff, 0, rawData.length); + out.flush(); + out.close(); return data.length(); } @@ -1001,6 +1005,10 @@ public class FileUtils extends Plugin { * @throws FileNotFoundException, IOException */ private long truncateFile(String filename, long size) throws FileNotFoundException, IOException { + if (filename.startsWith("file://")) { + filename = filename.substring(7); + } + RandomAccessFile raf = new RandomAccessFile(filename, "rw"); if (raf.length() >= size) { From 4d03577220a9d7cafc8f5ea1e0f1f60a54008c1a Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Fri, 17 Feb 2012 17:28:35 -0600 Subject: [PATCH 17/20] Add cordova.android.js from common-js build. --- framework/assets/js/cordova.android.js | 4612 ++++++++++++++++++++++++ 1 file changed, 4612 insertions(+) create mode 100755 framework/assets/js/cordova.android.js diff --git a/framework/assets/js/cordova.android.js b/framework/assets/js/cordova.android.js new file mode 100755 index 00000000..9e62fdd1 --- /dev/null +++ b/framework/assets/js/cordova.android.js @@ -0,0 +1,4612 @@ +/* + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011 Adobe + Copyright 2011 IBM Corporation + Copyright 2011 RIM + + Licensed 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. + + + +*/ +var require, + define; + +(function () { + var modules = {}; + + function build(module) { + var factory = module.factory; + module.exports = {}; + delete module.factory; + factory(require, module.exports, module); + return module.exports; + } + + require = function (id) { + if (!modules[id]) { + throw "module " + id + " not found"; + } + return modules[id].factory ? build(modules[id]) : modules[id].exports; + } + + define = function (id, factory) { + if (modules[id]) { + throw "module " + id + " already defined"; + } + + modules[id] = { + id: id, + factory: factory + }; + } + + define.remove = function (id) { + delete modules[id]; + }; + +})(); + +//Export for use in node +if (typeof module === "object" && typeof require === "function") { + module.exports.require = require; + module.exports.define = define; +} + +define('cordova/channel', function(require, exports, module) { +/** + * Custom pub-sub channel that can have functions subscribed to it + * @constructor + * @param type String the channel name + * @param opts Object options to pass into the channel, currently + * supports: + * onSubscribe: callback that fires when + * something subscribes to the Channel. Sets + * context to the Channel. + * onUnsubscribe: callback that fires when + * something unsubscribes to the Channel. Sets + * context to the Channel. + */ +var Channel = function(type, opts) { + this.type = type; + this.handlers = []; + this.guid = 0; + this.fired = false; + this.enabled = true; + this.events = { + onSubscribe:null, + onUnsubscribe:null + }; + if (opts) { + if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe; + if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe; + } + }, + channel = { + /** + * Calls the provided function only after all of the channels specified + * have been fired. + */ + join: function (h, c) { + var i = c.length; + var len = i; + var f = function() { + if (!(--i)) h(); + }; + for (var j=0; j 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); + } +}; + +}); +define('cordova/common', function(require, exports, module) { +module.exports = { + objects: { + cordova: { + path: 'cordova', + children: { + exec: { + path: 'cordova/exec' + } + } + }, + navigator: { + children: { + notification: { + path: 'cordova/plugin/notification' + }, + accelerometer: { + path: 'cordova/plugin/accelerometer' + }, + battery: { + path: 'cordova/plugin/battery' + }, + camera:{ + path: 'cordova/plugin/Camera' + }, + compass:{ + path: 'cordova/plugin/compass' + }, + contacts: { + path: 'cordova/plugin/contacts' + }, + device:{ + children:{ + capture: { + path: 'cordova/plugin/capture' + } + } + }, + geolocation: { + path: 'cordova/plugin/geolocation' + }, + network: { + children: { + connection: { + path: 'cordova/plugin/network' + } + } + } + } + }, + Acceleration: { + path: 'cordova/plugin/Acceleration' + }, + Camera:{ + path: 'cordova/plugin/CameraConstants' + }, + CaptureError: { + path: 'cordova/plugin/CaptureError' + }, + CaptureAudioOptions:{ + path: 'cordova/plugin/CaptureAudioOptions' + }, + CaptureImageOptions: { + path: 'cordova/plugin/CaptureImageOptions' + }, + CaptureVideoOptions: { + path: 'cordova/plugin/CaptureVideoOptions' + }, + CompassHeading:{ + path: 'cordova/plugin/CompassHeading' + }, + CompassError:{ + path: 'cordova/plugin/CompassConstants' + }, + ConfigurationData: { + path: 'cordova/plugin/ConfigurationData' + }, + Connection: { + path: 'cordova/plugin/Connection' + }, + Contact: { + path: 'cordova/plugin/Contact' + }, + ContactAddress: { + path: 'cordova/plugin/ContactAddress' + }, + ContactError: { + path: 'cordova/plugin/ContactError' + }, + ContactField: { + path: 'cordova/plugin/ContactField' + }, + ContactFindOptions: { + path: 'cordova/plugin/ContactFindOptions' + }, + ContactName: { + path: 'cordova/plugin/ContactName' + }, + ContactOrganization: { + path: 'cordova/plugin/ContactOrganization' + }, + Coordinates: { + path: 'cordova/plugin/Coordinates' + }, + DirectoryEntry: { + path: 'cordova/plugin/DirectoryEntry' + }, + DirectoryReader: { + path: 'cordova/plugin/DirectoryReader' + }, + Entry: { + path: 'cordova/plugin/Entry' + }, + File: { + path: 'cordova/plugin/File' + }, + FileEntry: { + path: 'cordova/plugin/FileEntry' + }, + FileError: { + path: 'cordova/plugin/FileError' + }, + FileReader: { + path: 'cordova/plugin/FileReader' + }, + FileSystem: { + path: 'cordova/plugin/FileSystem' + }, + FileTransfer: { + path: 'cordova/plugin/FileTransfer' + }, + FileTransferError: { + path: 'cordova/plugin/FileTransferError' + }, + FileUploadOptions: { + path: 'cordova/plugin/FileUploadOptions' + }, + FileUploadResult: { + path: 'cordova/plugin/FileUploadResult' + }, + FileWriter: { + path: 'cordova/plugin/FileWriter' + }, + Flags: { + path: 'cordova/plugin/Flags' + }, + LocalFileSystem: { + path: 'cordova/plugin/LocalFileSystem' + }, + Media: { + path: 'cordova/plugin/Media' + }, + MediaError: { + path: 'cordova/plugin/MediaError' + }, + MediaFile: { + path: 'cordova/plugin/MediaFile' + }, + MediaFileData:{ + path: 'cordova/plugin/MediaFileData' + }, + Metadata:{ + path: 'cordova/plugin/Metadata' + }, + Position: { + path: 'cordova/plugin/Position' + }, + PositionError: { + path: 'cordova/plugin/PositionError' + }, + ProgressEvent: { + path: 'cordova/plugin/ProgressEvent' + }, + requestFileSystem:{ + path: 'cordova/plugin/requestFileSystem' + }, + resolveLocalFileSystemURI:{ + path: 'cordova/plugin/resolveLocalFileSystemURI' + } + } +}; + +}); +define('cordova/platform', function(require, exports, module) { +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.handlers.length === 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.handlers.length === 0) { + exec(null, null, "App", "overrideBackbutton", [false]); + } + } + }); + + // Add hardware MENU and SEARCH button handlers + cordova.addDocumentEventHandler('menubutton'); + cordova.addDocumentEventHandler('searchbutton'); + + // 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]); + + // 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 shit hits the fan. + 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) { + // TOOD: this is wrong + setupDroidDB(); + 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(); + } + }, + 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" + } + } +}; + +}); +define('cordova/utils', function(require, exports, module) { +function UUIDcreatePart(length) { + var uuidpart = ""; + for (var i=0; i 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; + +}); + + +define('cordova/plugin/battery', function(require, exports, module) { +/** + * This class contains information about the current battery status. + * @constructor + */ +var cordova = require('cordova'), + exec = require('cordova/exec'); + +function handlers() { + var count = function (a) { + return a.filter(function (v) {return !!v;}).length; + }; + + return count(module.exports.channels.batterystatus.handlers) + + count(module.exports.channels.batterylow.handlers) + + count(module.exports.channels.batterycritical.handlers); +} + +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 = module.exports; // TODO: i dont like this reference + // 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 = module.exports, + empty = function (a) { + return a.filter(function (v, i) {return v && !!i;}); + }; + // 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 = module.exports;//TODO: can we eliminate this global ref? + 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); +}; + +module.exports = new Battery(); + +}); + + +define('cordova/plugin/Camera', function(require, exports, module) { +var exec = require('cordova/exec'), + Camera = require('cordova/plugin/CameraConstants'); + +module.exports = { + /** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=FILE_URL. + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ + getPicture: function (successCallback, errorCallback, options) { + + // successCallback required + if (typeof successCallback != "function") { + console.log("Camera Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback != "function")) { + console.log("Camera Error: errorCallback is not a function"); + return; + } + + if (options && typeof options.quality == "number") { + quality = options.quality; + } else if (options && typeof options.quality == "string") { + var qlity = parseInt(options.quality, 10); + if (isNaN(qlity) === false) { + quality = qlity.valueOf(); + } + } + + var destinationType = Camera.DestinationType.FILE_URL; + if (options.destinationType) { + destinationType = options.destinationType; + } + + var sourceType = Camera.PictureSourceType.CAMERA; + if (typeof options.sourceType == "number") { + sourceType = options.sourceType; + } + + var targetWidth = -1; + if (typeof options.targetWidth == "number") { + targetWidth = options.targetWidth; + } else if (typeof options.targetWidth == "string") { + var width = parseInt(options.targetWidth, 10); + if (isNaN(width) === false) { + targetWidth = width.valueOf(); + } + } + + var targetHeight = -1; + if (typeof options.targetHeight == "number") { + targetHeight = options.targetHeight; + } else if (typeof options.targetHeight == "string") { + var height = parseInt(options.targetHeight, 10); + if (isNaN(height) === false) { + targetHeight = height.valueOf(); + } + } + + var encodingType = Camera.EncodingType.JPEG; + if (typeof options.encodingType == "number") { + encodingType = options.encodingType; + } + + exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]); + } +}; + +}); + +define('cordova/plugin/CameraConstants', function(require, exports, module) { +module.exports = { + DestinationType:{ + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android) + }, + EncodingType:{ + JPEG: 0, // Return JPEG encoded image + PNG: 1 // Return PNG encoded image + }, + MediaType:{ + PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType + VIDEO: 1, // allow selection of video only, ONLY RETURNS URL + ALLMEDIA : 2 // allow selection from all media types + }, + PictureSourceType:{ + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) + } +}; + +}); + +define('cordova/plugin/capture', function(require, exports, module) { +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(result) { + 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); + } + 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(); + +}); + +define('cordova/plugin/CaptureAudioOptions', function(require, exports, module) { +/** + * Encapsulates all audio capture operation configuration options. + */ +var CaptureAudioOptions = function(){ + // Upper limit of sound clips user can record. Value must be equal or greater than 1. + this.limit = 1; + // Maximum duration of a single sound clip in seconds. + this.duration = 0; + // The selected audio mode. Must match with one of the elements in supportedAudioModes array. + this.mode = null; +}; + +module.exports = CaptureAudioOptions; + +}); + +define('cordova/plugin/CaptureError', function(require, exports, module) { +/** + * The CaptureError interface encapsulates all errors in the Capture API. + */ +var CaptureError = function(c) { + this.code = c || null; +}; + +// Camera or microphone failed to capture image or sound. +CaptureError.CAPTURE_INTERNAL_ERR = 0; +// Camera application or audio capture application is currently serving other capture request. +CaptureError.CAPTURE_APPLICATION_BUSY = 1; +// Invalid use of the API (e.g. limit parameter has value less than one). +CaptureError.CAPTURE_INVALID_ARGUMENT = 2; +// User exited camera application or audio capture application before capturing anything. +CaptureError.CAPTURE_NO_MEDIA_FILES = 3; +// The requested capture operation is not supported. +CaptureError.CAPTURE_NOT_SUPPORTED = 20; + +module.exports = CaptureError; + +}); + +define('cordova/plugin/CaptureImageOptions', function(require, exports, module) { +/** + * Encapsulates all image capture operation configuration options. + */ +var CaptureImageOptions = function(){ + // Upper limit of images user can take. Value must be equal or greater than 1. + this.limit = 1; + // The selected image mode. Must match with one of the elements in supportedImageModes array. + this.mode = null; +}; + +module.exports = CaptureImageOptions; + +}); + +define('cordova/plugin/CaptureVideoOptions', function(require, exports, module) { +/** + * Encapsulates all video capture operation configuration options. + */ +var CaptureVideoOptions = function(){ + // Upper limit of videos user can record. Value must be equal or greater than 1. + this.limit = 1; + // Maximum duration of a single video clip in seconds. + this.duration = 0; + // The selected video mode. Must match with one of the elements in supportedVideoModes array. + this.mode = null; +}; + +module.exports = CaptureVideoOptions; + +}); + +define('cordova/plugin/compass', function(require, exports, module) { +var exec = require('cordova/exec'), + utils = require('cordova/utils'), + 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, options) { + // 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) { + if (result.timestamp) { + var timestamp = new Date(result.timestamp); + result.timestamp = timestamp; + } + successCallback(result); + }; + + // Get heading + exec(win, errorCallback, "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(); + var win = function(result) { + if (result.timestamp) { + var timestamp = new Date(result.timestamp); + result.timestamp = timestamp; + } + successCallback(result); + }; + timers[id] = window.setInterval(function() { + exec(win, errorCallback, "Compass", "getHeading", []); + }, 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]; + } + } + }; + +module.exports = compass; + +}); + +define('cordova/plugin/CompassConstants', function(require, exports, module) { +module.exports = { + COMPASS_INTERNAL_ERR:0, + COMPASS_NOT_SUPPORTED:20 +}; + +}); + +define('cordova/plugin/CompassHeading', function(require, exports, module) { +var CompassHeading = function() { + this.magneticHeading = null; + this.trueHeading = null; + this.headingAccuracy = null; + this.timestamp = new Date(); +}; + +module.exports = CompassHeading; + +}); + +define('cordova/plugin/ConfigurationData', function(require, exports, module) { +/** + * Encapsulates a set of parameters that the capture device supports. + */ +function ConfigurationData() { + // The ASCII-encoded string in lower case representing the media type. + this.type = null; + // The height attribute represents height of the image or video in pixels. + // In the case of a sound clip this attribute has value 0. + this.height = 0; + // The width attribute represents width of the image or video in pixels. + // In the case of a sound clip this attribute has value 0 + this.width = 0; +} + +module.exports = ConfigurationData; + +}); + +define('cordova/plugin/Connection', function(require, exports, module) { +/** + * Network status + */ +module.exports = { + UNKNOWN: "unknown", + ETHERNET: "ethernet", + WIFI: "wifi", + CELL_2G: "2g", + CELL_3G: "3g", + CELL_4G: "4g", + NONE: "none" +}; + +}); + +define('cordova/plugin/Contact', function(require, exports, module) { +var exec = require('cordova/exec'), + ContactError = require('cordova/plugin/ContactError'), + utils = require('cordova/utils'); + +/** +* Contains information about a single contact. +* @constructor +* @param {DOMString} id unique identifier +* @param {DOMString} displayName +* @param {ContactName} name +* @param {DOMString} nickname +* @param {Array.} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || []; // ContactField[] + this.emails = emails || []; // ContactField[] + this.addresses = addresses || []; // ContactAddress[] + this.ims = ims || []; // ContactField[] + this.organizations = organizations || []; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || []; // ContactField[] + this.categories = categories || []; // ContactField[] + this.urls = urls || []; // ContactField[] +}; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + if (this.id === null) { + var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); + errorCB(errorObj); + } + else { + exec(successCB, errorCB, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = utils.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.categories) { + for (i = 0; i < clonedContact.categories.length; i++) { + clonedContact.categories[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + exec(successCB, errorCB, "Contacts", "save", [this]); +}; + + +module.exports = Contact; + +}); + +define('cordova/plugin/ContactAddress', function(require, exports, module) { +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted // NOTE: not a W3C standard +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ + +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +module.exports = ContactAddress; + +}); + +define('cordova/plugin/ContactError', function(require, exports, module) { +/** + * ContactError. + * An error code assigned by an implementation when an error has occured + * @constructor + */ +var ContactError = function(err) { + this.code = (typeof err != 'undefined' ? err : null); +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +module.exports = ContactError; + +}); + +define('cordova/plugin/ContactField', function(require, exports, module) { +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = type || null; + this.value = value || null; + this.pref = (typeof pref != 'undefined' ? pref : false); +}; + +module.exports = ContactField; + +}); + +define('cordova/plugin/ContactFindOptions', function(require, exports, module) { +/** + * ContactFindOptions. + * @constructor + * @param filter used to match contacts against + * @param multiple boolean used to determine if more than one contact should be returned + */ + +var ContactFindOptions = function(filter, multiple) { + this.filter = filter || ''; + this.multiple = (typeof multiple != 'undefined' ? multiple : false); +}; + +module.exports = ContactFindOptions; + +}); + +define('cordova/plugin/ContactName', function(require, exports, module) { +/** +* Contact name. +* @constructor +* @param formatted // NOTE: not part of W3C standard +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +module.exports = ContactName; + +}); + +define('cordova/plugin/ContactOrganization', function(require, exports, module) { +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ + +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = (typeof pref != 'undefined' ? pref : false); + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +module.exports = ContactOrganization; + +}); + +define('cordova/plugin/contacts', function(require, exports, module) { +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; + +}); + +define('cordova/plugin/Coordinates', function(require, exports, module) { +/** + * This class contains position information. + * @param {Object} lat + * @param {Object} lng + * @param {Object} alt + * @param {Object} acc + * @param {Object} head + * @param {Object} vel + * @param {Object} altacc + * @constructor + */ +var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) { + /** + * The latitude of the position. + */ + this.latitude = lat; + /** + * The longitude of the position, + */ + this.longitude = lng; + /** + * The accuracy of the position. + */ + this.accuracy = acc; + /** + * The altitude of the position. + */ + this.altitude = alt; + /** + * The direction the device is moving at the position. + */ + this.heading = head; + /** + * The velocity with which the device is moving at the position. + */ + this.speed = vel; + /** + * The altitude accuracy of the position. + */ + this.altitudeAccuracy = (altacc !== undefined) ? altacc : null; +}; + +module.exports = Coordinates; + +}); + +define('cordova/plugin/DirectoryEntry', function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + Entry = require('cordova/plugin/Entry'), + DirectoryReader = require('cordova/plugin/DirectoryReader'); + +/** + * An interface representing a directory on the file system. + * + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function(name, fullPath) { + DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); +}; + +utils.extend(DirectoryEntry, Entry); + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function() { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + var win = function(result) { + var entry = new DirectoryEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + exec(successCallback, errorCallback, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + var win = function(result) { + var FileEntry = require('cordova/plugin/FileEntry'); + var entry = new FileEntry(result.name, result.fullPath); + successCallback(entry); + }; + var fail = function(code) { + errorCallback(new FileError(code)); + }; + exec(win, fail, "File", "getFile", [this.fullPath, path, options]); +}; + +module.exports = DirectoryEntry; + +}); + +define('cordova/plugin/DirectoryReader', function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * An interface that lists the files and directories in a directory. + */ +function DirectoryReader(path) { + this.path = path || null; +} + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + var win = function(result) { + var retVal = []; + for (var i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // Already loading something + if (this.readyState == FileReader.LOADING) { + throw new FileError(FileError.INVALID_STATE_ERR); + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart(new ProgressEvent("loadstart", {target:this})); + } + + var me = this; + + // Read file + exec( + // Success callback + function(r) { + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload(new ProgressEvent("load", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, + // Error callback + function(e) { + var evt; + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // DONE state + me.readyState = FileReader.DONE; + + me.result = null; + + // Save error + me.error = new FileError(e); + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror(new ProgressEvent("error", {target:me})); + } + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend(new ProgressEvent("loadend", {target:me})); + } + }, "File", "readAsDataURL", [this.fileName]); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + console.log('This method is not supported at this time.'); +}; + +module.exports = FileReader; + +}); + +define('cordova/plugin/FileSystem', function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'); + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function(name, root) { + this.name = name || null; + if (root) { + this.root = new DirectoryEntry(name, root); + } +}; + +module.exports = FileSystem; + +}); + +define('cordova/plugin/FileTransfer', function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * FileTransfer uploads a file to a remote server. + * @constructor + */ +var FileTransfer = function() {}; + +/** +* Given an absolute file path, uploads a file on the device to a remote server +* using a multipart HTTP request. +* @param filePath {String} Full path of the file on the device +* @param server {String} URL of the server to receive the file +* @param successCallback (Function} Callback to be invoked when upload has completed +* @param errorCallback {Function} Callback to be invoked upon error +* @param options {FileUploadOptions} Optional parameters such as file name and mimetype +*/ +FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) { + // check for options + var fileKey = null; + var fileName = null; + var mimeType = null; + var params = null; + var chunkedMode = true; + if (options) { + fileKey = options.fileKey; + fileName = options.fileName; + mimeType = options.mimeType; + if (options.chunkedMode !== null || typeof options.chunkedMode !== "undefined") { + chunkedMode = options.chunkedMode; + } + if (options.params) { + params = options.params; + } + else { + params = {}; + } + } + + exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]); +}; + +/** + * Downloads a file form a given URL and saves it to the specified directory. + * @param source {String} URL of the server to receive the file + * @param target {String} Full path of the file on the device + * @param successCallback (Function} Callback to be invoked when upload has completed + * @param errorCallback {Function} Callback to be invoked upon error + */ +FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + var win = function(result) { + var entry = null; + if (result.isDirectory) { + entry = new DirectoryEntry(); + } + else if (result.isFile) { + entry = new FileEntry(); + } + entry.isDirectory = result.isDirectory; + entry.isFile = result.isFile; + entry.name = result.name; + entry.fullPath = result.fullPath; + successCallback(entry); + }; + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); +}; + +module.exports = FileTransfer; + +}); + +define('cordova/plugin/FileTransferError', function(require, exports, module) { +/** + * FileTransferError + * @constructor + */ +var FileTransferError = function(code) { + this.code = code || null; +}; + +FileTransferError.FILE_NOT_FOUND_ERR = 1; +FileTransferError.INVALID_URL_ERR = 2; +FileTransferError.CONNECTION_ERR = 3; + +module.exports = FileTransferError; + +}); + +define('cordova/plugin/FileUploadOptions', function(require, exports, module) { +/** + * 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; + +}); + +define('cordova/plugin/FileUploadResult', function(require, exports, module) { +/** + * FileUploadResult + * @constructor + */ +var FileUploadResult = function() { + this.bytesSent = 0; + this.responseCode = null; + this.response = null; +}; + +module.exports = FileUploadResult; + +}); + +define('cordova/plugin/FileWriter', function(require, exports, module) { +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) { + var evt; + // 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; + +}); + +define('cordova/plugin/Flags', function(require, exports, module) { +/** + * 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; + +}); + +define('cordova/plugin/geolocation', function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + PositionError = require('cordova/plugin/PositionError'); + +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 (typeof options.maximumAge !== "undefined") { + opt.maximumAge = options.maximumAge; + } + if (typeof options.enableHighAccuracy !== "undefined") { + opt.enableHighAccuracy = options.enableHighAccuracy; + } + if (typeof 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); + exec(successCallback, errorCallback, "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() { + exec(successCallback, errorCallback, "Geolocation", "getLocation", [options.enableHighAccuracy, options.timeout, options.maximumAge]); + }, 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; + +}); + + +define('cordova/plugin/LocalFileSystem', function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Represents a local file system. + */ +var LocalFileSystem = function() { + +}; + +// Non-standard function +LocalFileSystem.prototype.isFileSystemRoot = function(path) { + return exec(null, null, "File", "isFileSystemRoot", [path]); +}; + +LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence +LocalFileSystem.PERSISTENT = 1; //persistent + +module.exports = LocalFileSystem; + +}); + +define('cordova/plugin/Media', function(require, exports, module) { +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; + } + + // statusCallback optional + if (positionCallback && (typeof positionCallback !== "function")) { + console.log("Media Error: positionCallback 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]); +}; + +module.exports = Media; + +}); + +define('cordova/plugin/MediaError', function(require, exports, module) { +/** + * This class contains information about any Media errors. + * @constructor + */ +var MediaError = function(code, msg) { + this.code = 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; + +}); + +define('cordova/plugin/MediaFile', function(require, exports, module) { +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.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; + +}); + +define('cordova/plugin/requestFileSystem', function(require, exports, module) { +var FileError = require('cordova/plugin/FileError'), + FileSystem = require('cordova/plugin/FileSystem'), + exec = require('cordova/exec'); + +/** + * Request a file system in which to store application data. + * @param type local file system type + * @param size indicates how much storage space, in bytes, the application expects to need + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + */ +var requestFileSystem = function(type, size, successCallback, errorCallback) { + if (type < 0 || type > 3) { + if (typeof errorCallback === "function") { + errorCallback(new FileError(FileError.SYNTAX_ERR)); + } + } else { + // if successful, return a FileSystem object + var success = function(file_system) { + if (file_system) { + // grab the name and root from the file system object + var result = new FileSystem(file_system.name, file_system.root); + successCallback(result); + } + else { + // no FileSystem object returned + errorCallback(new FileError(FileError.NOT_FOUND_ERR)); + } + }; + exec(success, errorCallback, "File", "requestFileSystem", [type, size]); + } +}; + +module.exports = requestFileSystem; + +}); + +define('cordova/plugin/resolveLocalFileSystemURI', function(require, exports, module) { +var DirectoryEntry = require('cordova/plugin/DirectoryEntry'), + FileEntry = require('cordova/plugin/FileEntry'), + exec = require('cordova/exec'); + +/** + * Look up file system Entry referred to by local URI. + * @param {DOMString} uri URI referring to a local file or directory + * @param successCallback invoked with Entry object corresponding to URI + * @param errorCallback invoked if error occurs retrieving file system entry + */ +module.exports = function(uri, successCallback, errorCallback) { + // error callback + var fail = function(error) { + errorCallback(new FileError(error)); + }; + // if successful, return either a file or directory entry + var success = function(entry) { + var result; + + if (entry) { + // create appropriate Entry object + result = (entry.isDirectory) ? new DirectoryEntry(entry.name, entry.fullPath) : new FileEntry(entry.name, entry.fullPath); + try { + successCallback(result); + } + catch (e) { + console.log('Error invoking callback: ' + e); + } + } + else { + // no Entry object returned + fail(FileError.NOT_FOUND_ERR); + } + }; + + exec(success, fail, "File", "resolveLocalFileSystemURI", [uri]); +}; + +}); + + +define('cordova/plugin/android/app', function(require, exports, module) { +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", []); + } +}; + +}); + +define('cordova/plugin/android/callback', function(require, exports, module) { +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; + +}); + +define('cordova/plugin/android/device', function(require, exports, module) { +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(); + +}); + +define('cordova/plugin/android/polling', function(require, exports, module) { +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; + +}); + +define('cordova/plugin/android/storage', function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'); + +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() { + 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); + }); + + }, + 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 +}; + +}); +window.cordova = require('cordova'); +(function (context) { + var channel = require("cordova/channel"), + /** + * cordova Channels that must fire before "deviceready" is fired. + */ + deviceReadyChannelsArray = [channel.onCordovaReady, channel.onCordovaInfoReady, channel.onCordovaConnectionReady], + deviceReadyChannelsMap = {}, + _self = { + boot: function () { + //--------------- + // Event handling + //--------------- + + /** + * Listen for DOMContentLoaded and notify our channel subscribers. + */ + document.addEventListener('DOMContentLoaded', function() { + channel.onDOMContentLoaded.fire(); + }, false); + if (document.readyState == 'complete') { + channel.onDOMContentLoaded.fire(); + } + + /** + * Create all cordova objects once page has fully loaded and native side is ready. + */ + channel.join(function() { + var builder = require('cordova/builder'), + base = require('cordova/common'), + platform = require('cordova/platform'); + + // Drop the common globals into the window object, but be nice and don't overwrite anything. + builder.build(base.objects).intoButDontClobber(window); + + // Drop the platform-specific globals into the window object and do it like a honey badger does it. + builder.build(platform.objects).intoAndClobberTheFOutOf(window); + + // Call the platform-specific initialization + platform.initialize(); + + // Fire event to notify that all objects are created + channel.onCordovaReady.fire(); + + // Fire onDeviceReady event once all constructors have run and + // cordova info has been received from native side. + channel.join(function() { + channel.onDeviceReady.fire(); + + // Fire the onresume event, since first one happens before JavaScript is loaded + channel.onResume.fire(); + }, deviceReadyChannelsArray); + + }, [ channel.onDOMContentLoaded, channel.onNativeReady ]); + } + }; + // boot up once native side is ready + channel.onNativeReady.subscribe(_self.boot); + + // _nativeReady is global variable that the native side can set + // to signify that the native code is ready. It is a global since + // it may be called before any cordova JS is ready. + if (window._nativeReady) { + channel.onNativeReady.fire(); + } + +}(window)); + From 14b368e731d3dac388c7a76722ace11043bf4bd7 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Fri, 17 Feb 2012 17:30:25 -0600 Subject: [PATCH 18/20] Update build to use new common cordova.android.js instead of building js from multiple Android js files. --- framework/build.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/build.xml b/framework/build.xml index 95c4915a..e10fcf3f 100644 --- a/framework/build.xml +++ b/framework/build.xml @@ -92,6 +92,7 @@ + @@ -130,8 +132,11 @@ + + From cc8edea06578780598e1bda573f24845e119ed89 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Sat, 18 Feb 2012 13:19:50 -0600 Subject: [PATCH 19/20] Call js code using new common js way. --- .../org/apache/cordova/LinearLayoutSoftKeyboardDetect.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java b/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java index 3f933005..31ccf8a9 100755 --- a/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java +++ b/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java @@ -87,13 +87,13 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout { // gone away. else if (height > oldHeight) { if(app != null) - app.sendJavascript("Cordova.fireDocumentEvent('hidekeyboard');"); + app.sendJavascript("require('cordova').fireDocumentEvent('hidekeyboard');"); } // If the height as gotten smaller then we will assume the soft keyboard has // been displayed. else if (height < oldHeight) { if(app != null) - app.sendJavascript("Cordova.fireDocumentEvent('showkeyboard');"); + app.sendJavascript("require('cordova').fireDocumentEvent('showkeyboard');"); } // Update the old height for the next event From 918877315242477a2a50b7b8f42ec24da65a63d5 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Sun, 19 Feb 2012 00:15:23 -0600 Subject: [PATCH 20/20] Arguments are passed as JSON array, not JSON object. --- .../src/org/apache/cordova/CameraLauncher.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index ec1cc361..c7eac886 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -108,16 +108,12 @@ public class CameraLauncher extends Plugin { this.mediaType = PICTURE; this.mQuality = 80; - JSONObject options = args.optJSONObject(0); - if (options != null) { - srcType = options.getInt("sourceType"); - destType = options.getInt("destinationType"); - this.targetHeight = options.getInt("targetHeight"); - this.targetWidth = options.getInt("targetWidth"); - this.encodingType = options.getInt("encodingType"); - this.mediaType = options.getInt("mediaType"); - this.mQuality = options.getInt("quality"); - } + this.mQuality = args.getInt(0); + destType = args.getInt(1); + srcType = args.getInt(2); + this.targetWidth = args.getInt(3); + this.targetHeight = args.getInt(4); + this.encodingType = args.getInt(5); if (srcType == CAMERA) { this.takePicture(destType, encodingType);