diff options
33 files changed, 1842 insertions, 1128 deletions
diff --git a/phonegap/Android/.classpath b/phonegap/Android/.classpath index 075ed1757..e088a5d5b 100644 --- a/phonegap/Android/.classpath +++ b/phonegap/Android/.classpath @@ -4,6 +4,6 @@ <classpathentry kind="src" path="gen"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/> <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/> - <classpathentry kind="lib" path="libs/cordova-1.8.0.jar"/> + <classpathentry kind="lib" path="libs/cordova-2.1.0.jar"/> <classpathentry kind="output" path="bin/classes"/> </classpath> diff --git a/phonegap/Android/AndroidManifest.xml b/phonegap/Android/AndroidManifest.xml index b9540ffbe..8e7f49994 100644 --- a/phonegap/Android/AndroidManifest.xml +++ b/phonegap/Android/AndroidManifest.xml @@ -4,7 +4,16 @@ android:versionCode="1" android:versionName="1.0" > - <uses-sdk android:minSdkVersion="10" /> + <uses-sdk + android:minSdkVersion="7" + android:targetSdkVersion="15" /> + + <supports-screens + android:largeScreens="true" + android:normalScreens="true" + android:smallScreens="true" + android:resizeable="true" + android:anyDensity="true" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/> @@ -15,10 +24,12 @@ <application android:icon="@drawable/ic_launcher" - android:label="@string/app_name"> + android:label="@string/app_name" + android:theme="@style/AppTheme" > <activity android:name=".AndroidActivity" - android:label="@string/app_name" > + android:label="@string/title_activity_main" + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" > <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/phonegap/Android/gen/org/mysociety/FixMyStreet/R.java b/phonegap/Android/gen/org/mysociety/FixMyStreet/R.java index adcacc076..9dd2e6d98 100644 --- a/phonegap/Android/gen/org/mysociety/FixMyStreet/R.java +++ b/phonegap/Android/gen/org/mysociety/FixMyStreet/R.java @@ -11,17 +11,28 @@ public final class R { public static final class attr { } public static final class drawable { - public static final int ic_launcher=0x7f020000; + public static final int ic_action_search=0x7f020000; + public static final int ic_launcher=0x7f020001; + } + public static final class id { + public static final int menu_settings=0x7f080000; } public static final class layout { - public static final int main=0x7f030000; + public static final int activity_main=0x7f030000; + } + public static final class menu { + public static final int activity_main=0x7f070000; } public static final class string { - public static final int app_name=0x7f050001; - public static final int hello=0x7f050000; + public static final int app_name=0x7f050000; + public static final int hello=0x7f050001; + public static final int menu_settings=0x7f050002; + public static final int title_activity_main=0x7f050003; + } + public static final class style { + public static final int AppTheme=0x7f060000; } public static final class xml { - public static final int cordova=0x7f040000; - public static final int plugins=0x7f040001; + public static final int config=0x7f040000; } } diff --git a/phonegap/Android/libs/cordova-1.8.0.jar b/phonegap/Android/libs/cordova-1.8.0.jar Binary files differdeleted file mode 100755 index 326fa8392..000000000 --- a/phonegap/Android/libs/cordova-1.8.0.jar +++ /dev/null diff --git a/phonegap/Android/libs/cordova-2.1.0.jar b/phonegap/Android/libs/cordova-2.1.0.jar Binary files differnew file mode 100644 index 000000000..d3eecaaaf --- /dev/null +++ b/phonegap/Android/libs/cordova-2.1.0.jar diff --git a/phonegap/Android/proguard-project.txt b/phonegap/Android/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/phonegap/Android/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/phonegap/Android/proguard.cfg b/phonegap/Android/proguard.cfg deleted file mode 100644 index b1cdf17b5..000000000 --- a/phonegap/Android/proguard.cfg +++ /dev/null @@ -1,40 +0,0 @@ --optimizationpasses 5 --dontusemixedcaseclassnames --dontskipnonpubliclibraryclasses --dontpreverify --verbose --optimizations !code/simplification/arithmetic,!field/*,!class/merging/* - --keep public class * extends android.app.Activity --keep public class * extends android.app.Application --keep public class * extends android.app.Service --keep public class * extends android.content.BroadcastReceiver --keep public class * extends android.content.ContentProvider --keep public class * extends android.app.backup.BackupAgentHelper --keep public class * extends android.preference.Preference --keep public class com.android.vending.licensing.ILicensingService - --keepclasseswithmembernames class * { - native <methods>; -} - --keepclasseswithmembers class * { - public <init>(android.content.Context, android.util.AttributeSet); -} - --keepclasseswithmembers class * { - public <init>(android.content.Context, android.util.AttributeSet, int); -} - --keepclassmembers class * extends android.app.Activity { - public void *(android.view.View); -} - --keepclassmembers enum * { - public static **[] values(); - public static ** valueOf(java.lang.String); -} - --keep class * implements android.os.Parcelable { - public static final android.os.Parcelable$Creator *; -} diff --git a/phonegap/Android/project.properties b/phonegap/Android/project.properties index f049142c1..9b84a6b4b 100644 --- a/phonegap/Android/project.properties +++ b/phonegap/Android/project.properties @@ -3,9 +3,12 @@ # # This file must be checked in Version Control Systems. # -# To customize properties used by the Ant build system use, +# To customize properties used by the Ant build system edit # "ant.properties", and override values to adapt the script to your # project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-10 +target=android-16 diff --git a/phonegap/Android/res/drawable-hdpi/ic_action_search.png b/phonegap/Android/res/drawable-hdpi/ic_action_search.png Binary files differnew file mode 100644 index 000000000..67de12dec --- /dev/null +++ b/phonegap/Android/res/drawable-hdpi/ic_action_search.png diff --git a/phonegap/Android/res/drawable-hdpi/ic_launcher.png b/phonegap/Android/res/drawable-hdpi/ic_launcher.png Binary files differindex 8074c4c57..a301d5795 100644 --- a/phonegap/Android/res/drawable-hdpi/ic_launcher.png +++ b/phonegap/Android/res/drawable-hdpi/ic_launcher.png diff --git a/phonegap/Android/res/drawable-ldpi/ic_launcher.png b/phonegap/Android/res/drawable-ldpi/ic_launcher.png Binary files differindex 1095584ec..2c2a58b2f 100644 --- a/phonegap/Android/res/drawable-ldpi/ic_launcher.png +++ b/phonegap/Android/res/drawable-ldpi/ic_launcher.png diff --git a/phonegap/Android/res/drawable-mdpi/ic_action_search.png b/phonegap/Android/res/drawable-mdpi/ic_action_search.png Binary files differnew file mode 100644 index 000000000..134d5490b --- /dev/null +++ b/phonegap/Android/res/drawable-mdpi/ic_action_search.png diff --git a/phonegap/Android/res/drawable-mdpi/ic_launcher.png b/phonegap/Android/res/drawable-mdpi/ic_launcher.png Binary files differindex a07c69fa5..f91f736fe 100644 --- a/phonegap/Android/res/drawable-mdpi/ic_launcher.png +++ b/phonegap/Android/res/drawable-mdpi/ic_launcher.png diff --git a/phonegap/Android/res/drawable-xhdpi/ic_action_search.png b/phonegap/Android/res/drawable-xhdpi/ic_action_search.png Binary files differnew file mode 100644 index 000000000..d699c6b37 --- /dev/null +++ b/phonegap/Android/res/drawable-xhdpi/ic_action_search.png diff --git a/phonegap/Android/res/drawable-xhdpi/ic_launcher.png b/phonegap/Android/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..96095ec84 --- /dev/null +++ b/phonegap/Android/res/drawable-xhdpi/ic_launcher.png diff --git a/phonegap/Android/res/layout/activity_main.xml b/phonegap/Android/res/layout/activity_main.xml new file mode 100644 index 000000000..0b4ffccc1 --- /dev/null +++ b/phonegap/Android/res/layout/activity_main.xml @@ -0,0 +1,14 @@ +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:text="@string/hello" + tools:context=".MainActivity" /> + +</RelativeLayout> diff --git a/phonegap/Android/res/layout/main.xml b/phonegap/Android/res/layout/main.xml deleted file mode 100644 index bc12cd823..000000000 --- a/phonegap/Android/res/layout/main.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:orientation="vertical" > - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="@string/hello" /> - -</LinearLayout>
\ No newline at end of file diff --git a/phonegap/Android/res/menu/activity_main.xml b/phonegap/Android/res/menu/activity_main.xml new file mode 100644 index 000000000..cfc10fd52 --- /dev/null +++ b/phonegap/Android/res/menu/activity_main.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/menu_settings" + android:title="@string/menu_settings" + android:orderInCategory="100" + android:showAsAction="never" /> +</menu> diff --git a/phonegap/Android/res/values-v11/styles.xml b/phonegap/Android/res/values-v11/styles.xml new file mode 100644 index 000000000..d408cbc37 --- /dev/null +++ b/phonegap/Android/res/values-v11/styles.xml @@ -0,0 +1,5 @@ +<resources> + + <style name="AppTheme" parent="android:Theme.Holo.Light" /> + +</resources>
\ No newline at end of file diff --git a/phonegap/Android/res/values-v14/styles.xml b/phonegap/Android/res/values-v14/styles.xml new file mode 100644 index 000000000..1c089a788 --- /dev/null +++ b/phonegap/Android/res/values-v14/styles.xml @@ -0,0 +1,5 @@ +<resources> + + <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" /> + +</resources>
\ No newline at end of file diff --git a/phonegap/Android/res/values/strings.xml b/phonegap/Android/res/values/strings.xml index a4df04a14..6e091cf98 100644 --- a/phonegap/Android/res/values/strings.xml +++ b/phonegap/Android/res/values/strings.xml @@ -3,5 +3,7 @@ <string name="hello">Hello World, AndroidActivity!</string> <string name="app_name">FixMyStreet</string> + <string name="menu_settings">Settings</string> + <string name="title_activity_main">AndroidActivity</string> </resources>
\ No newline at end of file diff --git a/phonegap/Android/res/xml/plugins.xml b/phonegap/Android/res/xml/config.xml index 76879a1ce..4a6fffccb 100644 --- a/phonegap/Android/res/xml/plugins.xml +++ b/phonegap/Android/res/xml/config.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- +<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information @@ -17,6 +17,21 @@ specific language governing permissions and limitations under the License. --> +<cordova> + <!-- + access elements control the Android whitelist. + Domains are assumed blocked unless set otherwise + --> + + <access origin="http://127.0.0.1*"/> <!-- allow local pages --> + + <!-- <access origin="https://example.com" /> allow any secure requests to example.com --> + <!-- <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www --> + <access origin=".*"/> + + <log level="DEBUG"/> + <preference name="useBrowserHistory" value="false" /> + <preference name="exit-on-suspend" value="false" /> <plugins> <plugin name="App" value="org.apache.cordova.App"/> <plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/> @@ -35,4 +50,7 @@ <plugin name="Capture" value="org.apache.cordova.Capture"/> <plugin name="Battery" value="org.apache.cordova.BatteryListener"/> <plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/> + <plugin name="Echo" value="org.apache.cordova.Echo" /> </plugins> +</cordova> + diff --git a/phonegap/Android/res/xml/cordova.xml b/phonegap/Android/res/xml/cordova.xml deleted file mode 100644 index c7b500003..000000000 --- a/phonegap/Android/res/xml/cordova.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<cordova> - <access origin="http://127.0.0.1*"/> - <log level="DEBUG"/> -</cordova> diff --git a/phonegap/android_cordova.js b/phonegap/android_cordova.js index 3e59432ed..9e7fa8ec2 100755 --- a/phonegap/android_cordova.js +++ b/phonegap/android_cordova.js @@ -1,6 +1,6 @@ -// commit ac0a3990438f4a89faa993316fb5614f61cf3be6 +// commit 143f5221a6251c9cbccdedc57005c61551b97f12 -// File generated at :: Tue Jun 05 2012 14:14:16 GMT-0700 (PDT) +// File generated at :: Wed Sep 12 2012 12:51:58 GMT-0700 (PDT) /* Licensed to the Apache Software Foundation (ASF) under one @@ -186,11 +186,19 @@ var cordova = { }, /** * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code */ - fireDocumentEvent: function(type, data) { + fireDocumentEvent: function(type, data, bNoDetach) { var evt = createEvent(type, data); if (typeof documentEventHandlers[type] != 'undefined') { - documentEventHandlers[type].fire(evt); + if( bNoDetach ) { + documentEventHandlers[type].fire(evt); + } + else { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } } else { document.dispatchEvent(evt); } @@ -198,15 +206,13 @@ var cordova = { fireWindowEvent: function(type, data) { var evt = createEvent(type,data); if (typeof windowEventHandlers[type] != 'undefined') { - windowEventHandlers[type].fire(evt); + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); } else { window.dispatchEvent(evt); } }, - // TODO: this is Android only; think about how to do this better - shuttingDown:false, - UsePolling:false, - // END TODO // TODO: iOS only // This queue holds the currently executing command and all pending @@ -285,17 +291,6 @@ var cordova = { } } }, - // TODO: remove in 2.0. - addPlugin: function(name, obj) { - console.log("[DEPRECATION NOTICE] window.addPlugin and window.plugins will be removed in version 2.0."); - if (!window.plugins[name]) { - window.plugins[name] = obj; - } - else { - console.log("Error: Plugin "+name+" already exists."); - } - }, - addConstructor: function(func) { channel.onCordovaReady.subscribeOnce(function() { try { @@ -312,51 +307,6 @@ channel.onPause = cordova.addDocumentEventHandler('pause'); channel.onResume = cordova.addDocumentEventHandler('resume'); channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); -// Adds deprecation warnings to functions of an object (but only logs a message once) -function deprecateFunctions(obj, objLabel) { - var newObj = {}; - var logHash = {}; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - if (typeof obj[i] == 'function') { - newObj[i] = (function(prop){ - var oldFunk = obj[prop]; - var funkId = objLabel + '_' + prop; - return function() { - if (!logHash[funkId]) { - console.log('[DEPRECATION NOTICE] The "' + objLabel + '" global will be removed in version 2.0, please use lowercase "cordova".'); - logHash[funkId] = true; - } - oldFunk.apply(obj, arguments); - }; - })(i); - } else { - newObj[i] = (function(prop) { return obj[prop]; })(i); - } - } - } - return newObj; -} - -/** - * Legacy variable for plugin support - * TODO: remove in 2.0. - */ -if (!window.PhoneGap) { - window.PhoneGap = deprecateFunctions(cordova, 'PhoneGap'); -} -if (!window.Cordova) { - window.Cordova = deprecateFunctions(cordova, 'Cordova'); -} - -/** - * Plugins object - * TODO: remove in 2.0. - */ -if (!window.plugins) { - window.plugins = {}; -} - module.exports = cordova; }); @@ -456,7 +406,8 @@ module.exports = { // file: lib/common/channel.js define("cordova/channel", function(require, exports, module) { -var utils = require('cordova/utils'); +var utils = require('cordova/utils'), + nextGuid = 1; /** * Custom pub-sub "channel" that can have functions subscribed to it @@ -508,7 +459,6 @@ var Channel = function(type, opts) { this.type = type; this.handlers = {}; this.numHandlers = 0; - this.guid = 1; this.fired = false; this.enabled = true; this.events = { @@ -601,19 +551,19 @@ Channel.prototype.subscribe = function(f, c, g) { g = g || func.observer_guid || f.observer_guid; if (!g) { - // first time we've seen this subscriber - g = this.guid++; - } - else { - // subscriber already handled; dont set it twice - return g; + // first time any channel has seen this subscriber + g = nextGuid++; } func.observer_guid = g; f.observer_guid = g; - this.handlers[g] = func; - this.numHandlers++; - if (this.events.onSubscribe) this.events.onSubscribe.call(this); - if (this.fired) func.call(this); + + // Don't add the same handler more than once. + if (!this.handlers[g]) { + this.handlers[g] = func; + this.numHandlers++; + if (this.events.onSubscribe) this.events.onSubscribe.call(this); + if (this.fired) func.apply(this, this.fireArgs); + } return g; }; @@ -627,15 +577,14 @@ Channel.prototype.subscribeOnce = function(f, c) { var g = null; var _this = this; - var m = function() { - f.apply(c || null, arguments); - _this.unsubscribe(g); - }; if (this.fired) { - if (typeof c == "object") { f = utils.close(c, f); } - f.apply(this, this.fireArgs); + f.apply(c || null, this.fireArgs); } else { - g = this.subscribe(m); + g = this.subscribe(function() { + _this.unsubscribe(g); + f.apply(c || null, arguments); + }); + f.observer_guid = g; } return g; }; @@ -651,7 +600,6 @@ Channel.prototype.unsubscribe = function(g) { var handler = this.handlers[g]; if (handler) { if (handler.observer_guid) handler.observer_guid=null; - this.handlers[g] = null; delete this.handlers[g]; this.numHandlers--; if (this.events.onUnsubscribe) this.events.onUnsubscribe.call(this); @@ -665,14 +613,17 @@ Channel.prototype.fire = function(e) { if (this.enabled) { var fail = false; this.fired = true; + this.fireArgs = arguments; + // Copy the values first so that it is safe to modify it from within + // callbacks. + var toCall = []; for (var item in this.handlers) { - var handler = this.handlers[item]; - if (typeof handler == 'function') { - var rv = (handler.apply(this, arguments)===false); - fail = fail || rv; - } + toCall.push(this.handlers[item]); + } + for (var i = 0; i < toCall.length; ++i) { + var rv = (toCall[i].apply(this, arguments)===false); + fail = fail || rv; } - this.fireArgs = arguments; return !fail; } return true; @@ -709,7 +660,6 @@ channel.create('onDestroy'); // Channels that must fire before "deviceready" is fired. channel.waitForInitialization('onCordovaReady'); -channel.waitForInitialization('onCordovaInfoReady'); channel.waitForInitialization('onCordovaConnectionReady'); module.exports = channel; @@ -738,13 +688,6 @@ module.exports = { } } }, - PhoneGap:{ - children: { - exec: { - path: 'cordova/exec' - } - } - }, navigator: { children: { notification: { @@ -844,6 +787,9 @@ module.exports = { Coordinates: { path: 'cordova/plugin/Coordinates' }, + device: { + path: 'cordova/plugin/device' + }, DirectoryEntry: { path: 'cordova/plugin/DirectoryEntry' }, @@ -940,76 +886,170 @@ define("cordova/exec", function(require, exports, module) { * @param {String} action Action to be run in cordova * @param {String[]} [args] Zero or more arguments to pass to the method */ -var cordova = require('cordova'); +var cordova = require('cordova'), + callback = require('cordova/plugin/android/callback'), + polling = require('cordova/plugin/android/polling'), + jsToNativeBridgeMode, + nativeToJsBridgeMode, + jsToNativeModes = { + PROMPT: 0, + JS_OBJECT: 1, + // This mode is currently for benchmarking purposes only. It must be enabled + // on the native side through the ENABLE_LOCATION_CHANGE_EXEC_MODE + // constant within CordovaWebViewClient.java before it will work. + LOCATION_CHANGE: 2 + }, + nativeToJsModes = { + // Polls for messages using the prompt() bridge. + POLLING: 0, + // Does an XHR to a local server, which will send back messages. This is + // broken on ICS when a proxy server is configured. + HANGING_GET: 1, + // For LOAD_URL to be viable, it would need to have a work-around for + // the bug where the soft-keyboard gets dismissed when a message is sent. + LOAD_URL: 2, + // For the ONLINE_EVENT to be viable, it would need to intercept all event + // listeners (both through addEventListener and window.ononline) as well + // as set the navigator property itself. + ONLINE_EVENT: 3, + // Uses reflection to access private APIs of the WebView that can send JS + // to be executed. + // Requires Android 3.2.4 or above. + PRIVATE_API: 4 + }; -module.exports = function(success, fail, service, action, args) { - try { - var callbackId = service + cordova.callbackId++; - if (success || fail) { - cordova.callbacks[callbackId] = {success:success, fail:fail}; +function androidExec(success, fail, service, action, args) { + // Set default bridge modes if they have not already been set. + if (jsToNativeBridgeMode === undefined) { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); + } + if (nativeToJsBridgeMode === undefined) { + if (callback.isAvailable()) { + androidExec.setNativeToJsBridgeMode(nativeToJsModes.HANGING_GET); + } else { + androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING); + } } + try { + var callbackId = service + cordova.callbackId++, + argsJson = JSON.stringify(args), + result; + if (success || fail) { + cordova.callbacks[callbackId] = {success:success, fail:fail}; + } - var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, action, callbackId, true])); + if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) { + window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson; + } else if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT) { + // Explicit cast to string is required on Android 2.1 to convert from + // a Java string to a JS string. + result = '' + _cordovaExec.exec(service, action, callbackId, argsJson); + } else { + result = prompt(argsJson, "gap:"+JSON.stringify([service, action, callbackId, true])); + } - // If a result was returned - if (r.length > 0) { - var v; - eval("v="+r+";"); + // If a result was returned + if (result) { + var v = JSON.parse(result); + + // 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); + } - // If status is OK, then return value back to caller - if (v.status === cordova.callbackStatus.OK) { + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + return v.message; + } - // 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); - } + // 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]; + } + } - // Clear callback if not expecting any more results - if (!v.keepCallback) { - delete cordova.callbacks[callbackId]; - } - } - return v.message; - } + // If error, then display error + else { + console.log("Error: Status="+v.status+" Message="+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 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); + } - // If error, then display error - else { - console.log("Error: Status="+v.status+" Message="+v.message); + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete cordova.callbacks[callbackId]; + } + } + return null; + } + } + } catch (e2) { + console.log("Error: "+e2); + } +} - // 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); - } +function onOnLineEvent(e) { + while (polling.pollOnce()); +} - // Clear callback if not expecting any more results - if (!v.keepCallback) { - delete cordova.callbacks[callbackId]; - } - } - return null; - } +androidExec.jsToNativeModes = jsToNativeModes; +androidExec.nativeToJsModes = nativeToJsModes; + +androidExec.setJsToNativeBridgeMode = function(mode) { + if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaExec) { + console.log('Falling back on PROMPT mode since _cordovaExec is missing.'); + mode = jsToNativeModes.PROMPT; } - } catch (e2) { - console.log("Error: "+e2); - } + jsToNativeBridgeMode = mode; }; +androidExec.setNativeToJsBridgeMode = function(mode) { + if (mode == nativeToJsBridgeMode) { + return; + } + if (nativeToJsBridgeMode == nativeToJsModes.POLLING) { + polling.stop(); + } else if (nativeToJsBridgeMode == nativeToJsModes.HANGING_GET) { + callback.stop(); + } else if (nativeToJsBridgeMode == nativeToJsModes.ONLINE_EVENT) { + window.removeEventListener('online', onOnLineEvent, false); + window.removeEventListener('offline', onOnLineEvent, false); + } + + nativeToJsBridgeMode = mode; + // Tell the native side to switch modes. + prompt(mode, "gap_bridge_mode:"); + + if (mode == nativeToJsModes.POLLING) { + polling.start(); + } else if (mode == nativeToJsModes.HANGING_GET) { + callback.start(); + } else if (mode == nativeToJsModes.ONLINE_EVENT) { + window.addEventListener('online', onOnLineEvent, false); + window.addEventListener('offline', onOnLineEvent, false); + } +}; + +module.exports = androidExec; + }); // file: lib/android/platform.js @@ -1019,34 +1059,8 @@ module.exports = { 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() { @@ -1111,7 +1125,7 @@ module.exports = { // 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:"); + exec(null, null, "App", "show", []); }, [channel.onCordovaReady]); }, objects: { @@ -1132,9 +1146,6 @@ module.exports = { } } }, - device:{ - path: "cordova/plugin/android/device" - }, File: { // exists natively on Android WebView, override path: "cordova/plugin/File" }, @@ -1149,6 +1160,9 @@ module.exports = { } }, merges: { + device: { + path: 'cordova/plugin/android/device' + }, navigator: { children: { notification: { @@ -1281,7 +1295,14 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { popoverOptions = options.popoverOptions; } - exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]); + var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]; + + exec(successCallback, errorCallback, "Camera", "takePicture", args); +}; + +cameraExport.cleanup = function(successCallback, errorCallback) { + exec(successCallback, errorCallback, "Camera", "cleanup", []); }; module.exports = cameraExport; @@ -1866,7 +1887,7 @@ var utils = require('cordova/utils'), * {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) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) */ var DirectoryEntry = function(name, fullPath) { DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); @@ -2596,11 +2617,13 @@ var FileSystem = function(name, root) { }; module.exports = FileSystem; + }); // file: lib/common/plugin/FileTransfer.js define("cordova/plugin/FileTransfer", function(require, exports, module) { -var exec = require('cordova/exec'); +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); /** * FileTransfer uploads a file to a remote server. @@ -2619,16 +2642,20 @@ var FileTransfer = function() {}; * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false */ FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); // check for options var fileKey = null; var fileName = null; var mimeType = null; var params = null; var chunkedMode = true; + var headers = null; if (options) { fileKey = options.fileKey; fileName = options.fileName; mimeType = options.mimeType; + headers = options.headers; if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { chunkedMode = options.chunkedMode; } @@ -2640,7 +2667,12 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro } } - exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]); }; /** @@ -2651,6 +2683,8 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro * @param errorCallback {Function} Callback to be invoked upon error */ FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); var win = function(result) { var entry = null; if (result.isDirectory) { @@ -2665,6 +2699,12 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro entry.fullPath = result.fullPath; successCallback(entry); }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); }; @@ -2678,8 +2718,11 @@ define("cordova/plugin/FileTransferError", function(require, exports, module) { * FileTransferError * @constructor */ -var FileTransferError = function(code) { +var FileTransferError = function(code, source, target, status) { this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; }; FileTransferError.FILE_NOT_FOUND_ERR = 1; @@ -2687,6 +2730,7 @@ FileTransferError.INVALID_URL_ERR = 2; FileTransferError.CONNECTION_ERR = 3; module.exports = FileTransferError; + }); // file: lib/common/plugin/FileUploadOptions.js @@ -2698,15 +2742,19 @@ define("cordova/plugin/FileUploadOptions", function(require, exports, module) { * @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. + * @param headers {Object} Keys are header names, values are header values. Multiple + * headers of the same name are not supported. */ -var FileUploadOptions = function(fileKey, fileName, mimeType, params) { +var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) { this.fileKey = fileKey || null; this.fileName = fileName || null; this.mimeType = mimeType || null; this.params = params || null; + this.headers = headers || null; }; module.exports = FileUploadOptions; + }); // file: lib/common/plugin/FileUploadResult.js @@ -2894,7 +2942,7 @@ FileWriter.prototype.seek = function(offset) { if (offset < 0) { this.position = Math.max(offset + this.length, 0); } - // Offset is bigger then file size so set position + // Offset is bigger than file size so set position // to the end of the file. else if (offset > this.length) { this.position = this.length; @@ -3101,7 +3149,6 @@ Media.prototype.stop = function() { var me = this; exec(function() { me._position = 0; - me.successCallback(); }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); }; @@ -3147,14 +3194,14 @@ Media.prototype.getCurrentPosition = function(success, fail) { * Start recording audio file. */ Media.prototype.startRecord = function() { - exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); + exec(null, 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]); + exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); }; /** @@ -3176,34 +3223,39 @@ Media.prototype.setVolume = function(volume) { * PRIVATE * * @param id The media object id (string) - * @param status The status code (int) - * @param msg The status message (string) + * @param msgType The 'type' of update this is + * @param value Use of value is determined by the msgType */ -Media.onStatus = function(id, msg, value) { +Media.onStatus = function(id, msgType, value) { + var media = mediaObjects[id]; - // If state update - if (msg === Media.MEDIA_STATE) { - if (value === Media.MEDIA_STOPPED) { - if (media.successCallback) { - media.successCallback(); - } - } - if (media.statusCallback) { - media.statusCallback(value); - } - } - else if (msg === Media.MEDIA_DURATION) { - media._duration = value; - } - else if (msg === Media.MEDIA_ERROR) { - if (media.errorCallback) { - // value should be a MediaError object when msg == MEDIA_ERROR - media.errorCallback(value); + + if(media) { + switch(msgType) { + case Media.MEDIA_STATE : + media.statusCallback && media.statusCallback(value); + if(value == Media.MEDIA_STOPPED) { + media.successCallback && media.successCallback(); + } + break; + case Media.MEDIA_DURATION : + media._duration = value; + break; + case Media.MEDIA_ERROR : + media.errorCallback && media.errorCallback(value); + break; + case Media.MEDIA_POSITION : + media._position = Number(value); + break; + default : + console && console.error && console.error("Unhandled Media.onStatus :: " + msgType); + break; } } - else if (msg === Media.MEDIA_POSITION) { - media._position = value; + else { + console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id); } + }; module.exports = Media; @@ -3213,20 +3265,36 @@ module.exports = Media; define("cordova/plugin/MediaError", function(require, exports, module) { /** * This class contains information about any Media errors. - * @constructor +*/ +/* + According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror + We should never be creating these objects, we should just implement the interface + which has 1 property for an instance, 'code' + + instead of doing : + errorCallbackFunction( new MediaError(3,'msg') ); +we should simply use a literal : + errorCallbackFunction( {'code':3} ); */ -var MediaError = function(code, msg) { - this.code = (code !== undefined ? code : null); - this.message = msg || ""; -}; -MediaError.MEDIA_ERR_NONE_ACTIVE = 0; -MediaError.MEDIA_ERR_ABORTED = 1; -MediaError.MEDIA_ERR_NETWORK = 2; -MediaError.MEDIA_ERR_DECODE = 3; -MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; +if(!MediaError) { + var MediaError = function(code, msg) { + this.code = (typeof code != 'undefined') ? code : null; + this.message = msg || ""; // message is NON-standard! do not use! + }; +} + +MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0; +MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1; +MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2; +MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4; +// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below. +// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes +MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4; module.exports = MediaError; + }); // file: lib/common/plugin/MediaFile.js @@ -3264,28 +3332,6 @@ MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { } }; -// TODO: can we axe this? -/** - * 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 = []; - for (var i=0; i<pluginResult.message.length; i++) { - var mediaFile = new MediaFile(); - mediaFile.name = pluginResult.message[i].name; - mediaFile.fullPath = pluginResult.message[i].fullPath; - mediaFile.type = pluginResult.message[i].type; - mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate; - mediaFile.size = pluginResult.message[i].size; - mediaFiles.push(mediaFile); - } - pluginResult.message = mediaFiles; - return pluginResult; -}; - module.exports = MediaFile; }); @@ -3491,12 +3537,12 @@ var accelerometer = { var p; var win = function(a) { - successCallback(a); removeListeners(p); + successCallback(a); }; var fail = function(e) { - errorCallback(e); removeListeners(p); + errorCallback(e); }; p = createCallbackPair(win, fail); @@ -3528,8 +3574,8 @@ var accelerometer = { var id = utils.createUUID(); var p = createCallbackPair(function(){}, function(e) { - errorCallback(e); removeListeners(p); + errorCallback(e); }); listeners.push(p); @@ -3544,7 +3590,10 @@ var accelerometer = { if (running) { // If we're already running then immediately invoke the success callback - successCallback(accel); + // but only if we have retrieved a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } } else { start(); } @@ -3650,186 +3699,126 @@ module.exports = { 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; - } + xmlhttp; - var xmlhttp = new XMLHttpRequest(); +function startXhr() { + // cordova/exec depends on this module, so we can't require cordova/exec on the module level. + var exec = require('cordova/exec'), + 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; - } + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if (!xmlhttp) { + return; + } + if (xmlhttp.readyState === 4){ + // If callback has JavaScript statement to execute + if (xmlhttp.status === 200) { - // 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); - } + // 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(startXhr, 1); + } - // If callback ping (used to keep XHR request from timing out) - else if (xmlhttp.status === 404) { - setTimeout(callback, 10); - } + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(startXhr, 10); + } - // If security error - else if (xmlhttp.status === 403) { - console.log("JSCallback Error: Invalid token. Stopping callbacks."); - } + // 0 == Page is unloading. + // 400 == Bad request. + // 403 == invalid token. + // 503 == server stopped. + else { + console.log("JSCallback Error: Request failed with status " + xmlhttp.status); + exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING); + } + } + }; - // If server is stopping - else if (xmlhttp.status === 503) { - console.log("JSCallback Server Closed: Stopping callbacks."); - } + 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(); +} - // If request wasn't GET - else if (xmlhttp.status === 400) { - console.log("JSCallback Error: Bad request. Stopping callbacks."); - } +module.exports = { + start: function() { + startXhr(); + }, - // If error, revert to polling - else { - console.log("JSCallback Error: Request failed."); - cordova.UsePolling = true; - polling(); - } - } - }; + stop: function() { + if (xmlhttp) { + var tmp = xmlhttp; + xmlhttp = null; + tmp.abort(); + } + }, - 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(); + isAvailable: function() { + return ("true" != prompt("usePolling", "gap_callbackServer:")); + } }; -module.exports = callback; + }); // file: lib/android/plugin/android/device.js define("cordova/plugin/android/device", function(require, exports, module) { var channel = require('cordova/channel'), utils = require('cordova/utils'), - exec = require('cordova/exec'); - -/** - * This represents the mobile device, and provides properties for inspecting the model, version, UUID of the - * 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; - - channel.onCordovaReady.subscribeOnce(function() { - me.getInfo(function(info) { - me.available = true; - me.platform = info.platform; - me.version = info.version; - me.name = info.name; - me.uuid = info.uuid; - me.cordova = info.cordova; - channel.onCordovaInfoReady.fire(); - },function(e) { - me.available = false; - utils.alert("[ERROR] Error initializing Cordova: " + e); - }); - }); -} + exec = require('cordova/exec'), + app = require('cordova/plugin/android/app'); -/** - * 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) { +module.exports = { + /* + * DEPRECATED + * This is only for Android. + * + * You must explicitly override the back button. + */ + overrideBackButton:function() { + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); + }, - // successCallback required - if (typeof successCallback !== "function") { - console.log("Device Error: successCallback is not a function"); - return; - } + /* + * DEPRECATED + * This is only for Android. + * + * This resets the back button to the default behaviour + */ + resetBackButton:function() { + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); + }, - // errorCallback optional - if (errorCallback && (typeof errorCallback !== "function")) { - console.log("Device Error: errorCallback is not a function"); - return; + /* + * DEPRECATED + * This is only for Android. + * + * This terminates the activity! + */ + exitApp:function() { + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); } - - // 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(); }); // file: lib/android/plugin/android/notification.js @@ -3892,38 +3881,47 @@ module.exports = { // file: lib/android/plugin/android/polling.js 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; - } + POLL_INTERVAL = 50, + enabled = false; - // 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; - } +function pollOnce() { + var msg = prompt("", "gap_poll:"); + if (msg) { + try { + eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + return true; + } + return false; +} - 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); - } +function doPoll() { + if (!enabled) { + return; + } + var nextDelay = POLL_INTERVAL; + if (pollOnce()) { + nextDelay = 0; + } + setTimeout(doPoll, nextDelay); +} + +module.exports = { + start: function() { + enabled = true; + setTimeout(doPoll, 1); + }, + stop: function() { + enabled = false; + }, + pollOnce: pollOnce }; -module.exports = polling; + }); // file: lib/android/plugin/android/storage.js @@ -4796,7 +4794,7 @@ var contacts = { * 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 + * @param properties an object whose properties will be examined to create a new Contact * @returns new Contact object */ create:function(properties) { @@ -4815,6 +4813,93 @@ module.exports = contacts; }); +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * 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; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/echo.js +define("cordova/plugin/echo", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback. + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + * @param message The string to be echoed. + * @param forceAsync Whether to force an async return value (for testing native->js bridge). + */ +module.exports = function(successCallback, errorCallback, message, forceAsync) { + var action = forceAsync ? 'echoAsync' : 'echo'; + exec(successCallback, errorCallback, "Echo", action, [message]); +}; + + +}); + // file: lib/common/plugin/geolocation.js define("cordova/plugin/geolocation", function(require, exports, module) { var utils = require('cordova/utils'), @@ -4923,7 +5008,7 @@ var geolocation = { } else if (options.timeout === 0) { fail({ code:PositionError.TIMEOUT, - message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter." }); // Otherwise we have to call into native to retrieve a position. } else { @@ -5087,7 +5172,7 @@ CurrentLevel = LevelsMap.WARN; * * The value used determines which messages get printed. The logging * values above are in order, and only messages logged at the logging - * level or above will actually be displayed to the user. Eg, the + * level or above will actually be displayed to the user. E.g., the * default level is WARN, so only messages logged with LOG, ERROR, or * WARN will be displayed; INFO and DEBUG messages will be ignored. */ @@ -5466,6 +5551,9 @@ define("cordova/plugin/splashscreen", function(require, exports, module) { var exec = require('cordova/exec'); var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, hide:function() { exec(null, null, "SplashScreen", "hide", []); } @@ -5723,4 +5811,4 @@ window.cordova = require('cordova'); }(window)); -})();
\ No newline at end of file +})();var PhoneGap = cordova; diff --git a/phonegap/www/about.html b/phonegap/www/about.html index 03d2d389a..4d5194011 100644 --- a/phonegap/www/about.html +++ b/phonegap/www/about.html @@ -9,7 +9,7 @@ <link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="css/mobile.css"> <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> <script type="text/javascript" src="js/json2.js"></script> <script type="text/javascript" src="js/mobile.js"></script> diff --git a/phonegap/www/around.html b/phonegap/www/around.html index f3a6bee06..d98bbc962 100644 --- a/phonegap/www/around.html +++ b/phonegap/www/around.html @@ -14,7 +14,7 @@ <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript" src="js/json2.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> diff --git a/phonegap/www/cordova-1.8.0.js b/phonegap/www/cordova-2.1.0.js index d9cced70f..9e7fa8ec2 100644 --- a/phonegap/www/cordova-1.8.0.js +++ b/phonegap/www/cordova-2.1.0.js @@ -1,6 +1,6 @@ -// commit 109b8649b0e98597b147842a6f71999d2f7910f2 +// commit 143f5221a6251c9cbccdedc57005c61551b97f12 -// File generated at :: Tue Jun 05 2012 14:10:19 GMT-0700 (PDT) +// File generated at :: Wed Sep 12 2012 12:51:58 GMT-0700 (PDT) /* Licensed to the Apache Software Foundation (ASF) under one @@ -186,11 +186,19 @@ var cordova = { }, /** * Method to fire event from native code + * bNoDetach is required for events which cause an exception which needs to be caught in native code */ - fireDocumentEvent: function(type, data) { + fireDocumentEvent: function(type, data, bNoDetach) { var evt = createEvent(type, data); if (typeof documentEventHandlers[type] != 'undefined') { - documentEventHandlers[type].fire(evt); + if( bNoDetach ) { + documentEventHandlers[type].fire(evt); + } + else { + setTimeout(function() { + documentEventHandlers[type].fire(evt); + }, 0); + } } else { document.dispatchEvent(evt); } @@ -198,15 +206,13 @@ var cordova = { fireWindowEvent: function(type, data) { var evt = createEvent(type,data); if (typeof windowEventHandlers[type] != 'undefined') { - windowEventHandlers[type].fire(evt); + setTimeout(function() { + windowEventHandlers[type].fire(evt); + }, 0); } else { window.dispatchEvent(evt); } }, - // TODO: this is Android only; think about how to do this better - shuttingDown:false, - UsePolling:false, - // END TODO // TODO: iOS only // This queue holds the currently executing command and all pending @@ -285,17 +291,6 @@ var cordova = { } } }, - // TODO: remove in 2.0. - addPlugin: function(name, obj) { - console.log("[DEPRECATION NOTICE] window.addPlugin and window.plugins will be removed in version 2.0."); - if (!window.plugins[name]) { - window.plugins[name] = obj; - } - else { - console.log("Error: Plugin "+name+" already exists."); - } - }, - addConstructor: function(func) { channel.onCordovaReady.subscribeOnce(function() { try { @@ -312,51 +307,6 @@ channel.onPause = cordova.addDocumentEventHandler('pause'); channel.onResume = cordova.addDocumentEventHandler('resume'); channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready'); -// Adds deprecation warnings to functions of an object (but only logs a message once) -function deprecateFunctions(obj, objLabel) { - var newObj = {}; - var logHash = {}; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - if (typeof obj[i] == 'function') { - newObj[i] = (function(prop){ - var oldFunk = obj[prop]; - var funkId = objLabel + '_' + prop; - return function() { - if (!logHash[funkId]) { - console.log('[DEPRECATION NOTICE] The "' + objLabel + '" global will be removed in version 2.0, please use lowercase "cordova".'); - logHash[funkId] = true; - } - oldFunk.apply(obj, arguments); - }; - })(i); - } else { - newObj[i] = (function(prop) { return obj[prop]; })(i); - } - } - } - return newObj; -} - -/** - * Legacy variable for plugin support - * TODO: remove in 2.0. - */ -if (!window.PhoneGap) { - window.PhoneGap = deprecateFunctions(cordova, 'PhoneGap'); -} -if (!window.Cordova) { - window.Cordova = deprecateFunctions(cordova, 'Cordova'); -} - -/** - * Plugins object - * TODO: remove in 2.0. - */ -if (!window.plugins) { - window.plugins = {}; -} - module.exports = cordova; }); @@ -456,7 +406,8 @@ module.exports = { // file: lib/common/channel.js define("cordova/channel", function(require, exports, module) { -var utils = require('cordova/utils'); +var utils = require('cordova/utils'), + nextGuid = 1; /** * Custom pub-sub "channel" that can have functions subscribed to it @@ -508,7 +459,6 @@ var Channel = function(type, opts) { this.type = type; this.handlers = {}; this.numHandlers = 0; - this.guid = 1; this.fired = false; this.enabled = true; this.events = { @@ -601,19 +551,19 @@ Channel.prototype.subscribe = function(f, c, g) { g = g || func.observer_guid || f.observer_guid; if (!g) { - // first time we've seen this subscriber - g = this.guid++; - } - else { - // subscriber already handled; dont set it twice - return g; + // first time any channel has seen this subscriber + g = nextGuid++; } func.observer_guid = g; f.observer_guid = g; - this.handlers[g] = func; - this.numHandlers++; - if (this.events.onSubscribe) this.events.onSubscribe.call(this); - if (this.fired) func.call(this); + + // Don't add the same handler more than once. + if (!this.handlers[g]) { + this.handlers[g] = func; + this.numHandlers++; + if (this.events.onSubscribe) this.events.onSubscribe.call(this); + if (this.fired) func.apply(this, this.fireArgs); + } return g; }; @@ -627,15 +577,14 @@ Channel.prototype.subscribeOnce = function(f, c) { var g = null; var _this = this; - var m = function() { - f.apply(c || null, arguments); - _this.unsubscribe(g); - }; if (this.fired) { - if (typeof c == "object") { f = utils.close(c, f); } - f.apply(this, this.fireArgs); + f.apply(c || null, this.fireArgs); } else { - g = this.subscribe(m); + g = this.subscribe(function() { + _this.unsubscribe(g); + f.apply(c || null, arguments); + }); + f.observer_guid = g; } return g; }; @@ -651,7 +600,6 @@ Channel.prototype.unsubscribe = function(g) { var handler = this.handlers[g]; if (handler) { if (handler.observer_guid) handler.observer_guid=null; - this.handlers[g] = null; delete this.handlers[g]; this.numHandlers--; if (this.events.onUnsubscribe) this.events.onUnsubscribe.call(this); @@ -665,14 +613,17 @@ Channel.prototype.fire = function(e) { if (this.enabled) { var fail = false; this.fired = true; + this.fireArgs = arguments; + // Copy the values first so that it is safe to modify it from within + // callbacks. + var toCall = []; for (var item in this.handlers) { - var handler = this.handlers[item]; - if (typeof handler == 'function') { - var rv = (handler.apply(this, arguments)===false); - fail = fail || rv; - } + toCall.push(this.handlers[item]); + } + for (var i = 0; i < toCall.length; ++i) { + var rv = (toCall[i].apply(this, arguments)===false); + fail = fail || rv; } - this.fireArgs = arguments; return !fail; } return true; @@ -709,7 +660,6 @@ channel.create('onDestroy'); // Channels that must fire before "deviceready" is fired. channel.waitForInitialization('onCordovaReady'); -channel.waitForInitialization('onCordovaInfoReady'); channel.waitForInitialization('onCordovaConnectionReady'); module.exports = channel; @@ -738,13 +688,6 @@ module.exports = { } } }, - PhoneGap:{ - children: { - exec: { - path: 'cordova/exec' - } - } - }, navigator: { children: { notification: { @@ -844,6 +787,9 @@ module.exports = { Coordinates: { path: 'cordova/plugin/Coordinates' }, + device: { + path: 'cordova/plugin/device' + }, DirectoryEntry: { path: 'cordova/plugin/DirectoryEntry' }, @@ -924,159 +870,309 @@ module.exports = { }); -// file: lib/ios/exec.js +// file: lib/android/exec.js define("cordova/exec", function(require, exports, module) { - /** - * Creates a gap bridge iframe used to notify the native code about queued - * commands. - * - * @private - */ +/** + * Execute a cordova command. It is up to the native side whether this action + * is synchronous or asynchronous. The native side can return: + * Synchronous: PluginResult object as a JSON string + * Asynchrounous: Empty string "" + * If async, the native side will cordova.callbackSuccess or cordova.callbackError, + * depending upon the result of the action. + * + * @param {Function} success The success callback + * @param {Function} fail The fail callback + * @param {String} service The name of the service to use + * @param {String} action Action to be run in cordova + * @param {String[]} [args] Zero or more arguments to pass to the method + */ var cordova = require('cordova'), - utils = require('cordova/utils'), - gapBridge, - createGapBridge = function() { - gapBridge = document.createElement("iframe"); - gapBridge.setAttribute("style", "display:none;"); - gapBridge.setAttribute("height","0px"); - gapBridge.setAttribute("width","0px"); - gapBridge.setAttribute("frameborder","0"); - document.documentElement.appendChild(gapBridge); + callback = require('cordova/plugin/android/callback'), + polling = require('cordova/plugin/android/polling'), + jsToNativeBridgeMode, + nativeToJsBridgeMode, + jsToNativeModes = { + PROMPT: 0, + JS_OBJECT: 1, + // This mode is currently for benchmarking purposes only. It must be enabled + // on the native side through the ENABLE_LOCATION_CHANGE_EXEC_MODE + // constant within CordovaWebViewClient.java before it will work. + LOCATION_CHANGE: 2 }, - channel = require('cordova/channel'); + nativeToJsModes = { + // Polls for messages using the prompt() bridge. + POLLING: 0, + // Does an XHR to a local server, which will send back messages. This is + // broken on ICS when a proxy server is configured. + HANGING_GET: 1, + // For LOAD_URL to be viable, it would need to have a work-around for + // the bug where the soft-keyboard gets dismissed when a message is sent. + LOAD_URL: 2, + // For the ONLINE_EVENT to be viable, it would need to intercept all event + // listeners (both through addEventListener and window.ononline) as well + // as set the navigator property itself. + ONLINE_EVENT: 3, + // Uses reflection to access private APIs of the WebView that can send JS + // to be executed. + // Requires Android 3.2.4 or above. + PRIVATE_API: 4 + }; -module.exports = function() { - if (!channel.onCordovaInfoReady.fired) { - utils.alert("ERROR: Attempting to call cordova.exec()" + - " before 'deviceready'. Ignoring."); - return; +function androidExec(success, fail, service, action, args) { + // Set default bridge modes if they have not already been set. + if (jsToNativeBridgeMode === undefined) { + androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); + } + if (nativeToJsBridgeMode === undefined) { + if (callback.isAvailable()) { + androidExec.setNativeToJsBridgeMode(nativeToJsModes.HANGING_GET); + } else { + androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING); + } } + try { + var callbackId = service + cordova.callbackId++, + argsJson = JSON.stringify(args), + result; + if (success || fail) { + cordova.callbacks[callbackId] = {success:success, fail:fail}; + } - var successCallback, failCallback, service, action, actionArgs, splitCommand; - var callbackId = null; - if (typeof arguments[0] !== "string") { - // FORMAT ONE - successCallback = arguments[0]; - failCallback = arguments[1]; - service = arguments[2]; - action = arguments[3]; - actionArgs = arguments[4]; - - // Since we need to maintain backwards compatibility, we have to pass - // an invalid callbackId even if no callback was provided since plugins - // will be expecting it. The Cordova.exec() implementation allocates - // an invalid callbackId and passes it even if no callbacks were given. - callbackId = 'INVALID'; - } else { - // FORMAT TWO - splitCommand = arguments[0].split("."); - action = splitCommand.pop(); - service = splitCommand.join("."); - actionArgs = Array.prototype.splice.call(arguments, 1); - } - - // Start building the command object. - var command = { - className: service, - methodName: action, - "arguments": [] - }; + if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) { + window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson; + } else if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT) { + // Explicit cast to string is required on Android 2.1 to convert from + // a Java string to a JS string. + result = '' + _cordovaExec.exec(service, action, callbackId, argsJson); + } else { + result = prompt(argsJson, "gap:"+JSON.stringify([service, action, callbackId, true])); + } + + // If a result was returned + if (result) { + var v = JSON.parse(result); + + // 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]; + } + } - // Register the callbacks and add the callbackId to the positional - // arguments if given. - if (successCallback || failCallback) { - callbackId = service + cordova.callbackId++; - cordova.callbacks[callbackId] = - {success:successCallback, fail:failCallback}; + // 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); } - if (callbackId !== null) { - command["arguments"].push(callbackId); +} + +function onOnLineEvent(e) { + while (polling.pollOnce()); +} + +androidExec.jsToNativeModes = jsToNativeModes; +androidExec.nativeToJsModes = nativeToJsModes; + +androidExec.setJsToNativeBridgeMode = function(mode) { + if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaExec) { + console.log('Falling back on PROMPT mode since _cordovaExec is missing.'); + mode = jsToNativeModes.PROMPT; } + jsToNativeBridgeMode = mode; +}; - for (var i = 0; i < actionArgs.length; ++i) { - var arg = actionArgs[i]; - if (arg === undefined || arg === null) { // nulls are pushed to the args now (becomes NSNull) - command["arguments"].push(arg); - } else if (typeof(arg) == 'object' && !(utils.isArray(arg))) { - command.options = arg; - } else { - command["arguments"].push(arg); - } +androidExec.setNativeToJsBridgeMode = function(mode) { + if (mode == nativeToJsBridgeMode) { + return; + } + if (nativeToJsBridgeMode == nativeToJsModes.POLLING) { + polling.stop(); + } else if (nativeToJsBridgeMode == nativeToJsModes.HANGING_GET) { + callback.stop(); + } else if (nativeToJsBridgeMode == nativeToJsModes.ONLINE_EVENT) { + window.removeEventListener('online', onOnLineEvent, false); + window.removeEventListener('offline', onOnLineEvent, false); } - // Stringify and queue the command. We stringify to command now to - // effectively clone the command arguments in case they are mutated before - // the command is executed. - cordova.commandQueue.push(JSON.stringify(command)); + nativeToJsBridgeMode = mode; + // Tell the native side to switch modes. + prompt(mode, "gap_bridge_mode:"); - // If the queue length is 1, then that means it was empty before we queued - // the given command, so let the native side know that we have some - // commands to execute, unless the queue is currently being flushed, in - // which case the command will be picked up without notification. - if (cordova.commandQueue.length == 1 && !cordova.commandQueueFlushing) { - if (!gapBridge) { - createGapBridge(); - } - gapBridge.src = "gap://ready"; + if (mode == nativeToJsModes.POLLING) { + polling.start(); + } else if (mode == nativeToJsModes.HANGING_GET) { + callback.start(); + } else if (mode == nativeToJsModes.ONLINE_EVENT) { + window.addEventListener('online', onOnLineEvent, false); + window.addEventListener('offline', onOnLineEvent, false); } }; +module.exports = androidExec; + }); -// file: lib/ios/platform.js +// file: lib/android/platform.js define("cordova/platform", function(require, exports, module) { module.exports = { - id: "ios", + id: "android", initialize:function() { - // iOS doesn't allow reassigning / overriding navigator.geolocation object. - // So clobber its methods here instead :) - var geo = require('cordova/plugin/geolocation'); + var channel = require("cordova/channel"), + cordova = require('cordova'), + exec = require('cordova/exec'); + + // Inject a listener for the backbutton on the document. + var backButtonChannel = cordova.addDocumentEventHandler('backbutton', { + onSubscribe:function() { + // If we just attached the first handler, let native know we need to override the back button. + if (this.numHandlers === 1) { + exec(null, null, "App", "overrideBackbutton", [true]); + } + }, + onUnsubscribe:function() { + // If we just detached the last handler, let native know we no longer override the back button. + if (this.numHandlers === 0) { + exec(null, null, "App", "overrideBackbutton", [false]); + } + } + }); + + // Add hardware MENU and SEARCH button handlers + cordova.addDocumentEventHandler('menubutton'); + cordova.addDocumentEventHandler('searchbutton'); + + // Figure out if we need to shim-in localStorage and WebSQL + // support from the native side. + var storage = require('cordova/plugin/android/storage'); + + // First patch WebSQL if necessary + if (typeof window.openDatabase == 'undefined') { + // Not defined, create an openDatabase function for all to use! + window.openDatabase = storage.openDatabase; + } else { + // Defined, but some Android devices will throw a SECURITY_ERR - + // so we wrap the whole thing in a try-catch and shim in our own + // if the device has Android bug 16175. + var originalOpenDatabase = window.openDatabase; + window.openDatabase = function(name, version, desc, size) { + var db = null; + try { + db = originalOpenDatabase(name, version, desc, size); + } + catch (ex) { + if (ex.code === 18) { + db = null; + } else { + throw ex; + } + } + + if (db === null) { + return storage.openDatabase(name, version, desc, size); + } + else { + return db; + } + + }; + } + + // Patch localStorage if necessary + if (typeof window.localStorage == 'undefined' || window.localStorage === null) { + window.localStorage = new storage.CupcakeLocalStorage(); + } - navigator.geolocation.getCurrentPosition = geo.getCurrentPosition; - navigator.geolocation.watchPosition = geo.watchPosition; - navigator.geolocation.clearWatch = geo.clearWatch; + // Let native code know we are all done on the JS side. + // Native code will then un-hide the WebView. + channel.join(function() { + exec(null, null, "App", "show", []); + }, [channel.onCordovaReady]); }, objects: { - File: { // exists natively, override + cordova: { + children: { + JSCallback:{ + path:"cordova/plugin/android/callback" + }, + JSCallbackPolling:{ + path:"cordova/plugin/android/polling" + } + } + }, + navigator: { + children: { + app:{ + path: "cordova/plugin/android/app" + } + } + }, + File: { // exists natively on Android WebView, override path: "cordova/plugin/File" }, - MediaError: { // exists natively, override - path: "cordova/plugin/MediaError" + FileReader: { // exists natively on Android WebView, override + path: "cordova/plugin/FileReader" }, - device: { - path: 'cordova/plugin/ios/device' + FileError: { //exists natively on Android WebView on Android 4.x + path: "cordova/plugin/FileError" }, - console: { - path: 'cordova/plugin/ios/console' + MediaError: { // exists natively on Android WebView on Android 4.x + path: "cordova/plugin/MediaError" } }, - merges:{ - Contact:{ - path: "cordova/plugin/ios/Contact" - }, - Entry:{ - path: "cordova/plugin/ios/Entry" - }, - FileReader:{ - path: "cordova/plugin/ios/FileReader" + merges: { + device: { + path: 'cordova/plugin/android/device' }, - navigator:{ - children:{ - notification:{ - path:"cordova/plugin/ios/notification" - }, - contacts:{ - path:"cordova/plugin/ios/contacts" + navigator: { + children: { + notification: { + path: 'cordova/plugin/android/notification' } } } } }; -// use the native logger -var logger = require("cordova/plugin/logger"); -logger.useConsole(false); - }); // file: lib/common/plugin/Acceleration.js @@ -1199,7 +1295,14 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { popoverOptions = options.popoverOptions; } - exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]); + var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, + mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]; + + exec(successCallback, errorCallback, "Camera", "takePicture", args); +}; + +cameraExport.cleanup = function(successCallback, errorCallback) { + exec(successCallback, errorCallback, "Camera", "cleanup", []); }; module.exports = cameraExport; @@ -1784,7 +1887,7 @@ var utils = require('cordova/utils'), * {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) + * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly) */ var DirectoryEntry = function(name, fullPath) { DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]); @@ -2514,11 +2617,13 @@ var FileSystem = function(name, root) { }; module.exports = FileSystem; + }); // file: lib/common/plugin/FileTransfer.js define("cordova/plugin/FileTransfer", function(require, exports, module) { -var exec = require('cordova/exec'); +var exec = require('cordova/exec'), + FileTransferError = require('cordova/plugin/FileTransferError'); /** * FileTransfer uploads a file to a remote server. @@ -2537,16 +2642,20 @@ var FileTransfer = function() {}; * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false */ FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) { + // sanity parameter checking + if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum."); // check for options var fileKey = null; var fileName = null; var mimeType = null; var params = null; var chunkedMode = true; + var headers = null; if (options) { fileKey = options.fileKey; fileName = options.fileName; mimeType = options.mimeType; + headers = options.headers; if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") { chunkedMode = options.chunkedMode; } @@ -2558,7 +2667,12 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro } } - exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]); + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + + exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]); }; /** @@ -2569,6 +2683,8 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro * @param errorCallback {Function} Callback to be invoked upon error */ FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) { + // sanity parameter checking + if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum."); var win = function(result) { var entry = null; if (result.isDirectory) { @@ -2583,6 +2699,12 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro entry.fullPath = result.fullPath; successCallback(entry); }; + + var fail = function(e) { + var error = new FileTransferError(e.code, e.source, e.target, e.http_status); + errorCallback(error); + }; + exec(win, errorCallback, 'FileTransfer', 'download', [source, target]); }; @@ -2596,8 +2718,11 @@ define("cordova/plugin/FileTransferError", function(require, exports, module) { * FileTransferError * @constructor */ -var FileTransferError = function(code) { +var FileTransferError = function(code, source, target, status) { this.code = code || null; + this.source = source || null; + this.target = target || null; + this.http_status = status || null; }; FileTransferError.FILE_NOT_FOUND_ERR = 1; @@ -2605,6 +2730,7 @@ FileTransferError.INVALID_URL_ERR = 2; FileTransferError.CONNECTION_ERR = 3; module.exports = FileTransferError; + }); // file: lib/common/plugin/FileUploadOptions.js @@ -2616,15 +2742,19 @@ define("cordova/plugin/FileUploadOptions", function(require, exports, module) { * @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. + * @param headers {Object} Keys are header names, values are header values. Multiple + * headers of the same name are not supported. */ -var FileUploadOptions = function(fileKey, fileName, mimeType, params) { +var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) { this.fileKey = fileKey || null; this.fileName = fileName || null; this.mimeType = mimeType || null; this.params = params || null; + this.headers = headers || null; }; module.exports = FileUploadOptions; + }); // file: lib/common/plugin/FileUploadResult.js @@ -2812,7 +2942,7 @@ FileWriter.prototype.seek = function(offset) { if (offset < 0) { this.position = Math.max(offset + this.length, 0); } - // Offset is bigger then file size so set position + // Offset is bigger than file size so set position // to the end of the file. else if (offset > this.length) { this.position = this.length; @@ -3019,7 +3149,6 @@ Media.prototype.stop = function() { var me = this; exec(function() { me._position = 0; - me.successCallback(); }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]); }; @@ -3065,14 +3194,14 @@ Media.prototype.getCurrentPosition = function(success, fail) { * Start recording audio file. */ Media.prototype.startRecord = function() { - exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]); + exec(null, 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]); + exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]); }; /** @@ -3094,34 +3223,39 @@ Media.prototype.setVolume = function(volume) { * PRIVATE * * @param id The media object id (string) - * @param status The status code (int) - * @param msg The status message (string) + * @param msgType The 'type' of update this is + * @param value Use of value is determined by the msgType */ -Media.onStatus = function(id, msg, value) { +Media.onStatus = function(id, msgType, value) { + var media = mediaObjects[id]; - // If state update - if (msg === Media.MEDIA_STATE) { - if (value === Media.MEDIA_STOPPED) { - if (media.successCallback) { - media.successCallback(); - } - } - if (media.statusCallback) { - media.statusCallback(value); - } - } - else if (msg === Media.MEDIA_DURATION) { - media._duration = value; - } - else if (msg === Media.MEDIA_ERROR) { - if (media.errorCallback) { - // value should be a MediaError object when msg == MEDIA_ERROR - media.errorCallback(value); + + if(media) { + switch(msgType) { + case Media.MEDIA_STATE : + media.statusCallback && media.statusCallback(value); + if(value == Media.MEDIA_STOPPED) { + media.successCallback && media.successCallback(); + } + break; + case Media.MEDIA_DURATION : + media._duration = value; + break; + case Media.MEDIA_ERROR : + media.errorCallback && media.errorCallback(value); + break; + case Media.MEDIA_POSITION : + media._position = Number(value); + break; + default : + console && console.error && console.error("Unhandled Media.onStatus :: " + msgType); + break; } } - else if (msg === Media.MEDIA_POSITION) { - media._position = value; + else { + console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id); } + }; module.exports = Media; @@ -3131,20 +3265,36 @@ module.exports = Media; define("cordova/plugin/MediaError", function(require, exports, module) { /** * This class contains information about any Media errors. - * @constructor +*/ +/* + According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror + We should never be creating these objects, we should just implement the interface + which has 1 property for an instance, 'code' + + instead of doing : + errorCallbackFunction( new MediaError(3,'msg') ); +we should simply use a literal : + errorCallbackFunction( {'code':3} ); */ -var MediaError = function(code, msg) { - this.code = (code !== undefined ? code : null); - this.message = msg || ""; -}; -MediaError.MEDIA_ERR_NONE_ACTIVE = 0; -MediaError.MEDIA_ERR_ABORTED = 1; -MediaError.MEDIA_ERR_NETWORK = 2; -MediaError.MEDIA_ERR_DECODE = 3; -MediaError.MEDIA_ERR_NONE_SUPPORTED = 4; +if(!MediaError) { + var MediaError = function(code, msg) { + this.code = (typeof code != 'undefined') ? code : null; + this.message = msg || ""; // message is NON-standard! do not use! + }; +} + +MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0; +MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1; +MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2; +MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3; +MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4; +// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below. +// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes +MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4; module.exports = MediaError; + }); // file: lib/common/plugin/MediaFile.js @@ -3182,28 +3332,6 @@ MediaFile.prototype.getFormatData = function(successCallback, errorCallback) { } }; -// TODO: can we axe this? -/** - * 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 = []; - for (var i=0; i<pluginResult.message.length; i++) { - var mediaFile = new MediaFile(); - mediaFile.name = pluginResult.message[i].name; - mediaFile.fullPath = pluginResult.message[i].fullPath; - mediaFile.type = pluginResult.message[i].type; - mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate; - mediaFile.size = pluginResult.message[i].size; - mediaFiles.push(mediaFile); - } - pluginResult.message = mediaFiles; - return pluginResult; -}; - module.exports = MediaFile; }); @@ -3409,12 +3537,12 @@ var accelerometer = { var p; var win = function(a) { - successCallback(a); removeListeners(p); + successCallback(a); }; var fail = function(e) { - errorCallback(e); removeListeners(p); + errorCallback(e); }; p = createCallbackPair(win, fail); @@ -3446,8 +3574,8 @@ var accelerometer = { var id = utils.createUUID(); var p = createCallbackPair(function(){}, function(e) { - errorCallback(e); removeListeners(p); + errorCallback(e); }); listeners.push(p); @@ -3462,7 +3590,10 @@ var accelerometer = { if (running) { // If we're already running then immediately invoke the success callback - successCallback(accel); + // but only if we have retrieved a value, sample code does not check for null ... + if(accel) { + successCallback(accel); + } } else { start(); } @@ -3489,6 +3620,692 @@ module.exports = accelerometer; }); +// file: lib/android/plugin/android/app.js +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", []); + } +}; +}); + +// file: lib/android/plugin/android/callback.js +define("cordova/plugin/android/callback", function(require, exports, module) { +var port = null, + token = null, + xmlhttp; + +function startXhr() { + // cordova/exec depends on this module, so we can't require cordova/exec on the module level. + var exec = require('cordova/exec'), + xmlhttp = new XMLHttpRequest(); + + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if (!xmlhttp) { + return; + } + if (xmlhttp.readyState === 4){ + // 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(startXhr, 1); + } + + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(startXhr, 10); + } + + // 0 == Page is unloading. + // 400 == Bad request. + // 403 == invalid token. + // 503 == server stopped. + else { + console.log("JSCallback Error: Request failed with status " + xmlhttp.status); + exec.setNativeToJsBridgeMode(exec.nativeToJsModes.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 = { + start: function() { + startXhr(); + }, + + stop: function() { + if (xmlhttp) { + var tmp = xmlhttp; + xmlhttp = null; + tmp.abort(); + } + }, + + isAvailable: function() { + return ("true" != prompt("usePolling", "gap_callbackServer:")); + } +}; + + +}); + +// file: lib/android/plugin/android/device.js +define("cordova/plugin/android/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'), + app = require('cordova/plugin/android/app'); + +module.exports = { + /* + * DEPRECATED + * This is only for Android. + * + * You must explicitly override the back button. + */ + overrideBackButton:function() { + console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true)."); + app.overrideBackbutton(true); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This resets the back button to the default behaviour + */ + resetBackButton:function() { + console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false)."); + app.overrideBackbutton(false); + }, + + /* + * DEPRECATED + * This is only for Android. + * + * This terminates the activity! + */ + exitApp:function() { + console.log("Device.exitApp() is deprecated. Use App.exitApp()."); + app.exitApp(); + } +}; + +}); + +// file: lib/android/plugin/android/notification.js +define("cordova/plugin/android/notification", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Provides Android enhanced notification API. + */ +module.exports = { + activityStart : function(title, message) { + // If title and message not specified then mimic Android behavior of + // using default strings. + if (typeof title === "undefined" && typeof message == "undefined") { + title = "Busy"; + message = 'Please wait...'; + } + + exec(null, null, 'Notification', 'activityStart', [ title, message ]); + }, + + /** + * Close an activity dialog + */ + activityStop : function() { + exec(null, null, 'Notification', 'activityStop', []); + }, + + /** + * Display a progress dialog with progress bar that goes from 0 to 100. + * + * @param {String} + * title Title of the progress dialog. + * @param {String} + * message Message to display in the dialog. + */ + progressStart : function(title, message) { + exec(null, null, 'Notification', 'progressStart', [ title, message ]); + }, + + /** + * Close the progress dialog. + */ + progressStop : function() { + exec(null, null, 'Notification', 'progressStop', []); + }, + + /** + * Set the progress dialog value. + * + * @param {Number} + * value 0-100 + */ + progressValue : function(value) { + exec(null, null, 'Notification', 'progressValue', [ value ]); + } +}; +}); + +// file: lib/android/plugin/android/polling.js +define("cordova/plugin/android/polling", function(require, exports, module) { +var cordova = require('cordova'), + POLL_INTERVAL = 50, + enabled = false; + +function pollOnce() { + var msg = prompt("", "gap_poll:"); + if (msg) { + try { + eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + return true; + } + return false; +} + +function doPoll() { + if (!enabled) { + return; + } + var nextDelay = POLL_INTERVAL; + if (pollOnce()) { + nextDelay = 0; + } + setTimeout(doPoll, nextDelay); +} + +module.exports = { + start: function() { + enabled = true; + setTimeout(doPoll, 1); + }, + stop: function() { + enabled = false; + }, + pollOnce: pollOnce +}; + + +}); + +// file: lib/android/plugin/android/storage.js +define("cordova/plugin/android/storage", function(require, exports, module) { +var utils = require('cordova/utils'), + exec = require('cordova/exec'), + channel = require('cordova/channel'); + +var queryQueue = {}; + +/** + * SQL result set object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Rows = function() { + this.resultSet = []; // results array + this.length = 0; // number of rows +}; + +/** + * Get item from SQL result set + * + * @param row The row number to return + * @return The row object + */ +DroidDB_Rows.prototype.item = function(row) { + return this.resultSet[row]; +}; + +/** + * SQL result set that is returned to user. + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Result = function() { + this.rows = new DroidDB_Rows(); +}; + +/** + * Callback from native code when query is complete. + * PRIVATE METHOD + * + * @param id Query id + */ +function completeQuery(id, data) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + + // Save query results + var r = new DroidDB_Result(); + r.rows.resultSet = data; + r.rows.length = data.length; + try { + if (typeof query.successCallback === 'function') { + query.successCallback(query.tx, r); + } + } catch (ex) { + console.log("executeSql error calling user success callback: "+ex); + } + + tx.queryComplete(id); + } + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * Callback from native code when query fails + * PRIVATE METHOD + * + * @param reason Error message + * @param id Query id + */ +function failQuery(reason, id) { + var query = queryQueue[id]; + if (query) { + try { + delete queryQueue[id]; + + // Get transaction + var tx = query.tx; + + // If transaction hasn't failed + // Note: We ignore all query results if previous query + // in the same transaction failed. + if (tx && tx.queryList[id]) { + tx.queryList = {}; + + try { + if (typeof query.errorCallback === 'function') { + query.errorCallback(query.tx, reason); + } + } catch (ex) { + console.log("executeSql error calling user error callback: "+ex); + } + + tx.queryFailed(id, reason); + } + + } catch (e) { + console.log("executeSql error: "+e); + } + } +} + +/** + * SQL query object + * PRIVATE METHOD + * + * @constructor + * @param tx The transaction object that this query belongs to + */ +var DroidDB_Query = function(tx) { + + // Set the id of the query + this.id = utils.createUUID(); + + // Add this query to the queue + queryQueue[this.id] = this; + + // Init result + this.resultSet = []; + + // Set transaction that this query belongs to + this.tx = tx; + + // Add this query to transaction list + this.tx.queryList[this.id] = this; + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + +}; + +/** + * Transaction object + * PRIVATE METHOD + * @constructor + */ +var DroidDB_Tx = function() { + + // Set the id of the transaction + this.id = utils.createUUID(); + + // Callbacks + this.successCallback = null; + this.errorCallback = null; + + // Query list + this.queryList = {}; +}; + +/** + * Mark query in transaction as complete. + * If all queries are complete, call the user's transaction success callback. + * + * @param id Query id + */ +DroidDB_Tx.prototype.queryComplete = function(id) { + delete this.queryList[id]; + + // If no more outstanding queries, then fire transaction success + if (this.successCallback) { + var count = 0; + var i; + for (i in this.queryList) { + if (this.queryList.hasOwnProperty(i)) { + count++; + } + } + if (count === 0) { + try { + this.successCallback(); + } catch(e) { + console.log("Transaction error calling user success callback: " + e); + } + } + } +}; + +/** + * Mark query in transaction as failed. + * + * @param id Query id + * @param reason Error message + */ +DroidDB_Tx.prototype.queryFailed = function(id, reason) { + + // The sql queries in this transaction have already been run, since + // we really don't have a real transaction implemented in native code. + // However, the user callbacks for the remaining sql queries in transaction + // will not be called. + this.queryList = {}; + + if (this.errorCallback) { + try { + this.errorCallback(reason); + } catch(e) { + console.log("Transaction error calling user error callback: " + e); + } + } +}; + +/** + * Execute SQL statement + * + * @param sql SQL statement to execute + * @param params Statement parameters + * @param successCallback Success callback + * @param errorCallback Error callback + */ +DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) { + + // Init params array + if (typeof params === 'undefined') { + params = []; + } + + // Create query and add to queue + var query = new DroidDB_Query(this); + queryQueue[query.id] = query; + + // Save callbacks + query.successCallback = successCallback; + query.errorCallback = errorCallback; + + // Call native code + exec(null, null, "Storage", "executeSql", [sql, params, query.id]); +}; + +var DatabaseShell = function() { +}; + +/** + * Start a transaction. + * Does not support rollback in event of failure. + * + * @param process {Function} The transaction function + * @param successCallback {Function} + * @param errorCallback {Function} + */ +DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) { + var tx = new DroidDB_Tx(); + tx.successCallback = successCallback; + tx.errorCallback = errorCallback; + try { + process(tx); + } catch (e) { + console.log("Transaction error: "+e); + if (tx.errorCallback) { + try { + tx.errorCallback(e); + } catch (ex) { + console.log("Transaction error calling user error callback: "+e); + } + } + } +}; + +/** + * Open database + * + * @param name Database name + * @param version Database version + * @param display_name Database display name + * @param size Database size in bytes + * @return Database object + */ +var DroidDB_openDatabase = function(name, version, display_name, size) { + exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]); + var db = new DatabaseShell(); + return db; +}; + +/** + * For browsers with no localStorage we emulate it with SQLite. Follows the w3c api. + * TODO: Do similar for sessionStorage. + * @constructor + */ +var CupcakeLocalStorage = function() { + channel.waitForInitialization("cupcakeStorage"); + + try { + + this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440); + var storage = {}; + this.length = 0; + function setLength (length) { + this.length = length; + localStorage.length = length; + } + this.db.transaction( + function (transaction) { + var i; + transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))'); + transaction.executeSql('SELECT * FROM storage', [], function(tx, result) { + for(var i = 0; i < result.rows.length; i++) { + storage[result.rows.item(i).id] = result.rows.item(i).body; + } + setLength(result.rows.length); + channel.initializationComplete("cupcakeStorage"); + }); + + }, + function (err) { + utils.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) { + utils.alert("Database error "+e+"."); + return; + } +}; + +module.exports = { + openDatabase:DroidDB_openDatabase, + CupcakeLocalStorage:CupcakeLocalStorage, + failQuery:failQuery, + completeQuery:completeQuery +}; + +}); + // file: lib/common/plugin/battery.js define("cordova/plugin/battery", function(require, exports, module) { /** @@ -3977,7 +4794,7 @@ var contacts = { * 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 + * @param properties an object whose properties will be examined to create a new Contact * @returns new Contact object */ create:function(properties) { @@ -3996,6 +4813,93 @@ module.exports = contacts; }); +// file: lib/common/plugin/device.js +define("cordova/plugin/device", function(require, exports, module) { +var channel = require('cordova/channel'), + utils = require('cordova/utils'), + exec = require('cordova/exec'); + +// Tell cordova channel to wait on the CordovaInfoReady event +channel.waitForInitialization('onCordovaInfoReady'); + +/** + * 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; + + channel.onCordovaReady.subscribeOnce(function() { + me.getInfo(function(info) { + me.available = true; + me.platform = info.platform; + me.version = info.version; + me.name = info.name; + me.uuid = info.uuid; + me.cordova = info.cordova; + channel.onCordovaInfoReady.fire(); + },function(e) { + me.available = false; + utils.alert("[ERROR] Error initializing Cordova: " + e); + }); + }); +} + +/** + * Get device info + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + */ +Device.prototype.getInfo = function(successCallback, errorCallback) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Device Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Device Error: errorCallback is not a function"); + return; + } + + // Get info + exec(successCallback, errorCallback, "Device", "getDeviceInfo", []); +}; + +module.exports = new Device(); + +}); + +// file: lib/common/plugin/echo.js +define("cordova/plugin/echo", function(require, exports, module) { +var exec = require('cordova/exec'); + +/** + * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback. + * @param successCallback invoked with a FileSystem object + * @param errorCallback invoked if error occurs retrieving file system + * @param message The string to be echoed. + * @param forceAsync Whether to force an async return value (for testing native->js bridge). + */ +module.exports = function(successCallback, errorCallback, message, forceAsync) { + var action = forceAsync ? 'echoAsync' : 'echo'; + exec(successCallback, errorCallback, "Echo", action, [message]); +}; + + +}); + // file: lib/common/plugin/geolocation.js define("cordova/plugin/geolocation", function(require, exports, module) { var utils = require('cordova/utils'), @@ -4104,7 +5008,7 @@ var geolocation = { } else if (options.timeout === 0) { fail({ code:PositionError.TIMEOUT, - message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter." + message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter." }); // Otherwise we have to call into native to retrieve a position. } else { @@ -4194,325 +5098,6 @@ module.exports = geolocation; }); -// file: lib/ios/plugin/ios/Contact.js -define("cordova/plugin/ios/Contact", function(require, exports, module) { -var exec = require('cordova/exec'), - ContactError = require('cordova/plugin/ContactError'); - -/** - * Provides iOS Contact.display API. - */ -module.exports = { - display : function(errorCB, options) { - /* - * Display a contact using the iOS Contact Picker UI - * NOT part of W3C spec so no official documentation - * - * @param errorCB error callback - * @param options object - * allowsEditing: boolean AS STRING - * "true" to allow editing the contact - * "false" (default) display contact - */ - - if (this.id === null) { - if (typeof errorCB === "function") { - var errorObj = new ContactError(ContactError.UNKNOWN_ERROR); - errorCB(errorObj); - } - } - else { - exec(null, errorCB, "Contacts","displayContact", [this.id, options]); - } - } -}; -}); - -// file: lib/ios/plugin/ios/Entry.js -define("cordova/plugin/ios/Entry", function(require, exports, module) { -module.exports = { - toURL:function() { - // TODO: refactor path in a cross-platform way so we can eliminate - // these kinds of platform-specific hacks. - return "file://localhost" + this.fullPath; - }, - toURI: function() { - console.log("DEPRECATED: Update your code to use 'toURL'"); - return "file://localhost" + this.fullPath; - } -}; -}); - -// file: lib/ios/plugin/ios/FileReader.js -define("cordova/plugin/ios/FileReader", function(require, exports, module) { -var exec = require('cordova/exec'), - FileError = require('cordova/plugin/FileError'), - FileReader = require('cordova/plugin/FileReader'), - ProgressEvent = require('cordova/plugin/ProgressEvent'); - -module.exports = { - readAsText:function(file, encoding) { - // Figure out pathing - 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})); - } - - // Default encoding is UTF-8 - var enc = encoding ? encoding : "UTF-8"; - - var me = this; - - // Read file - exec( - // Success callback - function(r) { - // If DONE (cancelled), then don't do anything - if (me.readyState === FileReader.DONE) { - return; - } - - // Save result - me.result = decodeURIComponent(r); - - // If onload callback - if (typeof me.onload === "function") { - me.onload(new ProgressEvent("load", {target:me})); - } - - // DONE state - me.readyState = FileReader.DONE; - - // If onloadend callback - if (typeof me.onloadend === "function") { - me.onloadend(new ProgressEvent("loadend", {target:me})); - } - }, - // Error callback - function(e) { - // If DONE (cancelled), then don't do anything - if (me.readyState === FileReader.DONE) { - return; - } - - // DONE state - me.readyState = FileReader.DONE; - - // null result - 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", "readAsText", [this.fileName, enc]); - } -}; -}); - -// file: lib/ios/plugin/ios/console.js -define("cordova/plugin/ios/console", function(require, exports, module) { -var exec = require('cordova/exec'); - -/** - * This class provides access to the debugging console. - * @constructor - */ -var DebugConsole = function() { - this.winConsole = window.console; - this.logLevel = DebugConsole.INFO_LEVEL; -}; - -// from most verbose, to least verbose -DebugConsole.ALL_LEVEL = 1; // same as first level -DebugConsole.INFO_LEVEL = 1; -DebugConsole.WARN_LEVEL = 2; -DebugConsole.ERROR_LEVEL = 4; -DebugConsole.NONE_LEVEL = 8; - -DebugConsole.prototype.setLevel = function(level) { - this.logLevel = level; -}; - -var stringify = function(message) { - try { - if (typeof message === "object" && JSON && JSON.stringify) { - try { - return JSON.stringify(message); - } - catch (e) { - return "error JSON.stringify()ing argument: " + e; - } - } else { - return message.toString(); - } - } catch (e) { - return e.toString(); - } -}; - -/** - * Print a normal log message to the console - * @param {Object|String} message Message or object to print to the console - */ -DebugConsole.prototype.log = function(message) { - if (this.logLevel <= DebugConsole.INFO_LEVEL) { - exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'INFO' } ]); - } - else if (this.winConsole && this.winConsole.log) { - this.winConsole.log(message); - } -}; - -/** - * Print a warning message to the console - * @param {Object|String} message Message or object to print to the console - */ -DebugConsole.prototype.warn = function(message) { - if (this.logLevel <= DebugConsole.WARN_LEVEL) { - exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'WARN' } ]); - } - else if (this.winConsole && this.winConsole.warn) { - this.winConsole.warn(message); - } -}; - -/** - * Print an error message to the console - * @param {Object|String} message Message or object to print to the console - */ -DebugConsole.prototype.error = function(message) { - if (this.logLevel <= DebugConsole.ERROR_LEVEL) { - exec(null, null, 'Debug Console', 'log', [ stringify(message), { logLevel: 'ERROR' } ]); - } - else if (this.winConsole && this.winConsole.error){ - this.winConsole.error(message); - } -}; - -module.exports = new DebugConsole(); -}); - -// file: lib/ios/plugin/ios/contacts.js -define("cordova/plugin/ios/contacts", function(require, exports, module) { -var exec = require('cordova/exec'); - -/** - * Provides iOS enhanced contacts API. - */ -module.exports = { - newContactUI : function(successCallback) { - /* - * Create a contact using the iOS Contact Picker UI - * NOT part of W3C spec so no official documentation - * - * returns: the id of the created contact as param to successCallback - */ - exec(successCallback, null, "Contacts","newContact", []); - }, - chooseContact : function(successCallback, options) { - /* - * Select a contact using the iOS Contact Picker UI - * NOT part of W3C spec so no official documentation - * - * @param errorCB error callback - * @param options object - * allowsEditing: boolean AS STRING - * "true" to allow editing the contact - * "false" (default) display contact - * - * returns: the id of the selected contact as param to successCallback - */ - exec(successCallback, null, "Contacts","chooseContact", [options]); - } -}; -}); - -// file: lib/ios/plugin/ios/device.js -define("cordova/plugin/ios/device", function(require, exports, module) { -/** - * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the - * phone, etc. - * @constructor - */ -var exec = require('cordova/exec'), - utils = require('cordova/utils'), - channel = require('cordova/channel'); - -var Device = function() { - this.platform = null; - this.version = null; - this.name = null; - this.cordova = null; - this.uuid = null; -}; - -Device.prototype.setInfo = function(info) { - try { - this.platform = info.platform; - this.version = info.version; - this.name = info.name; - this.cordova = info.cordova; - this.uuid = info.uuid; - channel.onCordovaInfoReady.fire(); - } catch(e) { - utils.alert('Error during device info setting in cordova/plugin/ios/device!'); - } -}; - -module.exports = new Device(); - -}); - -// file: lib/ios/plugin/ios/nativecomm.js -define("cordova/plugin/ios/nativecomm", function(require, exports, module) { -var cordova = require('cordova'); - -/** - * Called by native code to retrieve all queued commands and clear the queue. - */ -module.exports = function() { - var json = JSON.stringify(cordova.commandQueue); - cordova.commandQueue = []; - return json; -}; -}); - -// file: lib/ios/plugin/ios/notification.js -define("cordova/plugin/ios/notification", function(require, exports, module) { -var Media = require('cordova/plugin/Media'); - -module.exports = { - beep:function(count) { - (new Media('beep.wav')).play(); - } -}; -}); - // file: lib/common/plugin/logger.js define("cordova/plugin/logger", function(require, exports, module) { //------------------------------------------------------------------------------ @@ -4587,7 +5172,7 @@ CurrentLevel = LevelsMap.WARN; * * The value used determines which messages get printed. The logging * values above are in order, and only messages logged at the logging - * level or above will actually be displayed to the user. Eg, the + * level or above will actually be displayed to the user. E.g., the * default level is WARN, so only messages logged with LOG, ERROR, or * WARN will be displayed; INFO and DEBUG messages will be ignored. */ @@ -4966,6 +5551,9 @@ define("cordova/plugin/splashscreen", function(require, exports, module) { var exec = require('cordova/exec'); var splashscreen = { + show:function() { + exec(null, null, "SplashScreen", "show", []); + }, hide:function() { exec(null, null, "SplashScreen", "hide", []); } @@ -5223,4 +5811,4 @@ window.cordova = require('cordova'); }(window)); -})();
\ No newline at end of file +})();var PhoneGap = cordova; diff --git a/phonegap/www/email_sent.html b/phonegap/www/email_sent.html index 529ccb693..9219e2586 100644 --- a/phonegap/www/email_sent.html +++ b/phonegap/www/email_sent.html @@ -13,7 +13,7 @@ <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="js/json2.js"></script> <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" src="cordova-1.8.0.js"></script> + <script type="text/javascript" src="cordova-2.1.0.js"></script> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> <script type="text/javascript" src="js/mobile.js"></script> diff --git a/phonegap/www/index.html b/phonegap/www/index.html index 77de47165..cb6116ff8 100644 --- a/phonegap/www/index.html +++ b/phonegap/www/index.html @@ -10,7 +10,7 @@ <link rel="stylesheet" href="css/mobile.css"> <script type="text/javascript" src="js/json2.js"></script> <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> <script src="js/jquery.validate.min.js" type="text/javascript" charset="utf-8"></script> diff --git a/phonegap/www/no_connection.html b/phonegap/www/no_connection.html index 8a77d9c26..feade291c 100644 --- a/phonegap/www/no_connection.html +++ b/phonegap/www/no_connection.html @@ -10,7 +10,7 @@ <link rel="stylesheet" href="css/mobile.css"> <script type="text/javascript" src="js/json2.js"></script> <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> <script src="js/jquery.validate.min.js" type="text/javascript" charset="utf-8"></script> diff --git a/phonegap/www/report_created.html b/phonegap/www/report_created.html index 6810ac60e..192e1df02 100644 --- a/phonegap/www/report_created.html +++ b/phonegap/www/report_created.html @@ -12,7 +12,7 @@ <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> <script type="text/javascript" src="js/mobile.js"></script> diff --git a/phonegap/www/sign_in.html b/phonegap/www/sign_in.html index 0a4658e5a..3c9fe21ed 100644 --- a/phonegap/www/sign_in.html +++ b/phonegap/www/sign_in.html @@ -13,7 +13,7 @@ <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> diff --git a/phonegap/www/signed_in.html b/phonegap/www/signed_in.html index 1ef6e284f..041333db0 100644 --- a/phonegap/www/signed_in.html +++ b/phonegap/www/signed_in.html @@ -13,7 +13,7 @@ <script type="text/javascript" src="js/config.js"></script> - <script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script> + <script type="text/javascript" charset="utf-8" src="cordova-2.1.0.js"></script> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <script type="text/javascript" src="js/jquery-1.7.0.min.js"></script> |