Version 2.7.0-rc.1
diff --git a/VERSION b/VERSION
index e70b452..59b7056 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.6.0
+2.7.0rc1
diff --git a/lib/cordova-android/.gitignore b/lib/cordova-android/.gitignore
new file mode 100644
index 0000000..757a698
--- /dev/null
+++ b/lib/cordova-android/.gitignore
@@ -0,0 +1,35 @@
+.DS_Store
+default.properties
+gen
+assets/www/cordova.js
+framework/assets/www/.tmp*
+local.properties
+framework/lib
+proguard.cfg
+proguard.cfg
+proguard-project.txt
+framework/bin
+framework/test/org/apache/cordova/*.class
+framework/assets/www/.DS_Store
+framework/assets/www/cordova-*.js
+framework/assets/www/phonegap-*.js
+framework/libs
+test/libs
+example
+./test
+test/bin
+test/assets/www/.tmp*
+tmp/**
+bin/node_modules
+.metadata
+tmp/**/*
+Thumbs.db
+Desktop.ini
+*.tmp
+*.bak
+*.swp
+*.class
+*.jar
+# IntelliJ IDEA files
+*.iml
+.idea
diff --git a/lib/cordova-android/bin/check_reqs.bat b/lib/cordova-android/bin/check_reqs.bat
new file mode 100644
index 0000000..65514c8
--- /dev/null
+++ b/lib/cordova-android/bin/check_reqs.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%check_reqs.js (
+        cscript "%full_path%check_reqs.js" //nologo
+) ELSE (
+    ECHO.
+    ECHO ERROR: Could not find 'check_reqs.js' in 'bin' folder, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/check_reqs.js b/lib/cordova-android/bin/check_reqs.js
new file mode 100644
index 0000000..ef30991
--- /dev/null
+++ b/lib/cordova-android/bin/check_reqs.js
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+// 
+// http://www.apache.org/licenses/LICENSE-2.0
+// 
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+var ROOT  = WScript.ScriptFullName.split('\\bin\\check_reqs.js').join(''),
+    shell = WScript.CreateObject("WScript.Shell"),
+    fso   = WScript.CreateObject('Scripting.FileSystemObject');
+
+
+// executes a command in the shell, returns stdout or stderr if error
+function exec_out(command) {
+    var oExec=shell.Exec(command);
+    var output = new String();
+    while (oExec.Status == 0) {
+        if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadAll();
+            // XXX: Change to verbose mode 
+            // WScript.StdOut.WriteLine(line);
+            output += line;
+        }
+        WScript.sleep(100);
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oExec.StdErr.AtEndOfStream) {
+        var line = oExec.StdErr.ReadAll();
+        return {'error' : true, 'output' : line};
+    } else if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadAll();
+            // XXX: Change to verbose mode 
+            // WScript.StdOut.WriteLine(line);
+            output += line;
+    }
+    return {'error' : false, 'output' : output};
+}
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+} 
+
+// checks that android requirements are met
+function check_requirements() {
+    var result = exec_out('%comspec% /c android list target');
+    if(result.error) {
+        Log('The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path. Output: ' + result.output, true);
+        WScript.Quit(2);
+    }
+    else if(!result.output.match(/android[-]17/)) {
+        Log('Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools.', true);
+        Log('Output : ' + result.output);
+        WScript.Quit(2);
+    }
+    else {
+        var cmd = '%comspec% /c android update project -p ' + ROOT + '\\framework -t android-17';
+        result = exec_out(cmd);
+        if(result.error) {
+            Log('Error updating the Cordova library to work with your Android environment. Command run: "' + cmd + '", output: ' + result.output, true);
+            WScript.Quit(2);  
+        }
+    }
+}
+
+check_requirements();
\ No newline at end of file
diff --git a/lib/cordova-android/bin/create b/lib/cordova-android/bin/create
index 2eae82b..b7e96b4 100755
--- a/lib/cordova-android/bin/create
+++ b/lib/cordova-android/bin/create
@@ -25,8 +25,11 @@
 
 if [ -z "$1" ] || [ "$1" == "-h" ]
 then
-    echo 'usage: create path package activity'
+    echo "Usage: $0 <path_to_new_project> <package_name> <project_name>"
     echo "Make sure the Android SDK tools folder is in your PATH!"
+    echo "    <path_to_new_project>: Path to your new Cordova iOS project"
+    echo "    <package_name>: Package name, following reverse-domain style convention"
+    echo "    <project_name>: Project name"
     exit 0
 fi
 
@@ -158,11 +161,18 @@
 
 # creating cordova folder and copying run/build/log/launch scripts
 mkdir "$PROJECT_PATH"/cordova
+mkdir "$PROJECT_PATH"/cordova/lib
 createAppInfoJar
 cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
-cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova
 cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
-cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release
 cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
 cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
 cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
+cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova "$PROJECT_PATH"/cordova/lib/cordova
+cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
+cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-emulator-images "$PROJECT_PATH"/cordova/lib/list-emulator-images
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-started-emulators "$PROJECT_PATH"/cordova/lib/list-started-emulators
+cp "$BUILD_PATH"/bin/templates/cordova/lib/start-emulator "$PROJECT_PATH"/cordova/lib/start-emulator
+
diff --git a/lib/cordova-android/bin/create.bat b/lib/cordova-android/bin/create.bat
index 35fdc3b..7f0346f 100644
--- a/lib/cordova-android/bin/create.bat
+++ b/lib/cordova-android/bin/create.bat
@@ -1,3 +1,5 @@
+@ECHO OFF
+GOTO BEGIN
 :: 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
@@ -15,18 +17,38 @@
 :: specific language governing permissions and limitations
 :: under the License.
 
-@ECHO OFF
-IF NOT DEFINED JAVA_HOME GOTO MISSING
-FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
-    SET FOUND=%%~$PATH:X
-    IF NOT DEFINED FOUND GOTO MISSING
-)
-cscript "%~dp0\create.js" %*
-GOTO END
-:MISSING
-ECHO Missing one of the following:
-ECHO JDK: http://java.oracle.com
-ECHO Android SDK: http://developer.android.com
-ECHO Apache ant: http://ant.apache.org
+:BEGIN
+        IF NOT DEFINED JAVA_HOME GOTO MISSING_JAVA_HOME
+
+        FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
+            IF [%%~$PATH:X]==[] (
+              ECHO Cannot locate %%X using the PATH environment variable.
+              ECHO Retry after adding directory containing %%X to the PATH variable.
+              ECHO Remember to open a new command window after updating the PATH variable.
+              IF "%%X"=="java.exe" GOTO GET_JAVA
+              IF "%%X"=="javac.exe" GOTO GET_JAVA
+              IF "%%X"=="ant.bat" GOTO GET_ANT
+              IF "%%X"=="android.bat" GOTO GET_ANDROID
+              GOTO ERROR
+            )
+        )
+        cscript "%~dp0\create.js" %* //nologo
+        GOTO END
+:MISSING_JAVA_HOME
+        ECHO The JAVA_HOME environment variable is not set.
+        ECHO Set JAVA_HOME to an existing JRE directory.
+        ECHO Remember to also add JAVA_HOME to the PATH variable.
+        ECHO After updating system variables, open a new command window and retry.
+        GOTO ERROR
+:GET_JAVA
+        ECHO Visit http://java.oracle.com if you need to install Java (JDK).
+        GOTO ERROR
+:GET_ANT
+        ECHO Visit http://ant.apache.org if you need to install Apache Ant.
+        GOTO ERROR
+:GET_ANDROID
+        ECHO Visit http://developer.android.com if you need to install the Android SDK.
+        GOTO ERROR
+:ERROR
 EXIT /B 1
 :END
diff --git a/lib/cordova-android/bin/create.js b/lib/cordova-android/bin/create.js
index d0d9999..5176b7d 100644
--- a/lib/cordova-android/bin/create.js
+++ b/lib/cordova-android/bin/create.js
@@ -24,7 +24,30 @@
  *  ./create [path package activity]
  */
 
-var fso = WScript.CreateObject('Scripting.FileSystemObject');
+var args = WScript.Arguments, PROJECT_PATH="example", 
+    PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
+    shell=WScript.CreateObject("WScript.Shell"),
+    fso = WScript.CreateObject('Scripting.FileSystemObject');
+
+function Usage() {
+    Log("Usage: create PathTONewProject [ PackageName AppName ]");
+    Log("    PathTONewProject : The path to where you wish to create the project");
+    Log("    PackageName      : The package for the project (default is org.apache.cordova.example)")
+    Log("    AppName          : The name of the application/activity (default is cordovaExample)");
+    Log("examples:");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject");
+    Log("    create C:\\Users\\anonymous\\Desktop\\MyProject io.Cordova.Example AnApp");
+}
+
+// logs messaged to stdout and stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+}
 
 function read(filename) {
     var fso=WScript.CreateObject("Scripting.FileSystemObject");
@@ -36,7 +59,7 @@
 
 function checkTargets(targets) {
     if(!targets) {
-        WScript.Echo("You do not have any android targets setup. Please create at least one target with the `android` command");
+        Log("You do not have any android targets setup. Please create at least one target with the `android` command", true);
         WScript.Quit(69);
     }
 }
@@ -74,7 +97,7 @@
 
 function createAppInfoJar() {
     if(!fso.FileExists(ROOT+"\\bin\\templates\\cordova\\appinfo.jar")) {
-        WScript.Echo("Creating appinfo.jar...");
+        Log("Creating appinfo.jar...");
         var cur = shell.CurrentDirectory;
         shell.CurrentDirectory = ROOT+"\\bin\\templates\\cordova\\ApplicationInfo";
         exec("javac ApplicationInfo.java");
@@ -120,7 +143,7 @@
           stream.SaveToFile(savePath);
           stream.Close();
         } else {
-          WScript.Echo('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
+          Log('Could not retrieve the commons-codec. Please download it yourself and put into the framework/libs directory. This process may fail now. Sorry.');
         }
       }
       var app = WScript.CreateObject('Shell.Application');
@@ -136,23 +159,34 @@
       fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.7', true);
     }
 }
-
-var args = WScript.Arguments, PROJECT_PATH="example", 
-    PACKAGE="org.apache.cordova.example", ACTIVITY="cordovaExample",
-    shell=WScript.CreateObject("WScript.Shell");
     
 // working dir
 var ROOT = WScript.ScriptFullName.split('\\bin\\create.js').join('');
+if (args.Count() > 0) {
+    // support help flags
+    if (args(0) == "--help" || args(0) == "/?" ||
+            args(0) == "help" || args(0) == "-help" || args(0) == "/help" || args(0) == "-h") {
+        Usage();
+        WScript.Quit(2);
+    }
 
-if (args.Count() == 3) {
     PROJECT_PATH=args(0);
-    PACKAGE=args(1);
-    ACTIVITY=args(2);
+    if (args.Count() > 1) {
+        PACKAGE = args(1);
+    }
+    if (args.Count() > 2) {
+        ACTIVITY = args(2);
+    }
+}
+else {
+    Log("Error : No project path provided.");
+    Usage();
+    WScript.Quit(2);
 }
 
 if(fso.FolderExists(PROJECT_PATH)) {
-    WScript.Echo("Project already exists!");
-    WScript.Quit(1);
+    Log("Project already exists!", true);
+    WScript.Quit(2);
 }
 
 var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
@@ -162,13 +196,13 @@
 var API_LEVEL=setApiLevel();
 var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
 // create the project
-WScript.Echo("Creating new android project...");
+Log("Creating new android project...");
 exec('android.bat create project --target '+TARGET+' --path '+PROJECT_PATH+' --package '+PACKAGE+' --activity '+ACTIVITY);
 
 // build from source. distro should have these files
 if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
     !fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
-    WScript.Echo("Building jar and js files...");
+    Log("Building jar and js files...");
     // update the cordova framework project to a target that exists on this machine
     exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
     // pull down commons codec if necessary
@@ -177,14 +211,14 @@
 }
 
 // copy in the project template
-WScript.Echo("Copying template files...");
+Log("Copying template files...");
 exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
 exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /E /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
 
 // check if we have the source or the distro files
-WScript.Echo("Copying js, jar & config.xml files...");
+Log("Copying js, jar & config.xml files...");
 if(fso.FolderExists(ROOT + '\\framework')) {
     exec('%comspec% /c copy "'+ROOT+'"\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
     exec('%comspec% /c copy "'+ROOT+'"\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
@@ -202,10 +236,17 @@
 
 // copy cordova scripts
 fso.CreateFolder(PROJECT_PATH + '\\cordova');
+fso.CreateFolder(PROJECT_PATH + '\\cordova\\lib');
 createAppInfoJar();
-WScript.Echo("Copying cordova command tools...");
+Log("Copying cordova command tools...");
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\cordova.js ' + PROJECT_PATH + '\\cordova\\lib\\cordova.js /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\install-device.bat ' + PROJECT_PATH + '\\cordova\\lib\\install-device.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\install-emulator.bat ' + PROJECT_PATH + '\\cordova\\lib\\install-emulator.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\list-emulator-images.bat ' + PROJECT_PATH + '\\cordova\\lib\\list-emulator-images.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\list-devices.bat ' + PROJECT_PATH + '\\cordova\\lib\\list-devices.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\list-started-emulators.bat ' + PROJECT_PATH + '\\cordova\\lib\\list-started-emulators.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\start-emulator.bat ' + PROJECT_PATH + '\\cordova\\lib\\start-emulator.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\build.bat ' + PROJECT_PATH + '\\cordova\\build.bat /Y');
@@ -213,7 +254,7 @@
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\run.bat ' + PROJECT_PATH + '\\cordova\\run.bat /Y');
 
 // interpolate the activity name and package
-WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
+Log("Updating AndroidManifest.xml and Main Activity...");
 replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
 replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
 
diff --git a/lib/cordova-android/bin/templates/cordova/ApplicationInfo/ApplicationInfo.class b/lib/cordova-android/bin/templates/cordova/ApplicationInfo/ApplicationInfo.class
deleted file mode 100644
index 2ef42a4..0000000
--- a/lib/cordova-android/bin/templates/cordova/ApplicationInfo/ApplicationInfo.class
+++ /dev/null
Binary files differ
diff --git a/lib/cordova-android/bin/templates/cordova/appinfo.jar b/lib/cordova-android/bin/templates/cordova/appinfo.jar
deleted file mode 100644
index 390bb6d..0000000
--- a/lib/cordova-android/bin/templates/cordova/appinfo.jar
+++ /dev/null
Binary files differ
diff --git a/lib/cordova-android/bin/templates/cordova/build b/lib/cordova-android/bin/templates/cordova/build
index e586e4d..3cbd9c1 100755
--- a/lib/cordova-android/bin/templates/cordova/build
+++ b/lib/cordova-android/bin/templates/cordova/build
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova build
+bash "$CORDOVA_PATH"/lib/cordova build "$@"
diff --git a/lib/cordova-android/bin/templates/cordova/build.bat b/lib/cordova-android/bin/templates/cordova/build.bat
index 8e6ca9a..7aa7c75 100644
--- a/lib/cordova-android/bin/templates/cordova/build.bat
+++ b/lib/cordova-android/bin/templates/cordova/build.bat
@@ -1,18 +1,2 @@
-:: Licensed to the Apache Software Foundation (ASF) under one
-:: or more contributor license agreements.  See the NOTICE file
-:: distributed with this work for additional information
-:: regarding copyright ownership.  The ASF licenses this file
-:: to you under the Apache License, Version 2.0 (the
-:: "License"); you may not use this file except in compliance
-:: with the License.  You may obtain a copy of the License at
-:: 
-:: http://www.apache.org/licenses/LICENSE-2.0
-:: 
-:: Unless required by applicable law or agreed to in writing,
-:: software distributed under the License is distributed on an
-:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-:: KIND, either express or implied.  See the License for the
-:: specific language governing permissions and limitations
-:: under the License.
-
-%~dp0\cordova.bat build
+@ECHO OFF
+%~dp0\cordova.bat build %*
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/clean b/lib/cordova-android/bin/templates/cordova/clean
index 53b7f9a..f52966a 100755
--- a/lib/cordova-android/bin/templates/cordova/clean
+++ b/lib/cordova-android/bin/templates/cordova/clean
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova clean
+bash "$CORDOVA_PATH"/lib/cordova clean "$@"
diff --git a/lib/cordova-android/bin/templates/cordova/clean.bat b/lib/cordova-android/bin/templates/cordova/clean.bat
index fe5c09f..b41bdc9 100644
--- a/lib/cordova-android/bin/templates/cordova/clean.bat
+++ b/lib/cordova-android/bin/templates/cordova/clean.bat
@@ -1,18 +1,2 @@
-:: Licensed to the Apache Software Foundation (ASF) under one
-:: or more contributor license agreements.  See the NOTICE file
-:: distributed with this work for additional information
-:: regarding copyright ownership.  The ASF licenses this file
-:: to you under the Apache License, Version 2.0 (the
-:: "License"); you may not use this file except in compliance
-:: with the License.  You may obtain a copy of the License at
-:: 
-:: http://www.apache.org/licenses/LICENSE-2.0
-:: 
-:: Unless required by applicable law or agreed to in writing,
-:: software distributed under the License is distributed on an
-:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-:: KIND, either express or implied.  See the License for the
-:: specific language governing permissions and limitations
-:: under the License.
-
-%~dp0\cordova.bat clean
+@ECHO OFF
+%~dp0\cordova.bat clean %*
diff --git a/lib/cordova-android/bin/templates/cordova/cordova b/lib/cordova-android/bin/templates/cordova/cordova
deleted file mode 100755
index 1945a4c..0000000
--- a/lib/cordova-android/bin/templates/cordova/cordova
+++ /dev/null
@@ -1,159 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-# 
-# http://www.apache.org/licenses/LICENSE-2.0
-# 
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-#!/bin/bash
-
-
-PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
-
-function check_devices {
-# FIXME
-    local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device`
-    if [ -z "$devices"  ] ; then
-        echo "1"
-    else
-        echo "0"
-    fi
-}
-
-function emulate {
-    declare -a avd_list=($(android list avd | grep "Name:" | cut -f 2 -d ":" | xargs))
-    # we need to start adb-server
-    adb start-server 1>/dev/null
-
-    # Do not launch an emulator if there is already one running or if a device is attached
-    if [ $(check_devices) == 0 ] ; then
-        return
-    fi
-
-    local avd_id="1000" #FIXME: hopefully user does not have 1000 AVDs
-    # User has no AVDs
-    if [ ${#avd_list[@]} == 0 ]
-    then
-        echo "You don't have any Android Virtual Devices. Please create at least one AVD."
-        echo "android"
-    fi
-    # User has only one AVD
-    if [ ${#avd_list[@]} == 1 ]
-    then
-        emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[0]} 1> /dev/null 2>&1 &
-    # User has more than 1 AVD
-    elif [ ${#avd_list[@]} -gt 1 ]
-    then
-        while [ -z ${avd_list[$avd_id]} ]
-        do
-            echo "Choose from one of the following Android Virtual Devices [0 to $((${#avd_list[@]}-1))]:"
-            for(( i = 0 ; i < ${#avd_list[@]} ; i++ ))
-            do
-                echo "$i) ${avd_list[$i]}"
-            done
-            read -t 5 -p "> " avd_id
-            # default value if input timeout
-            if [ $avd_id -eq 1000 ] ; then avd_id=0 ; fi
-        done
-        emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
-    fi
-    
-}
-
-function clean {
-    ant clean
-}
-# has to be used independently and not in conjunction with other commands
-function log {
-    adb logcat
-}
-
-function run {
-    clean && emulate && wait_for_device && install && launch 
-}
-
-function install {
-    
-    declare -a devices=($(adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device | cut -f 1))
-    local device_id="1000" #FIXME: hopefully user does not have 1000 AVDs
-    
-    if [ ${#devices[@]} == 0 ]
-    then
-        # should not reach here. Emulator should launch or device should be attached
-        echo "Emulator not running or device not attached. Could not install debug package"
-        exit 70
-    fi
-    
-    if [ ${#devices[@]} == 1 ]
-    then
-        export ANDROID_SERIAL=${devices[0]}
-    # User has more than 1 AVD
-    elif [ ${#devices[@]} -gt 1 ]
-    then
-        while [ -z ${devices[$device_id]} ]
-        do
-            echo "Choose from one of the following devices/emulators [0 to $((${#devices[@]}-1))]:"
-            for(( i = 0 ; i < ${#devices[@]} ; i++ ))
-            do
-                echo "$i) ${devices[$i]}"
-            done
-            read -t 5 -p "> " device_id
-            # default value if input timeout
-            if [ $device_id -eq 1000 ] ; then device_id=0 ; fi
-        done
-        export ANDROID_SERIAL=${devices[$device_id]}
-    fi
-
-    ant debug install
-}
-
-function build {
-    ant debug
-}
-
-function release {
-    ant release
-}
-
-function wait_for_device {
-    local i="0"
-    echo -n "Waiting for device..."
-
-    while [ $i -lt 300 ]
-    do
-        if [ $(check_devices) -eq 0 ]
-        then
-            break
-        else
-            sleep 1
-            i=$[i+1]
-            echo -n "."
-        fi
-    done
-    # Device timeout: emulator has not started in time or device not attached
-    if [ $i -eq 300 ]
-    then
-        echo "device timeout!"
-        exit 69
-    else
-        echo "connected!"
-    fi
-}
-
-function launch {
-    local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
-    adb shell am start -n $launch_str 
-}
-
-# TODO parse arguments
-(cd "$PROJECT_PATH" && $1)
diff --git a/lib/cordova-android/bin/templates/cordova/cordova.bat b/lib/cordova-android/bin/templates/cordova/cordova.bat
index 22c289a..9b56199 100644
--- a/lib/cordova-android/bin/templates/cordova/cordova.bat
+++ b/lib/cordova-android/bin/templates/cordova/cordova.bat
@@ -1,3 +1,5 @@
+@ECHO OFF
+GOTO BEGIN
 :: 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
@@ -14,14 +16,13 @@
 :: KIND, either express or implied.  See the License for the
 :: specific language governing permissions and limitations
 :: under the License.
-
-@ECHO OFF
+:BEGIN
 IF NOT DEFINED JAVA_HOME GOTO MISSING
 FOR %%X in (java.exe ant.bat android.bat) do (
     SET FOUND=%%~$PATH:X
     IF NOT DEFINED FOUND GOTO MISSING
 )
-cscript %~dp0\cordova.js %*
+cscript %~dp0\lib\cordova.js %* //nologo
 GOTO END
 :MISSING
 ECHO Missing one of the following:
diff --git a/lib/cordova-android/bin/templates/cordova/cordova.js b/lib/cordova-android/bin/templates/cordova/cordova.js
deleted file mode 100644
index 51533cb..0000000
--- a/lib/cordova-android/bin/templates/cordova/cordova.js
+++ /dev/null
@@ -1,137 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-// 
-// http://www.apache.org/licenses/LICENSE-2.0
-// 
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-var ROOT = WScript.ScriptFullName.split('\\cordova\\cordova.js').join(''),
-    shell=WScript.CreateObject("WScript.Shell");
-
-function exec(command) {
-    var oExec=shell.Exec(command);
-    var output = new String();
-    while(oExec.Status == 0) {
-        if(!oExec.StdOut.AtEndOfStream) {
-            var line = oExec.StdOut.ReadLine();
-            // XXX: Change to verbose mode 
-            // WScript.StdOut.WriteLine(line);
-            output += line;
-        }
-        WScript.sleep(100);
-    }
-
-    return output;
-}
-
-function device_running() {
-    var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
-    if(local_devices.match(/\w+\tdevice/)) {
-        WScript.Echo("Yes");
-        return true;
-    }
-    WScript.Echo("No");
-    return false;
-}
-function emulate() {
-    // don't run emulator if a device is plugged in or if emulator is already running
-    if(device_running()) {
-        //WScript.Echo("Device or Emulator already running!");
-        return;
-    }
-    var oExec = shell.Exec("%comspec% /c android.bat list avd");
-    var avd_list = [];
-    var avd_id = -10;
-    while(!oExec.StdOut.AtEndOfStream) {
-        var output = oExec.StdOut.ReadLine();
-        if(output.match(/Name: (.)*/)) {
-            avd_list.push(output.replace(/ *Name:\s/, ""));
-        }
-    }
-    // user has no AVDs
-    if(avd_list.length == 0) {
-        WScript.Echo("You don't have any Android Virtual Devices. Please create at least one AVD.");
-        WScript.Echo("android");
-        WScript.Quit(1);
-    }
-    // user has only one AVD so we launch that one
-    if(avd_list.length == 1) {
-
-        shell.Run("emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd "+avd_list[0]);
-    }
-
-    // user has more than one avd so we ask them to choose
-    if(avd_list.length > 1) {
-        while(!avd_list[avd_id]) {
-            WScript.Echo("Choose from one of the following Android Virtual Devices [0 to "+(avd_list.length - 1)+"]:")
-            for(i = 0, j = avd_list.length ; i < j ; i++) {
-                WScript.Echo((i)+") "+avd_list[i]);
-            }
-            WScript.StdOut.Write("> ");
-            avd_id = new Number(WScript.StdIn.ReadLine());
-        }
-
-        shell.Run("emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\\cache -avd "+avd_list[avd_id], 0, false);
-    }
-}
-
-function clean() {
-    WScript.Echo("Cleaning project...");
-    exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
-}
-
-function build() {
-    WScript.Echo("Building project...");
-    exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
-}
-
-function install() {
-    WScript.Echo("Building/Installing project...");
-    exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
-}
-
-function log() {
-    shell.Run("%comspec% /c adb logcat");
-}
-
-function launch() {
-    WScript.Echo("Launching app...");
-    var launch_str=exec("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
-    //WScript.Echo(launch_str);
-    exec("%comspec% /c adb shell am start -n "+launch_str+" 2>&1");
-}
-
-function run() {
-    var i=0;
-   clean();
-   emulate();
-   WScript.Stdout.Write('Waiting for device...');
-   while(!device_running() && i < 300) {
-        WScript.Stdout.Write('.');
-        WScript.sleep(1000);
-        i += 1;
-   }
-   if(i == 300) {
-       WScript.Stderr.WriteLine("device/emulator timeout!"); 
-   } else {
-       WScript.Stdout.WriteLine("connected!");
-   }
-   install();
-   launch();
-}
-var args = WScript.Arguments;
-if(args.count() != 1) {
-    WScript.StdErr.Write("An error has occured!\n");
-    WScript.Quit(1);
-}
-eval(args(0)+"()");
diff --git a/lib/cordova-android/bin/templates/cordova/lib/cordova b/lib/cordova-android/bin/templates/cordova/lib/cordova
new file mode 100755
index 0000000..294df49
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/cordova
@@ -0,0 +1,386 @@
+#!/bin/bash
+#   Licensed to the Apache Software Foundation (ASF) under one
+#   or more contributor license agreements.  See the NOTICE file
+#   distributed with this work for additional information
+#   regarding copyright ownership.  The ASF licenses this file
+#   to you under the Apache License, Version 2.0 (the
+#   "License"); you may not use this file except in compliance
+#   with the License.  You may obtain a copy of the License at
+# 
+#   http://www.apache.org/licenses/LICENSE-2.0
+# 
+#   Unless required by applicable law or agreed to in writing,
+#   software distributed under the License is distributed on an
+#   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#   KIND, either express or implied.  See the License for the
+#   specific language governing permissions and limitations
+#   under the License.
+
+PROJECT_PATH=$( cd "$( dirname "$0" )/../.." && pwd )
+
+function list_devices {
+    IFS=$'\n'
+    devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
+    device_list=($devices)
+    if [[ ${#device_list[@]} > 0 ]] ; then
+        for i in ${devices[@]}
+        do
+            # remove space and 'device'
+            echo ${i/[^a-zA-Z0-9._]device/}
+        done
+    else
+        echo "No devices found."
+        exit 2
+    fi
+}
+
+function list_started_emulators {
+    IFS=$'\n'
+    devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+    emulator_list=($devices)
+    if [[ ${#emulator_list[@]} > 0 ]] ; then
+        for i in ${emulator_list[@]}
+        do
+            # remove space and 'device'
+            echo ${i/[^a-zA-Z0-9._]device/}
+        done
+    else
+        echo "No started emulators found, you can start an emulator by using the command"
+        echo " 'cordova/lib/start-emulator'"
+        exit 2
+    fi
+}
+
+function list_emulator_images {
+    emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
+    emulator_list=($emulator_images)
+    if [[ ${#emulator_list[@]} > 0 ]] ; then
+        for i in ${emulator_list[@]}
+        do
+            echo ${i/[^a-zA-Z0-9._]/}
+        done
+    else
+        echo "No emulators found, if you would like to create an emulator follow the instructions"
+        echo " provided here : http://developer.android.com/tools/devices/index.html"
+        echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
+        exit 2
+    fi
+}
+
+function start_emulator {
+    emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
+    # if target emulator is provided
+    if [[ "$#" -eq 1 ]] ; then
+        # check that it exists
+        if [[ $emulator_images =~ $1 ]] ; then
+            #xterm -e emulator -avd $1 &
+            emulator -avd $1 1> /dev/null 2>&1 &
+        else
+            echo "Could not find the provided emulator, make sure the emulator exists"
+            echo " by checking 'cordova/lib/list-emulator-images'"
+            exit 2
+        fi
+    else
+        # start first emulator
+        emulator_list=($emulator_images)
+        if [[ ${#emulator_list[@]} > 0 ]] ; then
+            #xterm -e emulator -avd ${emulator_list[0]} &
+            emulator -avd ${emulator_list[0]/[^a-zA-Z0-9._]/} 1> /dev/null 2>&1 &
+        else
+            echo "No emulators found, if you would like to create an emulator follow the instructions"
+            echo " provided here : http://developer.android.com/tools/devices/index.html"
+            echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
+            exit 2
+        fi
+    fi
+}
+
+function install_device {
+    IFS=$'\n'
+    devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
+    device_list=($devices)
+    if [[ ${#device_list[@]} > 0 ]] ; then
+        apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
+        apk_list=($apks)
+        if [[ ${#apk_list[@]} > 0 ]] ; then
+            local target
+            # handle target emulator
+            if [[ "$#" -eq 1 ]] ; then
+                # deploy to given target
+                target=${1/--target=/}
+            else
+                # delete trailing space and 'device' after device ID
+                target=${device_list[0]/[^a-zA-Z0-9._]device/}
+            fi
+            echo "Installing ${apk_list[0]} onto device $target..."
+            adb -s $target install -r ${apk_list[0]};
+            echo "Launching application..."
+            local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
+            adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
+        else
+            echo "Application package not found, could not install to device"
+            echo " make sure your application is built before deploying."
+            exit 2
+        fi
+    else
+        echo "No devices found to deploy to. Please make sure your device is connected"
+        echo " and you can view it using the 'cordova/lib/list-devices' command."
+        exit 2
+    fi
+}
+
+function install_emulator {
+    IFS=$'\n'
+    # check that there is an emulator to deploy to
+    emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'emulator'`
+    emulator_list=($emulator_string)
+    if [[ ${#emulator_list[@]} > 0 ]] ; then
+        apks=`find $PROJECT_PATH/bin -type f -maxdepth 1 | egrep '\.apk$'`
+        apk_list=($apks)
+        if [[ ${#apk_list[@]} > 0 ]] ; then
+            local target
+            # handle target emulator
+            if [[ "$#" -eq 1 ]] ; then
+                # deploy to given target
+                target=${1/--target=/}
+            else
+                # delete trailing space and 'device' after emulator ID
+                target=${emulator_list[0]/[^a-zA-Z0-9._]device/}
+            fi
+            echo "Installing ${apk_list[0]} onto $target..."
+            adb -s $target install -r ${apk_list[0]};
+            echo "Launching application..."
+            local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
+            adb -s $target shell am start -W -a android.intent.action.MAIN -n $launch_str
+
+        else
+            echo "Application package not found, could not install to device"
+            echo " make sure your application is built before deploying."
+            exit 2
+        fi
+    else
+        echo "No emulators found to deploy to. Please make sure your emulator is started"
+        echo " and you can view it using the 'cordova/lib/list-started-emulators' command."
+        exit 2
+    fi
+}
+
+# cleans the project
+function clean {
+    echo "Cleaning project..."
+    ant clean
+}
+
+# has to be used independently and not in conjunction with other commands
+function log {
+    # filter out nativeGetEnabledTags spam from latest sdk bug.
+    adb logcat | grep -v nativeGetEnabledTags
+}
+
+
+function build {
+    if [[ "$#" -eq 1 ]] ; then
+        if [[ $1 == "--debug" ]] ; then
+            clean
+            ant debug -f "$PROJECT_PATH"/build.xml
+        elif [[ $1 == "--release" ]] ; then
+            clean
+            ant release -f "$PROJECT_PATH"/build.xml
+        elif [[ $1 == "--nobuild" ]] ; then
+            echo "Skipping build..."
+        else
+            echo "Error : Build command '$1' not recognized."
+            exit 2
+        fi
+    else
+        echo "Warning : [ --debug | --release | --nobuild ] not specified, defaulting to --debug"
+        clean
+        ant debug -f "$PROJECT_PATH"/build.xml
+    fi
+}
+
+
+function wait_for_emulator {
+    emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+    old_started=($emulator_string)
+    local new_started
+    local new_emulator_name
+    local i="0"
+    echo -n "Waiting for emulator..."
+    while [ $i -lt 300 ]
+    do
+        emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+        new_started=($emulator_string)
+        if [[ ${#new_started[@]} > ${#old_started[@]} && -z "$new_emulator_name" ]] ; then
+            # get the name of the started emulator
+            local count="0"
+            if [[ ${#old_started[@]} == 0 ]] ; then
+                new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
+            else
+                for count in {0...${#old_started[@]}}
+                do
+                    if [[ ! ${new_started[$count]} == ${old_started[$count]} ]] ; then
+                        new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
+                    fi
+                done
+                if [[ -z "$new_emulator_name" ]] ; then
+                    count=$[count+1]
+                    new_emulator_name=${new_started[$count]/[^a-zA-Z0-9._]device/}
+                fi
+            fi
+        elif [[ "$new_emulator_name" ]] ; then
+            boot_anim=`adb -s $new_emulator_name shell getprop init.svc.bootanim`
+            if [[ $boot_anim =~ "stopped" ]] ; then
+                break
+            else
+                sleep 1
+                i=$[i+1]
+                echo -n "."
+            fi
+        else
+            sleep 1
+            i=$[i+1]
+            echo -n "."
+        fi
+    done
+    # Device timeout: emulator has not started in time
+    if [ $i -eq 300 ]
+    then
+        echo "emulator timeout!"
+        exit 69
+    else
+        echo "connected!"
+    fi
+}
+
+function run {
+    IFS=$'\n'
+    if [[ "$#" -eq 2 ]] ; then
+        build $2
+        if [[ $1 == "--device" ]] ; then
+            install_device
+        elif [[ $1 == "--emulator" ]] ; then
+            install_emulator
+        elif [[ $1 =~ "--target=" ]]; then
+            install_device $1
+        else
+            echo "Error : '$1' is not recognized as an install option"
+        fi
+    elif [[ "$#" -eq 1 ]] ; then
+        if [[ $1 == "--debug" || $1 == "--release" || $1 == "--nobuild" ]] ; then
+            build $1
+        elif [[ $1 == "--device" ]] ; then
+            install_device
+        elif [[ $1 == "--emulator" ]] ; then
+            install_emulator
+        elif [[ $1 =~ "--target=" ]]; then
+            install_device $1
+        else
+            echo "Error : '$1' is not recognized as an install option"
+        fi
+    else
+        echo "Warning : [ --device | --emulate | --target=<targetID> ] not specified, using defaults."
+        build
+        devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep -v 'emulator'`
+        device_list=($devices)
+        emulator_string=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep 'device' | grep 'emulator'`
+        emulator_list=($emulator_string)
+        if [[ ${#device_list[@]} > 0 ]] ; then
+            install_device
+        elif [[ ${#emulator_list[@]} > 0 ]] ; then
+            install_emulator
+        else
+            emulator_images=`android list avds | grep "Name:" | cut -f 2 -d ":"`
+            echo $emulator_images
+            emulator_image_list=($emulator_images)
+            if [[ ${#emulator_image_list[@]} > 0 ]] ; then
+                echo "Starting emulator : ${emulator_image_list[0]}" 
+                emulator -avd ${emulator_image_list[0]/[^.\w]/} 1> /dev/null 2>&1 &
+                wait_for_emulator
+                install_emulator
+            else
+                # TODO : look for emulator images and start one if it's availible
+                echo "Error : there are no availible devices or emulators to deploy to."
+                echo " create an emulator or connect your device to run this command."
+                echo "If you would like to create an emulator follow the instructions"
+                echo " provided here : http://developer.android.com/tools/devices/index.html"
+                echo " Or run 'android create avd --name <name> --target <targetID>' in on the command line."
+                exit 2
+            fi
+        fi
+    fi
+}
+
+# parse command line arguments
+
+if [[ $# > 3 ]] ; then 
+    echo "Error :  too many arguments."
+    exit 2
+elif [[ $# == 3 ]] ; then
+    if [[ $1 == "run" ]] ; then
+        run $2 $3
+    else
+        echo "Error : too many arguments for '$1'"
+        exit 2
+    fi
+elif [[ $# == 2 ]] ; then
+    if [[ $1 == "run" ]] ; then
+        if [[ $2 == "--emulator" || $2 == "--device" || $2 =~ "--target=" ]] ; then
+            run $2 ''
+        elif [[ $2 == "--debug" || $2 == "--release" || $2 == "--nobuild" ]] ; then
+            run '' $2
+        else 
+            echo "Error : '$2' is not recognized as a run option."
+            exit 2
+        fi
+    elif [[ $1 == "build" ]] ; then
+        build $2
+    elif [[ $1 == "start-emulator" ]] ; then
+        start_emulator $2
+    elif [[ $1 == "install-device" ]] ; then
+        if [[ $2 =~ "--target=" ]] ; then
+            install_device $2
+        else
+            echo "Error : '$2' is not recognized as an install option"
+            exit 2
+        fi
+    elif [[ $1 == "install-emulator" ]] ; then
+        if [[ $2 =~ "--target=" ]] ; then
+            install_emulator $2
+        else
+            echo "Error : '$2' is not recognized as an install option"
+            exit 2
+        fi
+    else
+        echo "Error : '$1' is not recognized as an option that takes arguments"
+        exit 2
+    fi
+elif [[ $# == 1 ]] ; then
+    if [[ $1 == "run" ]] ; then
+        run
+    elif [[ $1 == "build" ]]; then
+        build
+    elif [[ $1 == "clean" ]]; then
+        clean
+    elif [[ $1 == "log" ]]; then
+        log
+    elif [[ $1 == "list-devices" ]]; then
+        list_devices
+    elif [[ $1 == "list-emulator-images" ]]; then
+        list_emulator_images
+    elif [[ $1 == "list-started-emulators" ]]; then
+        list_started_emulators
+    elif [[ $1 == "install-device" ]]; then
+        install_device
+    elif [[ $1 == "install-emulator" ]]; then
+        install_emulator
+    elif [[ $1 == "start-emulator" ]]; then
+        start_emulator
+    else
+        echo "Error : '$1' is not recognized as a tooling command."
+        exit 2
+    fi
+else
+    echo "Error : No command recieved, exiting..."
+    exit 2
+fi
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/cordova.js b/lib/cordova-android/bin/templates/cordova/lib/cordova.js
new file mode 100644
index 0000000..28f9b3e
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/cordova.js
@@ -0,0 +1,593 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+// 
+// http://www.apache.org/licenses/LICENSE-2.0
+// 
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+var ROOT  = WScript.ScriptFullName.split('\\cordova\\lib\\cordova.js').join(''),
+    shell = WScript.CreateObject("WScript.Shell"),
+    fso   = WScript.CreateObject('Scripting.FileSystemObject');
+
+
+// log to stdout or stderr
+function Log(msg, error) {
+    if (error) {
+        WScript.StdErr.WriteLine(msg);
+    }
+    else {
+        WScript.StdOut.WriteLine(msg);
+    }
+} 
+
+// executes a commmand in the shell, returning stdout
+function exec(command) {
+    var oExec=shell.Exec(command);
+    var output = new String();
+    while (oExec.Status == 0) {
+        if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadLine();
+            output += line;
+        }
+        WScript.sleep(100);
+    }
+    return output;
+}
+
+// executes a command in the shell, returns stdout or stderr if error
+function exec_out(command) {
+    var oExec=shell.Exec(command);
+    var output = new String();
+    while (oExec.Status == 0) {
+        if (!oExec.StdOut.AtEndOfStream) {
+            var line = oExec.StdOut.ReadLine();
+            // XXX: Change to verbose mode 
+            // WScript.StdOut.WriteLine(line);
+            output += line;
+        }
+        WScript.sleep(100);
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oExec.StdErr.AtEndOfStream) {
+        var line = oExec.StdErr.ReadAll();
+        return {'error' : true, 'output' : line};
+    }
+    return {'error' : false, 'output' : output};
+}
+
+// executes a commmand in the shell and outputs stdout and fails on stderr
+function exec_verbose(command) {
+    //Log("Command: " + command);
+    var oShell=shell.Exec(command);
+    while (oShell.Status == 0) {
+        //Wait a little bit so we're not super looping
+        WScript.sleep(100);
+        //Print any stdout output from the script
+        if (!oShell.StdOut.AtEndOfStream) {
+            var line = oShell.StdOut.ReadLine();
+            Log(line);
+        }
+    }
+    //Check to make sure our scripts did not encounter an error
+    if (!oShell.StdErr.AtEndOfStream) {
+        var line = oShell.StdErr.ReadAll();
+        Log(line, true);
+        WScript.Quit(2);
+    }
+}
+
+function get_devices() {
+    var device_list = []
+    var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
+    if (local_devices.match(/\w+\tdevice/)) {
+        devices = local_devices.split('\r\n');
+        //format (ID DESCRIPTION)
+        for (i in devices) {
+            if (devices[i].match(/\w+\tdevice/) && !devices[i].match(/emulator/)) {
+                device_list.push(devices[i].replace(/\t/, ' '));
+            }
+        }
+    }
+    return device_list
+}
+
+function list_devices() {
+    var devices = get_devices();
+    if (devices.length > 0) {
+        for (i in devices) {
+            Log(devices[i]);
+        }
+    }
+    else {
+        Log('No devices found, if your device is connected and not showing,');
+        Log(' then try and install the drivers for your device.');
+        Log(' http://developer.android.com/tools/extras/oem-usb.html');
+    }
+
+}
+
+function get_emulator_images() {
+    // discription contains all data recieved squashed onto one line
+    var add_description = true;
+    var avd_list = [];
+    var local_emulators = shell.Exec("%comspec% /c android list avds").StdOut.ReadAll();
+    if (local_emulators.match(/Name\:/)) {
+        emulators = local_emulators.split('\n');
+        //format (ID DESCRIPTION)
+        var count = 0;
+        var output = '';
+        for (i in emulators) {
+            if (emulators[i].match(/Name\:/)) {
+                var emulator_name = emulators[i].replace(/\s*Name\:\s/, '') + ' ';
+                if (add_description) {
+                    count = 1;
+                    output += emulator_name
+                }
+                else {
+                    avd_list.push(emulator_name);
+                }
+            }
+            // add description if indicated (all data squeezed onto one line)
+            if (count > 0) {
+                var emulator_description = emulators[i].replace(/\s*/g, '');
+                if (count > 4) {
+                    avd_list.push(output + emulator_description);
+                    count = 0;
+                    output = '';
+                }
+                else {
+                    count++;
+                    output += emulator_description + ' '
+                }
+            }
+        }
+    }
+    return avd_list;
+}
+
+function list_emulator_images() {
+    var images = get_emulator_images();
+    if (images.length > 0) {
+        for(i in images) {
+            Log(images[i]);
+        }
+    }
+    else {
+        Log('No emulators found, if you would like to create an emulator follow the instructions');
+        Log(' provided here : http://developer.android.com/tools/devices/index.html');
+        Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.');
+    }
+}
+
+function get_started_emulators() {
+    var started_emulators = [];
+    var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
+    if (local_devices.match(/emulator/)) {
+        devices = local_devices.split('\r\n');
+        //format (ID DESCRIPTION)
+        for (i in devices) {
+            if (devices[i].match(/\w+\tdevice/) && devices[i].match(/emulator/)) {
+                started_emulators.push(devices[i].replace(/\t/, ' '));
+            }
+        }
+    }
+    return started_emulators
+}
+
+function list_started_emulators() {
+    var images = get_started_emulators();
+    if (images.length > 0) {
+        for(i in images) {
+            Log(images[i]);
+        }
+    }
+    else {
+        Log('No started emulators found, if you would like to start an emulator call \'list-emulator-images\'');
+        Log(' to get the name of an emulator and then start the emulator with \'start-emulator <Name>\'');
+    }
+}
+
+function start_emulator(name) {  
+    var emulators = get_emulator_images();
+    var started_emulators = get_started_emulators();
+    var num_started = started_emulators.length;
+    var emulator_name;
+    var started = false;
+    if (name) {
+        for (i in emulators) {
+            if (emulators[i].substr(0,name.length) == name) {
+                Log("Starting emulator : " + name);
+                shell.Run("%comspec% /c start cmd /c emulator -avd " + name);
+                //shell.Run("%comspec% /c start cmd /c emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd " + name);
+                started = true;
+            }
+        }
+    }
+    else {
+        if (emulators.length > 0 && started_emulators < 1) {
+            emulator_name = emulators[0].split(' ', 1)[0];
+            start_emulator(emulator_name);
+            return;
+        } else if (started_emulators.length > 0) {
+            Log("Emulator already started : " + started_emulators[0].split(' ', 1));
+            return;
+        } else {
+            Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
+            WScript.Quit(2);
+        }
+    }
+    if (!started) {
+        Log("Error : unable to start emulator, ensure you have emulators availible by checking \'list-emulator-images\'", true);
+        WScript.Quit(2);
+    }
+    else { // wait for emulator to boot before returning
+        WScript.Stdout.Write('Booting up emulator..');
+        var boot_anim = null;
+        var emulator_ID = null;
+        var new_started = get_started_emulators();
+        var i = 0;
+        // use boot animation property to tell when boot is complete.
+        while ((boot_anim == null || !boot_anim.output.match(/stopped/)) && i < 100) {
+            if (new_started.length > started_emulators.length && emulator_ID == null) {
+                // find new emulator that was just started to get it's ID
+                for(var i = 0; i < new_started.length; i++) {
+                    if (new_started[i] != started_emulators[i]) {
+                        emulator_ID = new_started[i].split(' ', 1)[0];
+                        boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim');
+                        break;
+                    }
+                }
+            }
+            else if (boot_anim == null) {
+                new_started = get_started_emulators(); 
+            }
+            else {
+                boot_anim = exec_out('%comspec% /c adb -s ' + emulator_ID + ' shell getprop init.svc.bootanim'); 
+            }
+            i++;
+            WScript.Stdout.Write('.');
+            WScript.Sleep(2000);
+        }
+        if (i < 100) {
+            Log('\nBoot Complete!');
+        } else {
+             Log('\nEmulator boot timed out. Failed to load emulator');
+             WScript.Quit(2);
+        }
+    }
+}
+
+function install_device(target) {
+    var devices = get_devices();
+    var use_target = false;
+    if (devices.length < 1) {
+        Log("Error : No devices found to install to, make sure there are devices", true);
+        Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-devices\'", true);
+        WScript.Quit(2);
+    }
+    if (target) {
+        var exists = false;
+        for (i in devices) {
+            if (devices[i].substr(0,target.length) == target)
+            {
+                exists = true;
+                break;
+            }
+        }
+        if (!exists) {
+            Log("Error : Unable to find target " + target, true);
+            Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-devices'");
+            WScript.Quit(2);
+        }
+        use_target = true;
+    }
+    // check if file .apk has been created
+    if (fso.FolderExists(ROOT + '\\bin')) {
+        var path_to_apk;
+        var out_folder = fso.GetFolder(ROOT + '\\bin');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            var path = out_files.item() + '';
+            if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
+                path_to_apk = out_files.item();
+                break;
+            }
+        }
+        if (path_to_apk) {
+            var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
+            if (launch_name.error) {
+                Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
+                Log("Output : " + launch_name.output, true);
+                WScript.Quit(2);
+            }
+            // install on device (-d)
+            Log("Installing app on device...");
+            var cmd;
+            if (use_target) {
+                cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
+            } else {
+                cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' install -r ' + path_to_apk;
+            }
+            var install = exec_out(cmd);
+            if ( install.error && install.output.match(/Failure/)) {
+                Log("Error : Could not install apk to device : ", true);
+                Log(install.output, true);
+                WScript.Quit(2);
+            }
+            else {
+                Log(install.output);
+            }
+            // run on device
+            Log("Launching application...");
+            cmd;
+            if (use_target) {
+                cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
+            } else {
+                cmd = '%comspec% /c adb -s ' + devices[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
+            }
+            exec_verbose(cmd);
+        }
+        else {
+            Log('Failed to find apk, make sure you project is built and there is an ', true);
+            Log(' apk in <project>\\bin\\.  To build your project use \'<project>\\cordova\\build\'', true);
+            WScript.Quit(2);
+        }
+    }
+}
+
+function install_emulator(target) {
+    var emulators = get_started_emulators();
+    var use_target = false;
+    if (emulators.length < 1) {
+        Log("Error : No emulators found to install to, make sure there are emulators", true);
+        Log(" availible by checking \'<project_dir>\\cordova\\lib\\list-started-emulators\'", true);
+        WScript.Quit(2);
+    }
+    if (target) {
+        var exists = false;
+        for (i in emulators) {
+            if (emulators[i].substr(0,target.length) == target)
+            {
+                exists = true;
+                break;
+            }
+        }
+        if (!exists) {
+            Log("Error : Unable to find target " + target, true);
+            Log("Please ensure the target exists by checking \'<project>\\cordova\\lib\\list-started-emulators'")
+        }
+        use_target = true;
+    } else {
+        target = emulators[0].split(' ', 1)[0];
+        Log("Deploying to emulator : " + target);
+    }
+    // check if file .apk has been created
+    if (fso.FolderExists(ROOT + '\\bin')) {
+        var path_to_apk;
+        var out_folder = fso.GetFolder(ROOT + '\\bin');
+        var out_files = new Enumerator(out_folder.Files);
+        for (;!out_files.atEnd(); out_files.moveNext()) {
+            var path = out_files.item() + '';
+            if (fso.GetExtensionName(path) == 'apk' && !path.match(/unaligned/)) {
+                path_to_apk = out_files.item();
+                break;
+            }
+        }
+        if (path_to_apk) {
+            var launch_name = exec_out("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
+            if (launch_name.error) {
+                Log("Failed to get application name from appinfo.jar + AndroidManifest : ", true);
+                Log("Output : " + launch_name.output, true);
+                WScript.Quit(2);
+            }
+            // install on emulator (-e)
+            Log("Installing app on emulator...");
+            var cmd = '%comspec% /c adb -s ' + target + ' install -r ' + path_to_apk;
+            var install = exec_out(cmd);
+            if ( install.error && install.output.match(/Failure/)) {
+                Log("Error : Could not install apk to emulator : ", true);
+                Log(install.output, true);
+                WScript.Quit(2);
+            }
+            else {
+                Log(install.output);
+            }
+            // run on emulator
+            Log("Launching application...");
+            cmd;
+            if (use_target) {
+                cmd = '%comspec% /c adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output;
+            } else {
+                cmd = '%comspec% /c adb -s ' + emulators[0].split(' ', 1)[0] + ' shell am start -W -a android.intent.action.MAIN -n ' + launch_name.output
+            }
+            exec_verbose(cmd);
+        }
+        else {
+            Log('Failed to find apk, make sure you project is built and there is an ', true);
+            Log(' apk in <project>\\bin\\.  To build your project use \'<project>\\cordova\\build\'', true);
+            WScript.Quit(2);
+        }
+    }
+    else {
+        Log('Failed to find apk, make sure you project is built and there is an ', true);
+        Log(' apk in <project>\\bin\\.  To build your project use \'<project>\\cordova\\build\'', true);
+        WScript.Quit(2);
+    }
+}
+
+function clean() {
+    Log("Cleaning project...");
+    exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
+}
+
+function build(build_type) {
+    if (build_type) {
+        switch (build_type) {
+            case "--debug" :
+                clean();
+                Log("Building project...");
+                exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
+                break;
+            case "--release" :
+                clean();
+                Log("Building project...");
+                exec_verbose("%comspec% /c ant.bat release -f "+ROOT+"\\build.xml 2>&1");
+                break;
+            case "--nobuild" :
+                Log("Skipping build process.");
+                break;
+            default :
+                Log("Build option not recognized: " + build_type, true);
+                WScript.Quit(2);
+                break;
+        }
+    }
+    else {
+        Log("WARNING: [ --debug | --release | --nobuild ] not specified, defaulting to --debug.");
+        exec_verbose("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
+    }
+}
+
+function log() {
+    // filter out nativeGetEnabledTags spam from latest sdk bug.
+    shell.Run("%comspec% /c adb logcat | grep -v nativeGetEnabledTags");
+}
+
+function run(target, build_type) {
+    var use_target = false;
+    if (!target) {
+        Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
+    }
+    // build application
+    build(build_type);
+    // attempt to deploy to connected device 
+    var devices = get_devices();
+    if (devices.length > 0 || target == "--device") {
+        if (target) {
+            if (target.substr(0,9) == "--target=") {
+                install_device(target.split('--target=').join(''))
+            } else if (target == "--device") {
+                install_device();
+            } else {
+                Log("Did not regognize " + target + " as a run option.", true);
+                WScript.Quit(2);
+            }
+        }
+        else {
+            Log("WARNING: [ --target=<ID> | --emulator | --device ] not specified, using defaults");
+            install_device();
+        }
+    }
+    else {
+        var emulators = get_started_emulators();
+        if (emulators.length > 0) {
+            install_emulator();
+        }
+        else {
+            var emulator_images = get_emulator_images();
+            if (emulator_images.length < 1) {
+                Log('No emulators found, if you would like to create an emulator follow the instructions', true);
+                Log(' provided here : http://developer.android.com/tools/devices/index.html', true);
+                Log(' Or run \'android create avd --name <name> --target <targetID>\' in on the command line.', true);
+                WScript.Quit(2);
+            }
+            start_emulator(emulator_images[0].split(' ')[0]);
+            emulators = get_started_emulators();
+            if (emulators.length > 0) {
+                install_emulator();
+            }
+            else {
+                Log("Error : emulator failed to start.", true);
+                WScript.Quit(2);
+            }
+        }
+    }
+}
+
+var args = WScript.Arguments;
+if (args.count() == 0) {
+    Log("Error: no args provided.");
+    WScript.Quit(2);
+}
+else {
+    if (args(0) == "build") {
+        if (args.Count() > 1) {
+            build(args(1))
+        } else {
+            build();
+        }
+    } else if (args(0) == "clean") {
+        clean();
+    } else if (args(0) == "list-devices") {
+        list_devices();
+    } else if (args(0) == "list-emulator-images") {
+        list_emulator_images();
+    } else if (args(0) == "list-started-emulators") {
+        list_started_emulators();
+    } else if (args(0) == "start-emulator") {
+        if (args.Count() > 1) {
+            start_emulator(args(1))
+        } else {
+            start_emulator();
+        }
+    } else if (args(0) == "log") {
+        log();
+    } else if (args(0) == "install-emulator") {
+        if (args.Count() == 2) {
+            if (args(1).substr(0,9) == "--target=") {
+                install_emulator(args(1).split('--target=').join(''));
+            } else {
+                Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
+                WScript.Quit(2);
+            }
+        } else {
+            install_emulator();
+        }
+    } else if (args(0) == "install-device") {
+        if (args.Count() == 2) {
+            if (args(1).substr(0,9) == "--target=") {
+                install_device(args(1).split('--target=').join(''));
+            } else {
+                Log('Error: \"' + args(1) + '\" is not recognized as an install option', true);
+                WScript.Quit(2);
+            }
+        } else {
+            install_device();
+        }
+    } else if (args(0) == "run") {
+        if (args.Count() == 3) {
+            run(args(1), args(2));
+        }
+        else if (args.Count() == 2) {
+            if (args(1).substr(0,9) == "--target=" ||
+               args(1) == "--emulator" ||
+               args(1) == "--device") {
+                run(args(1));
+            } else if (args(1) == "--debug" ||
+                       args(1) == "--release" ||
+                       args(1) == "--nobuild") {
+                run(null, args(1))
+            } else {
+                Log('Error: \"' + args(1) + '\" is not recognized as a run option', true);
+                WScript.Quit(2);
+            }
+        }
+        else {
+            run();
+        }
+    } else {
+        Log('Error: \"' + args(0) + '\" is not recognized as a tooling command', true);
+        WScript.Quit(2);
+    }
+}
+
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/lib/install-device
similarity index 87%
copy from lib/cordova-android/bin/templates/cordova/release
copy to lib/cordova-android/bin/templates/cordova/lib/install-device
index 73d873e..604b5ae 100755
--- a/lib/cordova-android/bin/templates/cordova/release
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-device
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova release
+bash "$CORDOVA_LIB_PATH"/cordova install-device "$@"
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/install-device.bat b/lib/cordova-android/bin/templates/cordova/lib/install-device.bat
new file mode 100644
index 0000000..52d9775
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-device.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" install-device %* //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/lib/install-emulator
similarity index 87%
copy from lib/cordova-android/bin/templates/cordova/release
copy to lib/cordova-android/bin/templates/cordova/lib/install-emulator
index 73d873e..105e2ee 100755
--- a/lib/cordova-android/bin/templates/cordova/release
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-emulator
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova release
+bash "$CORDOVA_LIB_PATH"/cordova install-emulator "$@"
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat b/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat
new file mode 100644
index 0000000..d11a7be
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/install-emulator.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" install-emulator %* //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/lib/list-devices
similarity index 88%
rename from lib/cordova-android/bin/templates/cordova/release
rename to lib/cordova-android/bin/templates/cordova/lib/list-devices
index 73d873e..7a5b2f5 100755
--- a/lib/cordova-android/bin/templates/cordova/release
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-devices
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova release
+bash "$CORDOVA_LIB_PATH"/cordova list-devices
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat b/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat
new file mode 100644
index 0000000..c146f10
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-devices.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" list-devices //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images
similarity index 87%
copy from lib/cordova-android/bin/templates/cordova/release
copy to lib/cordova-android/bin/templates/cordova/lib/list-emulator-images
index 73d873e..db8e563 100755
--- a/lib/cordova-android/bin/templates/cordova/release
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova release
+bash "$CORDOVA_LIB_PATH"/cordova list-emulator-images
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat
new file mode 100644
index 0000000..172520b
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-emulator-images.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" list-emulator-images //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators
similarity index 87%
copy from lib/cordova-android/bin/templates/cordova/release
copy to lib/cordova-android/bin/templates/cordova/lib/list-started-emulators
index 73d873e..7911763 100755
--- a/lib/cordova-android/bin/templates/cordova/release
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova release
+bash "$CORDOVA_LIB_PATH"/cordova list-started-emulators
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat
new file mode 100644
index 0000000..f1b3c5d
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/list-started-emulators.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" list-started-emulators //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/release b/lib/cordova-android/bin/templates/cordova/lib/start-emulator
similarity index 87%
copy from lib/cordova-android/bin/templates/cordova/release
copy to lib/cordova-android/bin/templates/cordova/lib/start-emulator
index 73d873e..8e8964d 100755
--- a/lib/cordova-android/bin/templates/cordova/release
+++ b/lib/cordova-android/bin/templates/cordova/lib/start-emulator
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
-CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
+CORDOVA_LIB_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova release
+bash "$CORDOVA_LIB_PATH"/cordova start-emulator "$@"
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat b/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat
new file mode 100644
index 0000000..4f3fb5d
--- /dev/null
+++ b/lib/cordova-android/bin/templates/cordova/lib/start-emulator.bat
@@ -0,0 +1,9 @@
+@ECHO OFF
+SET full_path=%~dp0
+IF EXIST %full_path%cordova.js (
+    cscript "%full_path%cordova.js" start-emulator %* //nologo
+) ELSE (
+    ECHO. 
+    ECHO ERROR: Could not find 'cordova.js' in cordova/lib, aborting...>&2
+    EXIT /B 1
+)
\ No newline at end of file
diff --git a/lib/cordova-android/bin/templates/cordova/log b/lib/cordova-android/bin/templates/cordova/log
index 087a200..01fe107 100755
--- a/lib/cordova-android/bin/templates/cordova/log
+++ b/lib/cordova-android/bin/templates/cordova/log
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )/.." && pwd )
 
-bash "$CORDOVA_PATH"/cordova/cordova log
+bash "$CORDOVA_PATH"/cordova/lib/cordova log "$@"
diff --git a/lib/cordova-android/bin/templates/cordova/log.bat b/lib/cordova-android/bin/templates/cordova/log.bat
index b8cc6be..2c492e7 100644
--- a/lib/cordova-android/bin/templates/cordova/log.bat
+++ b/lib/cordova-android/bin/templates/cordova/log.bat
@@ -1,18 +1,2 @@
-:: Licensed to the Apache Software Foundation (ASF) under one
-:: or more contributor license agreements.  See the NOTICE file
-:: distributed with this work for additional information
-:: regarding copyright ownership.  The ASF licenses this file
-:: to you under the Apache License, Version 2.0 (the
-:: "License"); you may not use this file except in compliance
-:: with the License.  You may obtain a copy of the License at
-:: 
-:: http://www.apache.org/licenses/LICENSE-2.0
-:: 
-:: Unless required by applicable law or agreed to in writing,
-:: software distributed under the License is distributed on an
-:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-:: KIND, either express or implied.  See the License for the
-:: specific language governing permissions and limitations
-:: under the License.
-
-%~dp0\cordova.bat log
+@ECHO OFF
+%~dp0\cordova.bat log %*
diff --git a/lib/cordova-android/bin/templates/cordova/run b/lib/cordova-android/bin/templates/cordova/run
index 840a8d5..ec352b0 100755
--- a/lib/cordova-android/bin/templates/cordova/run
+++ b/lib/cordova-android/bin/templates/cordova/run
@@ -1,3 +1,4 @@
+#!/bin/bash
 # 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
@@ -15,10 +16,8 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#!/bin/bash
-
 set -e
 
 CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
 
-bash "$CORDOVA_PATH"/cordova run
+bash "$CORDOVA_PATH"/lib/cordova run "$@"
diff --git a/lib/cordova-android/bin/templates/cordova/run.bat b/lib/cordova-android/bin/templates/cordova/run.bat
index 7c470ed..b1cab64 100644
--- a/lib/cordova-android/bin/templates/cordova/run.bat
+++ b/lib/cordova-android/bin/templates/cordova/run.bat
@@ -1 +1,2 @@
-%~dp0\cordova.bat run
+@ECHO OFF
+%~dp0\cordova.bat run %*
\ No newline at end of file
diff --git a/lib/cordova-android/bin/update b/lib/cordova-android/bin/update
index 0e86886..92ceda8 100755
--- a/lib/cordova-android/bin/update
+++ b/lib/cordova-android/bin/update
@@ -130,10 +130,17 @@
 fi
 
 # creating cordova folder and copying run/build/log/launch scripts
+mkdir "$PROJECT_PATH"/cordova
+mkdir "$PROJECT_PATH"/cordova/lib
 cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
-cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova
 cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
-cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release
 cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
 cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
 cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
+cp "$BUILD_PATH"/bin/templates/cordova/lib/cordova "$PROJECT_PATH"/cordova/lib/cordova
+cp "$BUILD_PATH"/bin/templates/cordova/lib/install-device "$PROJECT_PATH"/cordova/lib/install-device
+cp "$BUILD_PATH"/bin/templates/cordova/lib/install-emulator "$PROJECT_PATH"/cordova/lib/install-emulator
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-devices "$PROJECT_PATH"/cordova/lib/list-devices
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-emulator-images "$PROJECT_PATH"/cordova/lib/list-emulator-images
+cp "$BUILD_PATH"/bin/templates/cordova/lib/list-started-emulators "$PROJECT_PATH"/cordova/lib/list-started-emulators
+cp "$BUILD_PATH"/bin/templates/cordova/lib/start-emulator "$PROJECT_PATH"/cordova/lib/start-emulator
diff --git a/lib/cordova-android/bin/update.js b/lib/cordova-android/bin/update.js
index 244dcc1..748d602 100644
--- a/lib/cordova-android/bin/update.js
+++ b/lib/cordova-android/bin/update.js
@@ -183,11 +183,17 @@
 createAppInfoJar();
 WScript.Echo("Copying cordova command tools...");
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
-exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\build.bat ' + PROJECT_PATH + '\\cordova\\build.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
 exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\run.bat ' + PROJECT_PATH + '\\cordova\\run.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\cordova.js ' + PROJECT_PATH + '\\cordova\\lib\\cordova.js /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\install-device.bat ' + PROJECT_PATH + '\\cordova\\lib\\install-device.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\install-emulator.bat ' + PROJECT_PATH + '\\cordova\\lib\\install-emulator.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\list-emulator-images.bat ' + PROJECT_PATH + '\\cordova\\lib\\list-emulator-images.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\list-devices.bat ' + PROJECT_PATH + '\\cordova\\lib\\list-devices.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\list-started-emulators.bat ' + PROJECT_PATH + '\\cordova\\lib\\list-started-emulators.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\lib\\start-emulator.bat ' + PROJECT_PATH + '\\cordova\\lib\\start-emulator.bat /Y');
 
 cleanup();
diff --git a/lib/cordova-android/framework/assets/js/cordova.android.js b/lib/cordova-android/framework/assets/js/cordova.android.js
index 93e85d1..2941307 100644
--- a/lib/cordova-android/framework/assets/js/cordova.android.js
+++ b/lib/cordova-android/framework/assets/js/cordova.android.js
@@ -1,8 +1,8 @@
 // Platform: android
 
-// commit 125dca530923a44a8f44f68f5e1970cbdd4e7faf
+// commit d0ffb852378ff018bac2f3b12c38098a19b8ce00
 
-// File generated at :: Mon Apr 01 2013 13:28:03 GMT-0700 (PDT)
+// File generated at :: Thu Apr 18 2013 15:10:54 GMT-0400 (EDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -219,6 +219,10 @@
             }
             else {
               setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
                   documentEventHandlers[type].fire(evt);
               }, 0);
             }
@@ -742,6 +746,7 @@
 // Channels that must fire before "deviceready" is fired.
 channel.waitForInitialization('onCordovaReady');
 channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
 
 module.exports = channel;
 
@@ -840,32 +845,27 @@
     }
 
     var callbackId = service + cordova.callbackId++,
-        argsJson = JSON.stringify(args),
-        returnValue;
+        argsJson = JSON.stringify(args);
 
-    // TODO: Returning the payload of a synchronous call was deprecated in 2.2.0.
-    // Remove it after 6 months.
-    function captureReturnValue(value) {
-        returnValue = value;
-        success && success(value);
+    if (success || fail) {
+        cordova.callbacks[callbackId] = {success:success, fail:fail};
     }
 
-    cordova.callbacks[callbackId] = {success:captureReturnValue, fail:fail};
-
     if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
         window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
     } else {
         var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson);
-        androidExec.processMessages(messages);
-    }
-    if (cordova.callbacks[callbackId]) {
-        if (success || fail) {
-            cordova.callbacks[callbackId].success = success;
+        // If argsJson was received by Java as null, try again with the PROMPT bridge mode.
+        // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2.  See CB-2666.
+        if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && messages === "@Null arguments.") {
+            androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
+            androidExec(success, fail, service, action, args);
+            androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
+            return;
         } else {
-            delete cordova.callbacks[callbackId];
+            androidExec.processMessages(messages);
         }
     }
-    return returnValue;
 }
 
 function pollOnce() {
@@ -981,30 +981,30 @@
 androidExec.processMessages = function(messages) {
     if (messages) {
         messagesFromNative.push(messages);
+        // Check for the reentrant case, and enqueue the message if that's the case.
+        if (messagesFromNative.length > 1) {
+            return;
+        }
         while (messagesFromNative.length) {
-            messages = messagesFromNative.shift();
+            // Don't unshift until the end so that reentrancy can be detected.
+            messages = messagesFromNative[0];
             // The Java side can send a * message to indicate that it
             // still has messages waiting to be retrieved.
-            // TODO(agrieve): This is currently disabled on the Java side
-            // since it breaks returning the result in exec of synchronous
-            // plugins. Once we remove this ability, we can remove this comment.
             if (messages == '*') {
+                messagesFromNative.shift();
                 window.setTimeout(pollOnce, 0);
-                continue;
+                return;
             }
 
             var spaceIdx = messages.indexOf(' ');
             var msgLen = +messages.slice(0, spaceIdx);
             var message = messages.substr(spaceIdx + 1, msgLen);
             messages = messages.slice(spaceIdx + msgLen + 1);
-            // Put the remaining messages back into queue in case an exec()
-            // is made by the callback.
+            processMessage(message);
             if (messages) {
-                messagesFromNative.unshift(messages);
-            }
-
-            if (message) {
-                processMessage(message);
+                messagesFromNative[0] = messages;
+            } else {
+                messagesFromNative.shift();
             }
         }
     }
@@ -2398,7 +2398,7 @@
 
     if (typeof file == 'string') {
         // Deprecated in Cordova 2.4.
-        console.warning('Using a string argument with FileReader.readAs functions is deprecated.');
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
         reader._fileName = file;
     } else if (typeof file.fullPath == 'string') {
         reader._fileName = file.fullPath;
@@ -2756,7 +2756,7 @@
         var origin = protocol + url.host;
 
         // check whether there are the username:password credentials in the url
-        if (url.href.indexOf(origin) != 0) { // credentials found
+        if (url.href.indexOf(origin) !== 0) { // credentials found
             var atIndex = url.href.indexOf("@");
             credentials = url.href.substring(protocol.length, atIndex);
         }
@@ -2805,15 +2805,11 @@
     var params = null;
     var chunkedMode = true;
     var headers = null;
-
+    var httpMethod = null;
     var basicAuthHeader = getBasicAuthHeader(server);
     if (basicAuthHeader) {
-        if (!options) {
-            options = new FileUploadOptions();
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2822,6 +2818,12 @@
         fileName = options.fileName;
         mimeType = options.mimeType;
         headers = options.headers;
+        httpMethod = options.httpMethod || "POST";
+        if (httpMethod.toUpperCase() == "PUT"){
+            httpMethod = "PUT";
+        } else {
+            httpMethod = "POST";
+        }
         if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
             chunkedMode = options.chunkedMode;
         }
@@ -2848,7 +2850,7 @@
             successCallback && successCallback(result);
         }
     };
-    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
 };
 
 /**
@@ -2866,12 +2868,8 @@
 
     var basicAuthHeader = getBasicAuthHeader(source);
     if (basicAuthHeader) {
-        if (!options) {
-            options = {};
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2910,12 +2908,11 @@
 };
 
 /**
- * Aborts the ongoing file transfer on this object
- * @param successCallback {Function}  Callback to be invoked upon success
- * @param errorCallback {Function}    Callback to be invoked upon error
+ * Aborts the ongoing file transfer on this object. The original error
+ * callback for the file transfer will be called if necessary.
  */
-FileTransfer.prototype.abort = function(successCallback, errorCallback) {
-    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
+FileTransfer.prototype.abort = function() {
+    exec(null, null, 'FileTransfer', 'abort', [this._id]);
 };
 
 module.exports = FileTransfer;
@@ -2959,12 +2956,13 @@
  * @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, headers) {
+var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers, httpMethod) {
     this.fileKey = fileKey || null;
     this.fileName = fileName || null;
     this.mimeType = mimeType || null;
     this.params = params || null;
     this.headers = headers || null;
+    this.httpMethod = httpMethod || null;
 };
 
 module.exports = FileUploadOptions;
@@ -3299,6 +3297,7 @@
 
 var exec = require('cordova/exec');
 var channel = require('cordova/channel');
+var modulemapper = require('cordova/modulemapper');
 
 function InAppBrowser() {
    this.channels = {
@@ -3327,6 +3326,26 @@
         if (eventname in this.channels) {
             this.channels[eventname].unsubscribe(f);
         }
+    },
+
+    executeScript: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectScriptCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectScriptFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('executeScript requires exactly one of code or file to be specified');
+        }
+    },
+
+    insertCSS: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectStyleCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectStyleFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('insertCSS requires exactly one of code or file to be specified');
+        }
     }
 };
 
@@ -3335,6 +3354,13 @@
     var cb = function(eventname) {
        iab._eventHandler(eventname);
     };
+
+    // Don't catch calls that write to existing frames (e.g. named iframes).
+    if (window.frames && window.frames[strWindowName]) {
+        var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
+        return origOpenFunc.apply(window, arguments);
+    }
+
     exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
     return iab;
 };
@@ -4858,7 +4884,7 @@
 console.assert = function(expression) {
     if (expression) return;
 
-    var message = utils.vformat(arguments[1], [].slice.call(arguments, 2));
+    var message = logger.format.apply(logger.format, [].slice.call(arguments, 1));
     console.log("ASSERT: " + message);
 };
 
@@ -5958,10 +5984,10 @@
  * Parameters passed after message are used applied to
  * the message with utils.format()
  */
-logger.logLevel = function(level, message /* , ... */) {
+logger.logLevel = function(level /* , ... */) {
     // format the message with the parameters
-    var formatArgs = [].slice.call(arguments, 2);
-    message    = utils.vformat(message, formatArgs);
+    var formatArgs = [].slice.call(arguments, 1);
+    var message    = logger.format.apply(logger.format, formatArgs);
 
     if (LevelsMap[level] === null) {
         throw new Error("invalid logging level: " + level);
@@ -5996,6 +6022,92 @@
     }
 };
 
+
+/**
+ * Formats a string and arguments following it ala console.log()
+ *
+ * Any remaining arguments will be appended to the formatted string.
+ *
+ * for rationale, see FireBug's Console API:
+ *    http://getfirebug.com/wiki/index.php/Console_API
+ */
+logger.format = function(formatString, args) {
+    return __format(arguments[0], [].slice.call(arguments,1)).join(' ');
+};
+
+
+//------------------------------------------------------------------------------
+/**
+ * Formats a string and arguments following it ala vsprintf()
+ *
+ * format chars:
+ *   %j - format arg as JSON
+ *   %o - format arg as JSON
+ *   %c - format arg as ''
+ *   %% - replace with '%'
+ * any other char following % will format it's
+ * arg via toString().
+ *
+ * Returns an array containing the formatted string and any remaining
+ * arguments.
+ */
+function __format(formatString, args) {
+    if (formatString === null || formatString === undefined) return [""];
+    if (arguments.length == 1) return [formatString.toString()];
+
+    if (typeof formatString != "string")
+        formatString = formatString.toString();
+
+    var pattern = /(.*?)%(.)(.*)/;
+    var rest    = formatString;
+    var result  = [];
+
+    while (args.length) {
+        var match = pattern.exec(rest);
+        if (!match) break;
+
+        var arg   = args.shift();
+        rest = match[3];
+        result.push(match[1]);
+
+        if (match[2] == '%') {
+            result.push('%');
+            args.unshift(arg);
+            continue;
+        }
+
+        result.push(__formatted(arg, match[2]));
+    }
+
+    result.push(rest);
+
+    var remainingArgs = [].slice.call(args);
+    remainingArgs.unshift(result.join(''));
+    return remainingArgs;
+}
+
+function __formatted(object, formatChar) {
+
+    try {
+        switch(formatChar) {
+            case 'j':
+            case 'o': return JSON.stringify(object);
+            case 'c': return '';
+        }
+    }
+    catch (e) {
+        return "error JSON.stringify()ing argument: " + e;
+    }
+
+    if ((object === null) || (object === undefined)) {
+        return Object.prototype.toString.call(object);
+    }
+
+    return object.toString();
+}
+
+
+//------------------------------------------------------------------------------
 // when deviceready fires, log queued messages
 logger.__onDeviceReady = function() {
     if (DeviceReady) return;
@@ -6164,13 +6276,13 @@
             console.log("Notification.confirm(string, function, string, string) is deprecated.  Use Notification.confirm(string, function, string, array).");
         }
 
-        // Android and iOS take an array of button label names.
+        // Some platforms take an array of button label names.
         // Other platforms take a comma separated list.
         // For compatibility, we convert to the desired type based on the platform.
-        if (platform.id == "android" || platform.id == "ios") {
+        if (platform.id == "android" || platform.id == "ios" || platform.id == "windowsphone") {
             if (typeof _buttonLabels === 'string') {
                 var buttonLabelString = _buttonLabels;
-                _buttonLabels = buttonLabelString.split(",");
+                _buttonLabels = _buttonLabels.split(","); // not crazy about changing the var type here
             }
         } else {
             if (Array.isArray(_buttonLabels)) {
@@ -6521,62 +6633,6 @@
     }
 };
 
-/**
- * Formats a string and arguments following it ala sprintf()
- *
- * see utils.vformat() for more information
- */
-utils.format = function(formatString /* ,... */) {
-    var args = [].slice.call(arguments, 1);
-    return utils.vformat(formatString, args);
-};
-
-/**
- * Formats a string and arguments following it ala vsprintf()
- *
- * format chars:
- *   %j - format arg as JSON
- *   %o - format arg as JSON
- *   %c - format arg as ''
- *   %% - replace with '%'
- * any other char following % will format it's
- * arg via toString().
- *
- * for rationale, see FireBug's Console API:
- *    http://getfirebug.com/wiki/index.php/Console_API
- */
-utils.vformat = function(formatString, args) {
-    if (formatString === null || formatString === undefined) return "";
-    if (arguments.length == 1) return formatString.toString();
-    if (typeof formatString != "string") return formatString.toString();
-
-    var pattern = /(.*?)%(.)(.*)/;
-    var rest    = formatString;
-    var result  = [];
-
-    while (args.length) {
-        var arg   = args.shift();
-        var match = pattern.exec(rest);
-
-        if (!match) break;
-
-        rest = match[3];
-
-        result.push(match[1]);
-
-        if (match[2] == '%') {
-            result.push('%');
-            args.unshift(arg);
-            continue;
-        }
-
-        result.push(formatted(arg, match[2]));
-    }
-
-    result.push(rest);
-
-    return result.join('');
-};
 
 //------------------------------------------------------------------------------
 function UUIDcreatePart(length) {
@@ -6591,26 +6647,6 @@
     return uuidpart;
 }
 
-//------------------------------------------------------------------------------
-function formatted(object, formatChar) {
-
-    try {
-        switch(formatChar) {
-            case 'j':
-            case 'o': return JSON.stringify(object);
-            case 'c': return '';
-        }
-    }
-    catch (e) {
-        return "error JSON.stringify()ing argument: " + e;
-    }
-
-    if ((object === null) || (object === undefined)) {
-        return Object.prototype.toString.call(object);
-    }
-
-    return object.toString();
-}
 
 });
 
@@ -6620,6 +6656,25 @@
 // file: lib/scripts/bootstrap.js
 
 (function (context) {
+    var channel = require('cordova/channel');
+    var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
+
+    function logUnfiredChannels(arr) {
+        for (var i = 0; i < arr.length; ++i) {
+            if (arr[i].state != 2) {
+                console.log('Channel not fired: ' + arr[i].type);
+            }
+        }
+    }
+
+    window.setTimeout(function() {
+        if (channel.onDeviceReady.state != 2) {
+            console.log('deviceready has not fired after 5 seconds.');
+            logUnfiredChannels(platformInitChannelsArray);
+            logUnfiredChannels(channel.deviceReadyChannelsArray);
+        }
+    }, 5000);
+
     // Replace navigator before any modules are required(), to ensure it happens as soon as possible.
     // We replace it so that properties that can't be clobbered can instead be overridden.
     function replaceNavigator(origNavigator) {
@@ -6641,8 +6696,6 @@
         context.navigator = replaceNavigator(context.navigator);
     }
 
-    var channel = require("cordova/channel");
-
     // _nativeReady is global variable that the native side can set
     // to signify that the native code is ready. It is a global since
     // it may be called before any cordova JS is ready.
@@ -6651,32 +6704,33 @@
     }
 
     /**
-     * Create all cordova objects once page has fully loaded and native side is ready.
+     * Create all cordova objects once native side is ready.
      */
     channel.join(function() {
-        var builder = require('cordova/builder'),
-            platform = require('cordova/platform');
-
-        builder.buildIntoButDoNotClobber(platform.defaults, context);
-        builder.buildIntoAndClobber(platform.clobbers, context);
-        builder.buildIntoAndMerge(platform.merges, context);
-
         // Call the platform-specific initialization
-        platform.initialize();
+        require('cordova/platform').initialize();
 
         // Fire event to notify that all objects are created
         channel.onCordovaReady.fire();
 
-        // Fire onDeviceReady event once all constructors have run and
-        // cordova info has been received from native side.
+        // Fire onDeviceReady event once page has fully loaded, all
+        // constructors have run and cordova info has been received from native
+        // side.
+        // This join call is deliberately made after platform.initialize() in
+        // order that plugins may manipulate channel.deviceReadyChannelsArray
+        // if necessary.
         channel.join(function() {
             require('cordova').fireDocumentEvent('deviceready');
         }, channel.deviceReadyChannelsArray);
 
-    }, [ channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady ]);
+    }, platformInitChannelsArray);
 
 }(window));
 
+// file: lib/scripts/bootstrap-android.js
+
+require('cordova/channel').onNativeReady.fire();
+
 // file: lib/scripts/plugin_loader.js
 
 // Tries to load all plugins' js-modules.
@@ -6752,35 +6806,31 @@
         }
     }
 
-    // Try to XHR the cordova_plugins.json file asynchronously.
-    try { // we commented we were going to try, so let us actually try and catch 
-        var xhr = new context.XMLHttpRequest();
-        xhr.onreadystatechange = function() {
-            if (this.readyState != 4) { // not DONE
-                return;
-            }
 
+    // Try to XHR the cordova_plugins.json file asynchronously.
+    try { // we commented we were going to try, so let us actually try and catch
+        var xhr = new context.XMLHttpRequest();
+        xhr.onload = function() {
             // If the response is a JSON string which composes an array, call handlePluginsObject.
             // If the request fails, or the response is not a JSON array, just call finishPluginLoading.
-            if (this.status == 200) {
-                var obj = JSON.parse(this.responseText);
-                if (obj && obj instanceof Array && obj.length > 0) {
-                    handlePluginsObject(obj);
-                } else {
-                    finishPluginLoading();
-                }
+            var obj = JSON.parse(this.responseText);
+            if (obj && obj instanceof Array && obj.length > 0) {
+                handlePluginsObject(obj);
             } else {
                 finishPluginLoading();
             }
         };
+        xhr.onerror = function() {
+            finishPluginLoading();
+        };
         xhr.open('GET', 'cordova_plugins.json', true); // Async
         xhr.send();
     }
-    catch(err) {
+    catch(err){
         finishPluginLoading();
     }
 }(window));
 
 
 
-})();
+})();
\ No newline at end of file
diff --git a/lib/cordova-android/framework/libs/commons-codec-1.7.jar b/lib/cordova-android/framework/libs/commons-codec-1.7.jar
deleted file mode 100644
index efa7f72..0000000
--- a/lib/cordova-android/framework/libs/commons-codec-1.7.jar
+++ /dev/null
Binary files differ
diff --git a/lib/cordova-android/framework/proguard-project.txt b/lib/cordova-android/framework/proguard-project.txt
deleted file mode 100644
index f2fe155..0000000
--- a/lib/cordova-android/framework/proguard-project.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java b/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
index 22a9b94..426d250 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
@@ -42,6 +42,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Matrix;
 import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Rect;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 import android.net.Uri;
@@ -396,19 +397,21 @@
                             (destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) {
                         this.callbackContext.success(uri.toString());
                     } else {
+                        String uriString = uri.toString();
                         // Get the path to the image. Makes loading so much easier.
-                        String imagePath = FileHelper.getRealPath(uri, this.cordova);
-                        String mimeType = FileHelper.getMimeType(imagePath, this.cordova);
-                        // Log.d(LOG_TAG, "Real path = " + imagePath);
-                        // Log.d(LOG_TAG, "mime type = " + mimeType);
+                        String mimeType = FileHelper.getMimeType(uriString, this.cordova);
                         // If we don't have a valid image so quit.
-                        if (imagePath == null || mimeType == null || 
-                                !(mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/png"))) {
+                        if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) {
                         	Log.d(LOG_TAG, "I either have a null image path or bitmap");
                             this.failPicture("Unable to retrieve path to picture!");
                             return;
                         }
-                        Bitmap bitmap = getScaledBitmap(imagePath);
+                        Bitmap bitmap = null;
+                        try {
+                            bitmap = getScaledBitmap(uriString);
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
                         if (bitmap == null) {
                         	Log.d(LOG_TAG, "I either have a null image path or bitmap");
                             this.failPicture("Unable to create bitmap!");
@@ -416,14 +419,7 @@
                         }
 
                         if (this.correctOrientation) {
-                            String[] cols = { MediaStore.Images.Media.ORIENTATION };
-                            Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
-                                    cols, null, null, null);
-                            if (cursor != null) {
-                                cursor.moveToPosition(0);
-                                rotate = cursor.getInt(0);
-                                cursor.close();
-                            }
+                            rotate = getImageOrientation(uri);
                             if (rotate != 0) {
                                 Matrix matrix = new Matrix();
                                 matrix.setRotate(rotate);
@@ -443,15 +439,17 @@
                                 try {
                                     // Create an ExifHelper to save the exif data that is lost during compression
                                     String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
+                                    // Some content: URIs do not map to file paths (e.g. picasa).
+                                    String realPath = FileHelper.getRealPath(uri, this.cordova);
                                     ExifHelper exif = new ExifHelper();
-                                    try {
-                                        if (this.encodingType == JPEG) {
-                                            exif.createInFile(FileHelper.getRealPath(uri, this.cordova));
+                                    if (realPath != null && this.encodingType == JPEG) {
+                                        try {
+                                            exif.createInFile(realPath);
                                             exif.readExifData();
                                             rotate = exif.getOrientation();
+                                        } catch (IOException e) {
+                                            e.printStackTrace();
                                         }
-                                    } catch (IOException e) {
-                                        e.printStackTrace();
                                     }
 
                                     OutputStream os = new FileOutputStream(resizePath);
@@ -459,7 +457,7 @@
                                     os.close();
 
                                     // Restore exif data to file
-                                    if (this.encodingType == JPEG) {
+                                    if (realPath != null && this.encodingType == JPEG) {
                                         exif.createOutFile(resizePath);
                                         exif.writeExifData();
                                     }
@@ -493,6 +491,19 @@
         }
     }
 
+    private int getImageOrientation(Uri uri) {
+        String[] cols = { MediaStore.Images.Media.ORIENTATION };
+        Cursor cursor = cordova.getActivity().getContentResolver().query(uri,
+                cols, null, null, null);
+        int rotate = 0;
+        if (cursor != null) {
+            cursor.moveToPosition(0);
+            rotate = cursor.getInt(0);
+            cursor.close();
+        }
+        return rotate;
+    }
+
     /**
      * Figure out if the bitmap should be rotated. For instance if the picture was taken in
      * portrait mode
@@ -563,17 +574,18 @@
      *
      * @param imagePath
      * @return
+     * @throws IOException 
      */
-    private Bitmap getScaledBitmap(String imagePath) {
+    private Bitmap getScaledBitmap(String imageUrl) throws IOException {
         // If no new width or height were specified return the original bitmap
         if (this.targetWidth <= 0 && this.targetHeight <= 0) {
-            return BitmapFactory.decodeFile(imagePath);
+            return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova));
         }
 
         // figure out the original width and height of the image
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inJustDecodeBounds = true;
-        BitmapFactory.decodeFile(imagePath, options);
+        BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
         
         //CB-2292: WTF? Why is the width null?
         if(options.outWidth == 0 || options.outHeight == 0)
@@ -587,7 +599,7 @@
         // Load in the smallest bitmap possible that is closest to the size we want
         options.inJustDecodeBounds = false;
         options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
-        Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options);
+        Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
         if (unscaledBitmap == null) {
             return null;
         }
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/Config.java b/lib/cordova-android/framework/src/org/apache/cordova/Config.java
index f5de38d..594c2b2 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/Config.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/Config.java
@@ -171,7 +171,7 @@
                     LOG.i("CordovaLog", "Found start page location: %s", src);
 
                     if (src != null) {
-                        Pattern schemeRegex = Pattern.compile("^[a-z]+://");
+                        Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
                         Matcher matcher = schemeRegex.matcher(src);
                         if (matcher.find()) {
                             startUrl = src;
@@ -220,19 +220,33 @@
             } else { // specific access
                 // check if subdomains should be included
                 // TODO: we should not add more domains if * has already been added
+                Pattern schemeRegex = Pattern.compile("^[a-z-]+://");
+                Matcher matcher = schemeRegex.matcher(origin);
                 if (subdomains) {
-                    // XXX making it stupid friendly for people who forget to include protocol/SSL
+                    // Check for http or https protocols
                     if (origin.startsWith("http")) {
                         this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?")));
-                    } else {
+                    }
+                    // Check for other protocols
+                    else if(matcher.find()){
+                        this.whiteList.add(Pattern.compile("^" + origin.replaceFirst("//", "//(.*\\.)?")));
+                    }
+                    // XXX making it stupid friendly for people who forget to include protocol/SSL
+                    else {
                         this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin));
                     }
                     LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
                 } else {
-                    // XXX making it stupid friendly for people who forget to include protocol/SSL
+                    // Check for http or https protocols
                     if (origin.startsWith("http")) {
                         this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
-                    } else {
+                    }
+                    // Check for other protocols
+                    else if(matcher.find()){
+                        this.whiteList.add(Pattern.compile("^" + origin));
+                    }
+                    // XXX making it stupid friendly for people who forget to include protocol/SSL
+                    else {
                         this.whiteList.add(Pattern.compile("^https?://" + origin));
                     }
                     LOG.d(TAG, "Origin to allow: %s", origin);
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/CordovaLocationListener.java b/lib/cordova-android/framework/src/org/apache/cordova/CordovaLocationListener.java
old mode 100755
new mode 100644
index 7b7a9f7..01c828b
--- a/lib/cordova-android/framework/src/org/apache/cordova/CordovaLocationListener.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/CordovaLocationListener.java
@@ -22,6 +22,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
 
 import org.apache.cordova.api.CallbackContext;
 
@@ -42,6 +44,8 @@
 
     public HashMap<String, CallbackContext> watches = new HashMap<String, CallbackContext>();
     private List<CallbackContext> callbacks = new ArrayList<CallbackContext>();
+    
+    private Timer timer = null;
 
     private String TAG = "[Cordova Location Listener]";
 
@@ -52,11 +56,12 @@
     }
 
     protected void fail(int code, String message) {
+    	this.cancelTimer();
         for (CallbackContext callbackContext: this.callbacks)
         {
-            this.owner.fail(code, message, callbackContext);
+            this.owner.fail(code, message, callbackContext, false);
         }
-        if(this.owner.isGlobalListener(this))
+        if(this.owner.isGlobalListener(this) && this.watches.size() == 0)
         {
         	Log.d(TAG, "Stopping global listener");
         	this.stop();
@@ -65,16 +70,17 @@
 
         Iterator<CallbackContext> it = this.watches.values().iterator();
         while (it.hasNext()) {
-            this.owner.fail(code, message, it.next());
+            this.owner.fail(code, message, it.next(), true);
         }
     }
 
     private void win(Location loc) {
+    	this.cancelTimer();
         for (CallbackContext callbackContext: this.callbacks)
         {
-            this.owner.win(loc, callbackContext);
+            this.owner.win(loc, callbackContext, false);
         }
-        if(this.owner.isGlobalListener(this))
+        if(this.owner.isGlobalListener(this) && this.watches.size() == 0)
         {
         	Log.d(TAG, "Stopping global listener");
         	this.stop();
@@ -83,7 +89,7 @@
 
         Iterator<CallbackContext> it = this.watches.values().iterator();
         while (it.hasNext()) {
-            this.owner.win(loc, it.next());
+            this.owner.win(loc, it.next(), true);
         }
     }
 
@@ -155,8 +161,12 @@
             this.start();
         }
     }
-    public void addCallback(CallbackContext callbackContext) {
-        this.callbacks.add(callbackContext);
+    public void addCallback(CallbackContext callbackContext, int timeout) {
+    	if(this.timer == null) {
+    		this.timer = new Timer();
+    	}
+    	this.timer.schedule(new LocationTimeoutTask(callbackContext, this), timeout);
+        this.callbacks.add(callbackContext);        
         if (this.size() == 1) {
             this.start();
         }
@@ -173,7 +183,7 @@
     /**
      * Destroy listener.
      */
-    public void destroy() {
+    public void destroy() {    	
         this.stop();
     }
 
@@ -199,9 +209,43 @@
      * Stop receiving location updates.
      */
     private void stop() {
+    	this.cancelTimer();
         if (this.running) {
             this.locationManager.removeUpdates(this);
             this.running = false;
         }
     }
+    
+    private void cancelTimer() {
+    	if(this.timer != null) {
+    		this.timer.cancel();
+        	this.timer.purge();
+        	this.timer = null;
+    	}
+    }
+    
+    private class LocationTimeoutTask extends TimerTask {
+    	
+    	private CallbackContext callbackContext = null;
+    	private CordovaLocationListener listener = null;
+    	
+    	public LocationTimeoutTask(CallbackContext callbackContext, CordovaLocationListener listener) {
+    		this.callbackContext = callbackContext;
+    		this.listener = listener;
+    	}
+
+		@Override
+		public void run() {
+			for (CallbackContext callbackContext: listener.callbacks) {
+				if(this.callbackContext == callbackContext) {
+					listener.callbacks.remove(callbackContext);
+					break;
+				}
+			}
+			
+			if(listener.size() == 0) {
+				listener.stop();
+			}
+		}    	
+    }
 }
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebView.java b/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebView.java
index e653a95..5dd061e 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebView.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebView.java
@@ -24,6 +24,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Stack;
+import java.util.regex.Pattern;
 
 import org.apache.cordova.Config;
 import org.apache.cordova.api.CordovaInterface;
@@ -237,7 +238,11 @@
         // Set the nav dump for HTC 2.x devices (disabling for ICS, deprecated entirely for Jellybean 4.2)
         try {
             Method gingerbread_getMethod =  WebSettings.class.getMethod("setNavDump", new Class[] { boolean.class });
-            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
+            
+            String manufacturer = android.os.Build.MANUFACTURER;
+            Log.d(TAG, "CordovaWebView is running on device made by: " + manufacturer);
+            if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB &&
+                    android.os.Build.MANUFACTURER.contains("HTC"))
             {
                 gingerbread_getMethod.invoke(settings, true);
             }
@@ -262,11 +267,8 @@
         // Enable database
         // We keep this disabled because we use or shim to get around DOM_EXCEPTION_ERROR_16
         String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
-        if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
-        {
-            settings.setDatabaseEnabled(true);
-            settings.setDatabasePath(databasePath);
-        }
+        settings.setDatabaseEnabled(true);
+        settings.setDatabasePath(databasePath);
         
         settings.setGeolocationDatabasePath(databasePath);
 
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebViewClient.java b/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebViewClient.java
index 4751fc3..4b00615 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebViewClient.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/CordovaWebViewClient.java
@@ -314,15 +314,6 @@
         // Clear timeout flag
         this.appView.loadUrlTimeout++;
 
-        // Try firing the onNativeReady event in JS. If it fails because the JS is
-        // not loaded yet then just set a flag so that the onNativeReady can be fired
-        // from the JS side when the JS gets to that code.
-        if (!url.equals("about:blank")) {
-            LOG.d(TAG, "Trying to fire onNativeReady");
-            this.appView.loadUrl("javascript:try{ cordova.require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}");
-            this.appView.postMessage("onNativeReady", null);
-        }
-
         // Broadcast message that page has loaded
         this.appView.postMessage("onPageFinished", url);
 
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/Device.java b/lib/cordova-android/framework/src/org/apache/cordova/Device.java
index 0f828a1..aa316c5 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/Device.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/Device.java
@@ -77,7 +77,6 @@
             r.put("uuid", Device.uuid);
             r.put("version", this.getOSVersion());
             r.put("platform", Device.platform);
-            r.put("name", this.getProductName());
             r.put("cordova", Device.cordovaVersion);
             r.put("model", this.getModel());
             callbackContext.success(r);
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/DroidGap.java b/lib/cordova-android/framework/src/org/apache/cordova/DroidGap.java
index d4296cb..bb895e2 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/DroidGap.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/DroidGap.java
@@ -51,6 +51,7 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
+import android.widget.ImageView;
 import android.webkit.ValueCallback;
 import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
@@ -717,7 +718,7 @@
             appView.handleDestroy();
         }
         else {
-            this.endActivity();
+            this.activityState = ACTIVITY_EXITING; 
         }
     }
 
@@ -1031,7 +1032,13 @@
                 root.setBackgroundColor(that.getIntegerProperty("backgroundColor", Color.BLACK));
                 root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                         ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
-                root.setBackgroundResource(that.splashscreen);
+                // We want the splashscreen to keep its ratio, 
+                // for this we need to use an ImageView and not simply the background of the LinearLayout
+                ImageView splashscreenView = new ImageView(that.getActivity());
+                splashscreenView.setImageResource(that.splashscreen);
+                splashscreenView.setScaleType(ImageView.ScaleType.CENTER_CROP); // similar to the background-size:cover CSS property
+                splashscreenView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+                root.addView(splashscreenView);
 
                 // Create and show the dialog
                 splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/ExposedJsApi.java b/lib/cordova-android/framework/src/org/apache/cordova/ExposedJsApi.java
index 48e7102..7702d35 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/ExposedJsApi.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/ExposedJsApi.java
@@ -20,6 +20,7 @@
 
 import android.webkit.JavascriptInterface;
 import org.apache.cordova.api.PluginManager;
+import org.apache.cordova.api.PluginResult;
 import org.json.JSONException;
 
 /**
@@ -39,6 +40,12 @@
 
     @JavascriptInterface
     public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
+        // If the arguments weren't received, send a message back to JS.  It will switch bridge modes and try again.  See CB-2666.
+        // We send a message meant specifically for this case.  It starts with "@" so no other message can be encoded into the same string.
+        if (arguments == null) {
+            return "@Null arguments.";
+        }
+
         jsMessageQueue.setPaused(true);
         try {
             boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/FileHelper.java b/lib/cordova-android/framework/src/org/apache/cordova/FileHelper.java
index c10ed96..8b446b0 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/FileHelper.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/FileHelper.java
@@ -28,6 +28,8 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URLConnection;
+import java.util.Locale;
 
 public class FileHelper {
     private static final String LOG_TAG = "FileUtils";
@@ -92,7 +94,8 @@
             Uri uri = Uri.parse(uriString);
             return cordova.getActivity().getContentResolver().openInputStream(uri);
         } else if (uriString.startsWith("file:///android_asset/")) {
-            String relativePath = uriString.substring(22);
+            Uri uri = Uri.parse(uriString);
+            String relativePath = uri.getPath().substring(15);
             return cordova.getActivity().getAssets().open(relativePath);
         } else {
             return new FileInputStream(getRealPath(uriString, cordova));
@@ -122,14 +125,18 @@
     public static String getMimeType(String uriString, CordovaInterface cordova) {
         String mimeType = null;
 
+        Uri uri = Uri.parse(uriString);
         if (uriString.startsWith("content://")) {
-            Uri uri = Uri.parse(uriString);
             mimeType = cordova.getActivity().getContentResolver().getType(uri);
         } else {
-            // MimeTypeMap.getFileExtensionFromUrl has a bug that occurs when the filename has a space, so we encode it.
-            // We also convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
-            String encodedUriString = uriString.replace(" ", "%20").toLowerCase();
-            String extension = MimeTypeMap.getFileExtensionFromUrl(encodedUriString);
+            // MimeTypeMap.getFileExtensionFromUrl() fails when there are query parameters.
+            String extension = uri.getPath();
+            int lastDot = extension.lastIndexOf('.');
+            if (lastDot != -1) {
+                extension = extension.substring(lastDot + 1);
+            }
+            // Convert the URI string to lower case to ensure compatibility with MimeTypeMap (see CB-2185).
+            extension = extension.toLowerCase();
             if (extension.equals("3ga")) {
                 mimeType = "audio/3gpp";
             } else {
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/FileTransfer.java b/lib/cordova-android/framework/src/org/apache/cordova/FileTransfer.java
index dba29af..c1ca15c 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/FileTransfer.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/FileTransfer.java
@@ -41,6 +41,8 @@
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
 
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
@@ -100,10 +102,83 @@
     }
 
     /**
+     * Adds an interface method to an InputStream to return the number of bytes
+     * read from the raw stream. This is used to track total progress against
+     * the HTTP Content-Length header value from the server.
+     */
+    private static abstract class TrackingInputStream extends FilterInputStream {
+    	public TrackingInputStream(final InputStream in) {
+    		super(in);
+    	}
+        public abstract long getTotalRawBytesRead();
+	}
+
+    private static class ExposedGZIPInputStream extends GZIPInputStream {
+	    public ExposedGZIPInputStream(final InputStream in) throws IOException {
+	    	super(in);
+	    }
+	    public Inflater getInflater() {
+	    	return inf;
+	    }
+	}
+
+    /**
+     * Provides raw bytes-read tracking for a GZIP input stream. Reports the
+     * total number of compressed bytes read from the input, rather than the
+     * number of uncompressed bytes.
+     */
+    private static class TrackingGZIPInputStream extends TrackingInputStream {
+    	private ExposedGZIPInputStream gzin;
+	    public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
+	    	super(gzin);
+	    	this.gzin = gzin;
+	    }
+	    public long getTotalRawBytesRead() {
+	    	return gzin.getInflater().getBytesRead();
+	    }
+	}
+
+    /**
+     * Provides simple total-bytes-read tracking for an existing InputStream
+     */
+    private static class TrackingHTTPInputStream extends TrackingInputStream {
+        private long bytesRead = 0;
+        public TrackingHTTPInputStream(InputStream stream) {
+            super(stream);
+        }
+
+        private int updateBytesRead(int newBytesRead) {
+        	if (newBytesRead != -1) {
+        		bytesRead += newBytesRead;
+        	}
+        	return newBytesRead;
+        }
+
+        @Override
+        public int read() throws IOException {
+            return updateBytesRead(super.read());
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return updateBytesRead(super.read(buffer));
+        }
+
+        @Override
+        public int read(byte[] bytes, int offset, int count) throws IOException {
+            return updateBytesRead(super.read(bytes, offset, count));
+        }
+
+        public long getTotalRawBytesRead() {
+        	return bytesRead;
+        }
+    }
+
+    /**
      * Works around a bug on Android 2.3.
      * http://code.google.com/p/android/issues/detail?id=14562
      */
-    private static final class DoneHandlerInputStream extends FilterInputStream {
+    private static final class DoneHandlerInputStream extends TrackingHTTPInputStream {
         private boolean done;
         
         public DoneHandlerInputStream(InputStream stream) {
@@ -204,6 +279,7 @@
         // Look for headers on the params map for backwards compatibility with older Cordova versions.
         final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
         final String objectId = args.getString(9);
+        final String httpMethod = getArgument(args, 10, "POST");
 
         Log.d(LOG_TAG, "fileKey: " + fileKey);
         Log.d(LOG_TAG, "fileName: " + fileName);
@@ -213,6 +289,7 @@
         Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
         Log.d(LOG_TAG, "headers: " + headers);
         Log.d(LOG_TAG, "objectId: " + objectId);
+        Log.d(LOG_TAG, "httpMethod: " + httpMethod);
         
         final URL url;
         try {
@@ -280,7 +357,7 @@
                     conn.setUseCaches(false);
 
                     // Use a post method.
-                    conn.setRequestMethod("POST");
+                    conn.setRequestMethod(httpMethod);
                     conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
 
                     // Set the cookies on the response
@@ -407,7 +484,7 @@
                     int responseCode = conn.getResponseCode();
                     Log.d(LOG_TAG, "response code: " + responseCode);
                     Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
-                    InputStream inStream = null;
+                    TrackingInputStream inStream = null;
                     try {
                         inStream = getInputStream(conn);
                         synchronized (context) {
@@ -483,11 +560,15 @@
         }
     }
 
-    private static InputStream getInputStream(URLConnection conn) throws IOException {
+    private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
             return new DoneHandlerInputStream(conn.getInputStream());
         }
-        return conn.getInputStream();
+        String encoding = conn.getContentEncoding();
+        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
+        	return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
+        }
+        return new TrackingHTTPInputStream(conn.getInputStream());
     }
 
     // always verify the host - don't check for certificate
@@ -698,6 +779,9 @@
                     {
                         connection.setRequestProperty("cookie", cookie);
                     }
+                    
+                    // This must be explicitly set for gzip progress tracking to work.
+                    connection.setRequestProperty("Accept-Encoding", "gzip");
 
                     // Handle the other headers
                     if (headers != null) {
@@ -709,14 +793,15 @@
                     Log.d(LOG_TAG, "Download file:" + url);
 
                     FileProgressResult progress = new FileProgressResult();
-                    if (connection.getContentEncoding() == null) {
-                        // Only trust content-length header if no gzip etc
+                    if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
+                        // Only trust content-length header if we understand
+                        // the encoding -- identity or gzip
                         progress.setLengthComputable(true);
                         progress.setTotal(connection.getContentLength());
                     }
                     
                     FileOutputStream outputStream = null;
-                    InputStream inputStream = null;
+                    TrackingInputStream inputStream = null;
                     
                     try {
                         inputStream = getInputStream(connection);
@@ -731,12 +816,10 @@
                         // write bytes to file
                         byte[] buffer = new byte[MAX_BUFFER_SIZE];
                         int bytesRead = 0;
-                        long totalBytes = 0;
                         while ((bytesRead = inputStream.read(buffer)) > 0) {
                             outputStream.write(buffer, 0, bytesRead);
-                            totalBytes += bytesRead;
                             // Send a progress event.
-                            progress.setLoaded(totalBytes);
+                            progress.setLoaded(inputStream.getTotalRawBytesRead());
                             PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
                             progressResult.setKeepCallback(true);
                             context.sendPluginResult(progressResult);
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/GeoBroker.java b/lib/cordova-android/framework/src/org/apache/cordova/GeoBroker.java
old mode 100755
new mode 100644
index e7cdce0..4a07b73
--- a/lib/cordova-android/framework/src/org/apache/cordova/GeoBroker.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/GeoBroker.java
@@ -38,7 +38,7 @@
 public class GeoBroker extends CordovaPlugin {
     private GPSListener gpsListener;
     private NetworkListener networkListener;
-    private LocationManager locationManager;
+    private LocationManager locationManager;    
 
     /**
      * Constructor.
@@ -73,7 +73,7 @@
                     PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
                     callbackContext.sendPluginResult(result);
                 } else {
-                    this.getCurrentLocation(callbackContext, enableHighAccuracy);
+                    this.getCurrentLocation(callbackContext, enableHighAccuracy, args.optInt(2, 60000));
                 }
             }
             else if (action.equals("addWatch")) {
@@ -102,11 +102,11 @@
         this.networkListener.clearWatch(id);
     }
 
-    private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy) {
+    private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy, int timeout) {
         if (enableHighAccuracy) {
-            this.gpsListener.addCallback(callbackContext);
+            this.gpsListener.addCallback(callbackContext, timeout);
         } else {
-            this.networkListener.addCallback(callbackContext);
+            this.networkListener.addCallback(callbackContext, timeout);
         }
     }
 
@@ -160,8 +160,9 @@
         return o;
     }
 
-    public void win(Location loc, CallbackContext callbackContext) {
-        PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
+    public void win(Location loc, CallbackContext callbackContext, boolean keepCallback) {
+    	PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
+    	result.setKeepCallback(keepCallback);
         callbackContext.sendPluginResult(result);
     }
 
@@ -172,7 +173,7 @@
      * @param msg			The error message
      * @throws JSONException 
      */
-    public void fail(int code, String msg, CallbackContext callbackContext) {
+    public void fail(int code, String msg, CallbackContext callbackContext, boolean keepCallback) {
         JSONObject obj = new JSONObject();
         String backup = null;
         try {
@@ -189,6 +190,7 @@
             result = new PluginResult(PluginResult.Status.ERROR, backup);
         }
 
+        result.setKeepCallback(keepCallback);
         callbackContext.sendPluginResult(result);
     }
 
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java b/lib/cordova-android/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
index 2142714..375282e 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java
@@ -24,11 +24,12 @@
 import org.apache.cordova.api.CordovaInterface;
 import org.apache.cordova.api.LOG;
 
-import android.content.res.AssetManager;
-import android.net.Uri;
+import android.annotation.TargetApi;
+import android.os.Build;
 import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
 
+@TargetApi(Build.VERSION_CODES.HONEYCOMB)
 public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
 
 
@@ -43,34 +44,20 @@
     @Override
     public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
         if(url.contains("?") || url.contains("#") || needsIceCreamSpaceInAssetUrlFix(url)){
-            return generateWebResourceResponse(url);
-        } else {
-            return super.shouldInterceptRequest(view, url);
+            WebResourceResponse ret = generateWebResourceResponse(url);
+            if (ret != null) {
+                return ret;
+            }
         }
+        return super.shouldInterceptRequest(view, url);
     }
 
     private WebResourceResponse generateWebResourceResponse(String url) {
-        final String ANDROID_ASSET = "file:///android_asset/";
-        if (url.startsWith(ANDROID_ASSET)) {
-            String niceUrl = url;
-            niceUrl = url.replaceFirst(ANDROID_ASSET, "");
-            if(niceUrl.contains("?")){
-                niceUrl = niceUrl.split("\\?")[0];
-            }
-            else if(niceUrl.contains("#"))
-            {
-                niceUrl = niceUrl.split("#")[0];
-            }
-
-            String mimetype = null;
-            if(niceUrl.endsWith(".html")){
-                mimetype = "text/html";
-            }
+        if (url.startsWith("file:///android_asset/")) {
+            String mimetype = FileHelper.getMimeType(url, cordova);
 
             try {
-                AssetManager assets = cordova.getActivity().getAssets();
-                Uri uri = Uri.parse(niceUrl);
-                InputStream stream = assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
+                InputStream stream = FileHelper.getInputStreamFromUriString(url, cordova);
                 WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream);
                 return response;
             } catch (IOException e) {
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/InAppBrowser.java b/lib/cordova-android/framework/src/org/apache/cordova/InAppBrowser.java
index 0d5d496..8e78baa 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/InAppBrowser.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/InAppBrowser.java
@@ -51,6 +51,7 @@
 import android.view.inputmethod.InputMethodManager;
 import android.webkit.WebChromeClient;
 import android.webkit.GeolocationPermissions.Callback;
+import android.webkit.JsPromptResult;
 import android.webkit.WebSettings;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
@@ -92,12 +93,9 @@
      * @return              A PluginResult object with a status and message.
      */
     public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
-        PluginResult.Status status = PluginResult.Status.OK;
-        String result = "";
-        this.callbackContext = callbackContext;
-        
         try {
             if (action.equals("open")) {
+                this.callbackContext = callbackContext;
                 String url = args.getString(0);
                 String target = args.optString(1);
                 if (target == null || target.equals("") || target.equals(NULL)) {
@@ -108,6 +106,7 @@
                 Log.d(LOG_TAG, "target = " + target);
 
                 url = updateUrl(url);
+                String result = "";
 
                 // SELF
                 if (SELF.equals(target)) {
@@ -143,35 +142,52 @@
                     Log.d(LOG_TAG, "in blank");
                     result = this.showWebPage(url, features);
                 }
+
+                PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
+                pluginResult.setKeepCallback(true);
+                this.callbackContext.sendPluginResult(pluginResult);
             }
             else if (action.equals("close")) {
                 closeDialog();
-
-                PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
-                pluginResult.setKeepCallback(false);
-                this.callbackContext.sendPluginResult(pluginResult);
+                this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
             }
             else if (action.equals("injectScriptCode")) {
-                String source = args.getString(0);
-
-                org.json.JSONArray jsonEsc = new org.json.JSONArray();
-                jsonEsc.put(source);
-                String jsonRepr = jsonEsc.toString();
-                String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
-                String scriptEnclosure = "(function(d){var c=d.createElement('script');c.type='text/javascript';c.innerText="
-                                       + jsonSourceString
-                                       + ";d.getElementsByTagName('head')[0].appendChild(c);})(document)";
-                this.inAppWebView.loadUrl("javascript:" + scriptEnclosure);
-
-                PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
-                this.callbackContext.sendPluginResult(pluginResult);
+                String jsWrapper = null;
+                if (args.getBoolean(1)) {
+                    jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
+                }
+                injectDeferredObject(args.getString(0), jsWrapper);
+            }
+            else if (action.equals("injectScriptFile")) {
+                String jsWrapper;
+                if (args.getBoolean(1)) {
+                    jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
+                } else {
+                    jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
+                }
+                injectDeferredObject(args.getString(0), jsWrapper);
+            }
+            else if (action.equals("injectStyleCode")) {
+                String jsWrapper;
+                if (args.getBoolean(1)) {
+                    jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
+                } else {
+                    jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
+                }
+                injectDeferredObject(args.getString(0), jsWrapper);
+            }
+            else if (action.equals("injectStyleFile")) {
+                String jsWrapper;
+                if (args.getBoolean(1)) {
+                    jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
+                } else {
+                    jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
+                }
+                injectDeferredObject(args.getString(0), jsWrapper);
             }
             else {
-                status = PluginResult.Status.INVALID_ACTION;
+                return false;
             }
-            PluginResult pluginResult = new PluginResult(status, result);
-            pluginResult.setKeepCallback(true);
-            this.callbackContext.sendPluginResult(pluginResult);
         } catch (JSONException e) {
             this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
         }
@@ -179,6 +195,37 @@
     }
 
     /**
+     * Inject an object (script or style) into the InAppBrowser WebView.
+     *
+     * This is a helper method for the inject{Script|Style}{Code|File} API calls, which
+     * provides a consistent method for injecting JavaScript code into the document.
+     *
+     * If a wrapper string is supplied, then the source string will be JSON-encoded (adding
+     * quotes) and wrapped using string formatting. (The wrapper string should have a single
+     * '%s' marker)
+     *
+     * @param source      The source object (filename or script/style text) to inject into
+     *                    the document.
+     * @param jsWrapper   A JavaScript string to wrap the source string in, so that the object
+     *                    is properly injected, or null if the source string is JavaScript text
+     *                    which should be executed directly.
+     */
+    private void injectDeferredObject(String source, String jsWrapper) {
+        String scriptToInject;
+        if (jsWrapper != null) {
+            org.json.JSONArray jsonEsc = new org.json.JSONArray();
+            jsonEsc.put(source);
+            String jsonRepr = jsonEsc.toString();
+            String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
+            scriptToInject = String.format(jsWrapper, jsonSourceString);
+        } else {
+            scriptToInject = source;
+        }
+        // This action will have the side-effect of blurring the currently focused element
+        this.inAppWebView.loadUrl("javascript:" + scriptToInject);
+    }
+
+    /**
      * Put the list of features into a hash map
      * 
      * @param optString
@@ -444,7 +491,7 @@
                 // WebView
                 inAppWebView = new WebView(cordova.getActivity());
                 inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-                inAppWebView.setWebChromeClient(new InAppChromeClient());
+                inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
                 WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
                 inAppWebView.setWebViewClient(client);
                 WebSettings settings = inAppWebView.getSettings();
@@ -527,8 +574,15 @@
         result.setKeepCallback(keepCallback);
         this.callbackContext.sendPluginResult(result);
     }
+
     public class InAppChromeClient extends WebChromeClient {
 
+        private CordovaWebView webView;
+
+        public InAppChromeClient(CordovaWebView webView) {
+            super();
+            this.webView = webView;
+        }
         /**
          * Handle database quota exceeded notification.
          *
@@ -571,6 +625,57 @@
             super.onGeolocationPermissionsShowPrompt(origin, callback);
             callback.invoke(origin, true, false);
         }
+
+        /**
+         * Tell the client to display a prompt dialog to the user.
+         * If the client returns true, WebView will assume that the client will
+         * handle the prompt dialog and call the appropriate JsPromptResult method.
+         *
+         * The prompt bridge provided for the InAppBrowser is capable of executing any
+         * oustanding callback belonging to the InAppBrowser plugin. Care has been
+         * taken that other callbacks cannot be triggered, and that no other code
+         * execution is possible.
+         *
+         * To trigger the bridge, the prompt default value should be of the form:
+         *
+         * gap-iab://<callbackId>
+         *
+         * where <callbackId> is the string id of the callback to trigger (something
+         * like "InAppBrowser0123456789")
+         *
+         * If present, the prompt message is expected to be a JSON-encoded value to
+         * pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
+         *
+         * @param view
+         * @param url
+         * @param message
+         * @param defaultValue
+         * @param result
+         */
+        @Override
+        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
+            // See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
+            if (defaultValue != null && defaultValue.startsWith("gap-iab://")) {
+                PluginResult scriptResult;
+                String scriptCallbackId = defaultValue.substring(10);
+                if (scriptCallbackId.startsWith("InAppBrowser")) {
+                    if(message == null || message.length() == 0) {
+                        scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
+                    } else {
+                        try {
+                            scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
+                        } catch(JSONException e) {
+                            scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
+                        }
+                    }
+                    this.webView.sendPluginResult(scriptResult, scriptCallbackId);
+                    result.confirm("");
+                    return true;
+                }
+            }
+            return false;
+        }
+
     }
     
     /**
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/NativeToJsMessageQueue.java b/lib/cordova-android/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
index ea684a4..8a13213 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
@@ -50,13 +50,10 @@
     // exec() is asynchronous. Set this to true when running bridge benchmarks.
     static final boolean DISABLE_EXEC_CHAINING = false;
     
-    // Upper limit for how much data to send to JS in one shot.
-    // TODO(agrieve): This is currently disable. It should be re-enabled once we
-    // remove support for returning values from exec() calls. This was
-    // deprecated in 2.2.0.
-    // Also, this currently only chops up on message boundaries. It may be useful
+    // Arbitrarily chosen upper limit for how much data to send to JS in one shot.
+    // This currently only chops up on message boundaries. It may be useful
     // to allow it to break up messages.
-    private static int MAX_PAYLOAD_SIZE = -1; //50 * 1024 * 10240;
+    private static int MAX_PAYLOAD_SIZE = 50 * 1024 * 10240;
     
     /**
      * The index into registeredListeners to treat as active. 
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java b/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java
deleted file mode 100755
index 72171f2..0000000
--- a/lib/cordova-android/framework/src/org/apache/cordova/api/Plugin.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
-*/
-package org.apache.cordova.api;
-
-import org.apache.cordova.CordovaWebView;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-/**
- * Legacy Plugin class. This acts as a shim to support the old execute() signature.
- * New plugins should extend CordovaPlugin directly.
- */
-@Deprecated
-public abstract class Plugin extends CordovaPlugin {
-    public LegacyContext    ctx;			        // LegacyContext object
-
-    public abstract PluginResult execute(String action, JSONArray args, String callbackId);
-
-    public boolean isSynch(String action) {
-        return false;
-    }
-    
-    @Override
-    public void initialize(CordovaInterface cordova, CordovaWebView webView) {
-        super.initialize(cordova, webView);
-        this.setContext(cordova);
-        this.setView(webView);
-    }
-
-    /**
-     * Sets the context of the Plugin. This can then be used to do things like
-     * get file paths associated with the Activity.
-     *
-     * @param ctx The context of the main Activity.
-     */
-    public void setContext(CordovaInterface ctx) {
-        this.cordova = ctx;
-        this.ctx = new LegacyContext(cordova);
-    }
-
-    /**
-     * Sets the main View of the application, this is the WebView within which
-     * a Cordova app runs.
-     *
-     * @param webView The Cordova WebView
-     */
-    public void setView(CordovaWebView webView) {
-        this.webView = webView;
-    }
-    
-    @Override
-    public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
-        final String callbackId = callbackContext.getCallbackId();
-        boolean runAsync = !isSynch(action);
-        if (runAsync) {
-            // Run this on a different thread so that this one can return back to JS
-            cordova.getThreadPool().execute(new Runnable() {
-                public void run() {
-                    PluginResult cr;
-                    try {
-                        cr = execute(action, args, callbackId);
-                    } catch (Throwable e) {
-                        cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
-                    }
-                    sendPluginResult(cr, callbackId);
-                }
-            });
-        } else {
-            PluginResult cr = execute(action, args, callbackId);
-    
-            // Interpret a null response as NO_RESULT, which *does* clear the callbacks on the JS side.
-            if (cr == null) {
-                cr = new PluginResult(PluginResult.Status.NO_RESULT);
-            }
-            
-            callbackContext.sendPluginResult(cr);
-        }
-        return true;
-    }
-
-    /**
-     * Send generic JavaScript statement back to JavaScript.
-     * sendPluginResult() should be used instead where possible.
-     */
-    public void sendJavascript(String statement) {
-        this.webView.sendJavascript(statement);
-    }
-
-    /**
-     * Send generic JavaScript statement back to JavaScript.
-     */
-    public void sendPluginResult(PluginResult pluginResult, String callbackId) {
-        this.webView.sendPluginResult(pluginResult, callbackId);
-    }
-
-    /**
-     * Call the JavaScript success callback for this plugin.
-     *
-     * This can be used if the execute code for the plugin is asynchronous meaning
-     * that execute should return null and the callback from the async operation can
-     * call success(...) or error(...)
-     *
-     * @param pluginResult      The result to return.
-     * @param callbackId        The callback id used when calling back into JavaScript.
-     */
-    public void success(PluginResult pluginResult, String callbackId) {
-        this.webView.sendPluginResult(pluginResult, callbackId);
-    }
-
-    /**
-     * Helper for success callbacks that just returns the Status.OK by default
-     *
-     * @param message           The message to add to the success result.
-     * @param callbackId        The callback id used when calling back into JavaScript.
-     */
-    public void success(JSONObject message, String callbackId) {
-        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
-    }
-
-    /**
-     * Helper for success callbacks that just returns the Status.OK by default
-     *
-     * @param message           The message to add to the success result.
-     * @param callbackId        The callback id used when calling back into JavaScript.
-     */
-    public void success(String message, String callbackId) {
-        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
-    }
-
-    /**
-     * Call the JavaScript error callback for this plugin.
-     *
-     * @param pluginResult      The result to return.
-     * @param callbackId        The callback id used when calling back into JavaScript.
-     */
-    public void error(PluginResult pluginResult, String callbackId) {
-        this.webView.sendPluginResult(pluginResult, callbackId);
-    }
-
-    /**
-     * Helper for error callbacks that just returns the Status.ERROR by default
-     *
-     * @param message           The message to add to the error result.
-     * @param callbackId        The callback id used when calling back into JavaScript.
-     */
-    public void error(JSONObject message, String callbackId) {
-        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
-    }
-
-    /**
-     * Helper for error callbacks that just returns the Status.ERROR by default
-     *
-     * @param message           The message to add to the error result.
-     * @param callbackId        The callback id used when calling back into JavaScript.
-     */
-    public void error(String message, String callbackId) {
-        this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
-    }
-
-}
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java
index 774b21c..7d823cd 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/api/PluginManager.java
@@ -119,6 +119,7 @@
                     service = xml.getAttributeValue(null, "name");
                     pluginClass = xml.getAttributeValue(null, "value");
                     // System.out.println("Plugin: "+name+" => "+value);
+                    Log.d(TAG, "<plugin> tags are deprecated, please use <features> instead. <plugin> will no longer work as of Cordova 3.0");
                     onload = "true".equals(xml.getAttributeValue(null, "onload"));
                     entry = new PluginEntry(service, pluginClass, onload);
                     this.addService(entry);
diff --git a/lib/cordova-blackberry/.gitignore b/lib/cordova-blackberry/.gitignore
new file mode 100644
index 0000000..a67abe6
--- /dev/null
+++ b/lib/cordova-blackberry/.gitignore
@@ -0,0 +1,26 @@
+# OS X
+
+.DS_Store
+
+# Eclipse
+
+deliverables/
+.preprocessed/
+
+# Text Editor Tmp
+
+._*
+
+# Generated libraries
+
+build/
+dist/
+bin/node_modules
+bin/templates/project/lib
+example/
+
+# OS X
+
+.DS_Store
+
+tags
diff --git a/lib/cordova-blackberry/VERSION b/lib/cordova-blackberry/VERSION
index e70b452..59b7056 100644
--- a/lib/cordova-blackberry/VERSION
+++ b/lib/cordova-blackberry/VERSION
@@ -1 +1 @@
-2.6.0
+2.7.0rc1
diff --git a/lib/cordova-blackberry/bin/create b/lib/cordova-blackberry/bin/create
index b7e719b..54903b8 100755
--- a/lib/cordova-blackberry/bin/create
+++ b/lib/cordova-blackberry/bin/create
@@ -25,11 +25,15 @@
 
 if [ -n "$1" ] && [ "$1" == "-h" ]
 then
-    echo 'usage: create path package appname'
-    echo 'After you have created your application, make sure to customize the project.properties file inside your app directory with your environment specifics!'
-    exit 0
+  echo "Usage: $0 <path_to_new_project> <package_name> <project_name>"
+  echo "    <path_to_new_project>: Path to your new Cordova iOS project"
+  echo "    <package_name>: Package name, following reverse-domain style convention (ignored on BlackBerry platforms)"
+  echo "    <project_name>: Project name"
+  echo 'After you have created your application, make sure to customize the project.properties file inside your app directory with your environment specifics!'
+  exit 0;
 fi
 
+
 BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
 VERSION=$(cat "$BUILD_PATH/VERSION")
 
diff --git a/lib/cordova-blackberry/bin/templates/project/lib/ant-contrib/ant-contrib-1.0b3.jar b/lib/cordova-blackberry/bin/templates/project/lib/ant-contrib/ant-contrib-1.0b3.jar
deleted file mode 100644
index 0625376..0000000
--- a/lib/cordova-blackberry/bin/templates/project/lib/ant-contrib/ant-contrib-1.0b3.jar
+++ /dev/null
Binary files differ
diff --git a/lib/cordova-blackberry/bin/templates/project/www/VERSION b/lib/cordova-blackberry/bin/templates/project/www/VERSION
index e70b452..59b7056 100644
--- a/lib/cordova-blackberry/bin/templates/project/www/VERSION
+++ b/lib/cordova-blackberry/bin/templates/project/www/VERSION
@@ -1 +1 @@
-2.6.0
+2.7.0rc1
diff --git a/lib/cordova-blackberry/bin/templates/project/www/index.html b/lib/cordova-blackberry/bin/templates/project/www/index.html
index 4d39cf3..a56d963 100644
--- a/lib/cordova-blackberry/bin/templates/project/www/index.html
+++ b/lib/cordova-blackberry/bin/templates/project/www/index.html
@@ -33,7 +33,7 @@
                 <p class="event received">Device is Ready</p>
             </div>
         </div>
-        <script type="text/javascript" src="cordova-2.6.0.js"></script>
+        <script type="text/javascript" src="cordova-2.7.0rc1.js"></script>
         <script type="text/javascript" src="js/index.js"></script>
         <script type="text/javascript">
             app.initialize();
diff --git a/lib/cordova-blackberry/framework/ext/src/org/apache/cordova/device/Device.java b/lib/cordova-blackberry/framework/ext/src/org/apache/cordova/device/Device.java
index af39a49..c332cc4 100644
--- a/lib/cordova-blackberry/framework/ext/src/org/apache/cordova/device/Device.java
+++ b/lib/cordova-blackberry/framework/ext/src/org/apache/cordova/device/Device.java
@@ -54,7 +54,7 @@
 				JSONObject device = new JSONObject();
 				device.put( FIELD_PLATFORM, "BlackBerry");
 				device.put( FIELD_UUID, new Integer( DeviceInfo.getDeviceId()) );
-				device.put( FIELD_CORDOVA, "2.6.0" );
+				device.put( FIELD_CORDOVA, "2.7.0rc1" );
 				device.put( FIELD_MODEL, new String(DeviceInfo.getDeviceName()) );
 				device.put( FIELD_NAME, new String(DeviceInfo.getDeviceName()) );
 				device.put( FIELD_VERSION, new String(DeviceInfo.getSoftwareVersion()) );
diff --git a/lib/cordova-blackberry/javascript/cordova.blackberry.js b/lib/cordova-blackberry/javascript/cordova.blackberry.js
index 86b9e8b..cd5878b 100644
--- a/lib/cordova-blackberry/javascript/cordova.blackberry.js
+++ b/lib/cordova-blackberry/javascript/cordova.blackberry.js
@@ -1,8 +1,8 @@
 // Platform: blackberry
 
-// commit 125dca530923a44a8f44f68f5e1970cbdd4e7faf
+// commit 4808bdada2a73c3fb2ec69857b8e970414c31d57
 
-// File generated at :: Wed Apr 03 2013 15:26:44 GMT-0700 (PDT)
+// File generated at :: Fri Apr 19 2013 14:51:35 GMT-0700 (PDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -219,6 +219,10 @@
             }
             else {
               setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
                   documentEventHandlers[type].fire(evt);
               }, 0);
             }
@@ -742,6 +746,7 @@
 // Channels that must fire before "deviceready" is fired.
 channel.waitForInitialization('onCordovaReady');
 channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
 
 module.exports = channel;
 
@@ -2222,7 +2227,7 @@
 
     if (typeof file == 'string') {
         // Deprecated in Cordova 2.4.
-        console.warning('Using a string argument with FileReader.readAs functions is deprecated.');
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
         reader._fileName = file;
     } else if (typeof file.fullPath == 'string') {
         reader._fileName = file.fullPath;
@@ -2580,7 +2585,7 @@
         var origin = protocol + url.host;
 
         // check whether there are the username:password credentials in the url
-        if (url.href.indexOf(origin) != 0) { // credentials found
+        if (url.href.indexOf(origin) !== 0) { // credentials found
             var atIndex = url.href.indexOf("@");
             credentials = url.href.substring(protocol.length, atIndex);
         }
@@ -2629,15 +2634,11 @@
     var params = null;
     var chunkedMode = true;
     var headers = null;
-
+    var httpMethod = null;
     var basicAuthHeader = getBasicAuthHeader(server);
     if (basicAuthHeader) {
-        if (!options) {
-            options = new FileUploadOptions();
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2646,6 +2647,12 @@
         fileName = options.fileName;
         mimeType = options.mimeType;
         headers = options.headers;
+        httpMethod = options.httpMethod || "POST";
+        if (httpMethod.toUpperCase() == "PUT"){
+            httpMethod = "PUT";
+        } else {
+            httpMethod = "POST";
+        }
         if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
             chunkedMode = options.chunkedMode;
         }
@@ -2672,7 +2679,7 @@
             successCallback && successCallback(result);
         }
     };
-    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
 };
 
 /**
@@ -2690,12 +2697,8 @@
 
     var basicAuthHeader = getBasicAuthHeader(source);
     if (basicAuthHeader) {
-        if (!options) {
-            options = {};
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2734,12 +2737,11 @@
 };
 
 /**
- * Aborts the ongoing file transfer on this object
- * @param successCallback {Function}  Callback to be invoked upon success
- * @param errorCallback {Function}    Callback to be invoked upon error
+ * Aborts the ongoing file transfer on this object. The original error
+ * callback for the file transfer will be called if necessary.
  */
-FileTransfer.prototype.abort = function(successCallback, errorCallback) {
-    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
+FileTransfer.prototype.abort = function() {
+    exec(null, null, 'FileTransfer', 'abort', [this._id]);
 };
 
 module.exports = FileTransfer;
@@ -2783,12 +2785,13 @@
  * @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, headers) {
+var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers, httpMethod) {
     this.fileKey = fileKey || null;
     this.fileName = fileName || null;
     this.mimeType = mimeType || null;
     this.params = params || null;
     this.headers = headers || null;
+    this.httpMethod = httpMethod || null;
 };
 
 module.exports = FileUploadOptions;
@@ -3123,6 +3126,7 @@
 
 var exec = require('cordova/exec');
 var channel = require('cordova/channel');
+var modulemapper = require('cordova/modulemapper');
 
 function InAppBrowser() {
    this.channels = {
@@ -3151,6 +3155,26 @@
         if (eventname in this.channels) {
             this.channels[eventname].unsubscribe(f);
         }
+    },
+
+    executeScript: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectScriptCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectScriptFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('executeScript requires exactly one of code or file to be specified');
+        }
+    },
+
+    insertCSS: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectStyleCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectStyleFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('insertCSS requires exactly one of code or file to be specified');
+        }
     }
 };
 
@@ -3159,6 +3183,13 @@
     var cb = function(eventname) {
        iab._eventHandler(eventname);
     };
+
+    // Don't catch calls that write to existing frames (e.g. named iframes).
+    if (window.frames && window.frames[strWindowName]) {
+        var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
+        return origOpenFunc.apply(window, arguments);
+    }
+
     exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
     return iab;
 };
@@ -5361,7 +5392,7 @@
                     model: "PlayBook",
                     name: "PlayBook", // deprecated: please use device.model
                     uuid: info.uuid,
-                    cordova: "2.6.0"
+                    cordova: "2.7.0rc1"
                 });
             }),
             request = new blackberry.transport.RemoteFunctionCall("org/apache/cordova/getDeviceInfo");
@@ -6018,7 +6049,7 @@
 console.assert = function(expression) {
     if (expression) return;
 
-    var message = utils.vformat(arguments[1], [].slice.call(arguments, 2));
+    var message = logger.format.apply(logger.format, [].slice.call(arguments, 1));
     console.log("ASSERT: " + message);
 };
 
@@ -8669,10 +8700,10 @@
  * Parameters passed after message are used applied to
  * the message with utils.format()
  */
-logger.logLevel = function(level, message /* , ... */) {
+logger.logLevel = function(level /* , ... */) {
     // format the message with the parameters
-    var formatArgs = [].slice.call(arguments, 2);
-    message    = utils.vformat(message, formatArgs);
+    var formatArgs = [].slice.call(arguments, 1);
+    var message    = logger.format.apply(logger.format, formatArgs);
 
     if (LevelsMap[level] === null) {
         throw new Error("invalid logging level: " + level);
@@ -8707,6 +8738,92 @@
     }
 };
 
+
+/**
+ * Formats a string and arguments following it ala console.log()
+ *
+ * Any remaining arguments will be appended to the formatted string.
+ *
+ * for rationale, see FireBug's Console API:
+ *    http://getfirebug.com/wiki/index.php/Console_API
+ */
+logger.format = function(formatString, args) {
+    return __format(arguments[0], [].slice.call(arguments,1)).join(' ');
+};
+
+
+//------------------------------------------------------------------------------
+/**
+ * Formats a string and arguments following it ala vsprintf()
+ *
+ * format chars:
+ *   %j - format arg as JSON
+ *   %o - format arg as JSON
+ *   %c - format arg as ''
+ *   %% - replace with '%'
+ * any other char following % will format it's
+ * arg via toString().
+ *
+ * Returns an array containing the formatted string and any remaining
+ * arguments.
+ */
+function __format(formatString, args) {
+    if (formatString === null || formatString === undefined) return [""];
+    if (arguments.length == 1) return [formatString.toString()];
+
+    if (typeof formatString != "string")
+        formatString = formatString.toString();
+
+    var pattern = /(.*?)%(.)(.*)/;
+    var rest    = formatString;
+    var result  = [];
+
+    while (args.length) {
+        var match = pattern.exec(rest);
+        if (!match) break;
+
+        var arg   = args.shift();
+        rest = match[3];
+        result.push(match[1]);
+
+        if (match[2] == '%') {
+            result.push('%');
+            args.unshift(arg);
+            continue;
+        }
+
+        result.push(__formatted(arg, match[2]));
+    }
+
+    result.push(rest);
+
+    var remainingArgs = [].slice.call(args);
+    remainingArgs.unshift(result.join(''));
+    return remainingArgs;
+}
+
+function __formatted(object, formatChar) {
+
+    try {
+        switch(formatChar) {
+            case 'j':
+            case 'o': return JSON.stringify(object);
+            case 'c': return '';
+        }
+    }
+    catch (e) {
+        return "error JSON.stringify()ing argument: " + e;
+    }
+
+    if ((object === null) || (object === undefined)) {
+        return Object.prototype.toString.call(object);
+    }
+
+    return object.toString();
+}
+
+
+//------------------------------------------------------------------------------
 // when deviceready fires, log queued messages
 logger.__onDeviceReady = function() {
     if (DeviceReady) return;
@@ -8875,13 +8992,13 @@
             console.log("Notification.confirm(string, function, string, string) is deprecated.  Use Notification.confirm(string, function, string, array).");
         }
 
-        // Android and iOS take an array of button label names.
+        // Some platforms take an array of button label names.
         // Other platforms take a comma separated list.
         // For compatibility, we convert to the desired type based on the platform.
-        if (platform.id == "android" || platform.id == "ios") {
+        if (platform.id == "android" || platform.id == "ios" || platform.id == "windowsphone") {
             if (typeof _buttonLabels === 'string') {
                 var buttonLabelString = _buttonLabels;
-                _buttonLabels = buttonLabelString.split(",");
+                _buttonLabels = _buttonLabels.split(","); // not crazy about changing the var type here
             }
         } else {
             if (Array.isArray(_buttonLabels)) {
@@ -9290,7 +9407,7 @@
             model: "Dev Alpha",
             name: "Dev Alpha", // deprecated: please use device.model
             uuid: blackberry.identity.uuid,
-            cordova: "2.6.0"
+            cordova: "2.7.0rc1"
         });
 
         return { "status" : cordova.callbackStatus.NO_RESULT, "message" : "Device info returned" };
@@ -9303,6 +9420,7 @@
 define("cordova/plugin/qnx/file", function(require, exports, module) {
 
 /*global WebKitBlobBuilder:false */
+/*global Blob:false */
 var cordova = require('cordova'),
     FileError = require('cordova/plugin/FileError'),
     DirectoryEntry = require('cordova/plugin/DirectoryEntry'),
@@ -9710,6 +9828,7 @@
 // file: lib/blackberry/plugin/qnx/fileTransfer.js
 define("cordova/plugin/qnx/fileTransfer", function(require, exports, module) {
 
+/*global Blob:false */
 var cordova = require('cordova'),
     FileEntry = require('cordova/plugin/FileEntry'),
     FileTransferError = require('cordova/plugin/FileTransferError'),
@@ -10566,62 +10685,6 @@
     }
 };
 
-/**
- * Formats a string and arguments following it ala sprintf()
- *
- * see utils.vformat() for more information
- */
-utils.format = function(formatString /* ,... */) {
-    var args = [].slice.call(arguments, 1);
-    return utils.vformat(formatString, args);
-};
-
-/**
- * Formats a string and arguments following it ala vsprintf()
- *
- * format chars:
- *   %j - format arg as JSON
- *   %o - format arg as JSON
- *   %c - format arg as ''
- *   %% - replace with '%'
- * any other char following % will format it's
- * arg via toString().
- *
- * for rationale, see FireBug's Console API:
- *    http://getfirebug.com/wiki/index.php/Console_API
- */
-utils.vformat = function(formatString, args) {
-    if (formatString === null || formatString === undefined) return "";
-    if (arguments.length == 1) return formatString.toString();
-    if (typeof formatString != "string") return formatString.toString();
-
-    var pattern = /(.*?)%(.)(.*)/;
-    var rest    = formatString;
-    var result  = [];
-
-    while (args.length) {
-        var arg   = args.shift();
-        var match = pattern.exec(rest);
-
-        if (!match) break;
-
-        rest = match[3];
-
-        result.push(match[1]);
-
-        if (match[2] == '%') {
-            result.push('%');
-            args.unshift(arg);
-            continue;
-        }
-
-        result.push(formatted(arg, match[2]));
-    }
-
-    result.push(rest);
-
-    return result.join('');
-};
 
 //------------------------------------------------------------------------------
 function UUIDcreatePart(length) {
@@ -10636,26 +10699,6 @@
     return uuidpart;
 }
 
-//------------------------------------------------------------------------------
-function formatted(object, formatChar) {
-
-    try {
-        switch(formatChar) {
-            case 'j':
-            case 'o': return JSON.stringify(object);
-            case 'c': return '';
-        }
-    }
-    catch (e) {
-        return "error JSON.stringify()ing argument: " + e;
-    }
-
-    if ((object === null) || (object === undefined)) {
-        return Object.prototype.toString.call(object);
-    }
-
-    return object.toString();
-}
 
 });
 
@@ -10665,6 +10708,25 @@
 // file: lib/scripts/bootstrap.js
 
 (function (context) {
+    var channel = require('cordova/channel');
+    var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
+
+    function logUnfiredChannels(arr) {
+        for (var i = 0; i < arr.length; ++i) {
+            if (arr[i].state != 2) {
+                console.log('Channel not fired: ' + arr[i].type);
+            }
+        }
+    }
+
+    window.setTimeout(function() {
+        if (channel.onDeviceReady.state != 2) {
+            console.log('deviceready has not fired after 5 seconds.');
+            logUnfiredChannels(platformInitChannelsArray);
+            logUnfiredChannels(channel.deviceReadyChannelsArray);
+        }
+    }, 5000);
+
     // Replace navigator before any modules are required(), to ensure it happens as soon as possible.
     // We replace it so that properties that can't be clobbered can instead be overridden.
     function replaceNavigator(origNavigator) {
@@ -10686,8 +10748,6 @@
         context.navigator = replaceNavigator(context.navigator);
     }
 
-    var channel = require("cordova/channel");
-
     // _nativeReady is global variable that the native side can set
     // to signify that the native code is ready. It is a global since
     // it may be called before any cordova JS is ready.
@@ -10696,29 +10756,26 @@
     }
 
     /**
-     * Create all cordova objects once page has fully loaded and native side is ready.
+     * Create all cordova objects once native side is ready.
      */
     channel.join(function() {
-        var builder = require('cordova/builder'),
-            platform = require('cordova/platform');
-
-        builder.buildIntoButDoNotClobber(platform.defaults, context);
-        builder.buildIntoAndClobber(platform.clobbers, context);
-        builder.buildIntoAndMerge(platform.merges, context);
-
         // Call the platform-specific initialization
-        platform.initialize();
+        require('cordova/platform').initialize();
 
         // Fire event to notify that all objects are created
         channel.onCordovaReady.fire();
 
-        // Fire onDeviceReady event once all constructors have run and
-        // cordova info has been received from native side.
+        // Fire onDeviceReady event once page has fully loaded, all
+        // constructors have run and cordova info has been received from native
+        // side.
+        // This join call is deliberately made after platform.initialize() in
+        // order that plugins may manipulate channel.deviceReadyChannelsArray
+        // if necessary.
         channel.join(function() {
             require('cordova').fireDocumentEvent('deviceready');
         }, channel.deviceReadyChannelsArray);
 
-    }, [ channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady ]);
+    }, platformInitChannelsArray);
 
 }(window));
 
@@ -10823,31 +10880,27 @@
         }
     }
 
-    // Try to XHR the cordova_plugins.json file asynchronously.
-    try { // we commented we were going to try, so let us actually try and catch 
-        var xhr = new context.XMLHttpRequest();
-        xhr.onreadystatechange = function() {
-            if (this.readyState != 4) { // not DONE
-                return;
-            }
 
+    // Try to XHR the cordova_plugins.json file asynchronously.
+    try { // we commented we were going to try, so let us actually try and catch
+        var xhr = new context.XMLHttpRequest();
+        xhr.onload = function() {
             // If the response is a JSON string which composes an array, call handlePluginsObject.
             // If the request fails, or the response is not a JSON array, just call finishPluginLoading.
-            if (this.status == 200) {
-                var obj = JSON.parse(this.responseText);
-                if (obj && obj instanceof Array && obj.length > 0) {
-                    handlePluginsObject(obj);
-                } else {
-                    finishPluginLoading();
-                }
+            var obj = JSON.parse(this.responseText);
+            if (obj && obj instanceof Array && obj.length > 0) {
+                handlePluginsObject(obj);
             } else {
                 finishPluginLoading();
             }
         };
+        xhr.onerror = function() {
+            finishPluginLoading();
+        };
         xhr.open('GET', 'cordova_plugins.json', true); // Async
         xhr.send();
     }
-    catch(err) {
+    catch(err){
         finishPluginLoading();
     }
 }(window));
diff --git a/lib/cordova-ios/.gitignore b/lib/cordova-ios/.gitignore
new file mode 100644
index 0000000..e0ac686
--- /dev/null
+++ b/lib/cordova-ios/.gitignore
@@ -0,0 +1,12 @@
+.DS_Store
+.*.sw?
+*.cso
+tmp
+*.mode1v3
+*.pbxuser
+build
+*.xcworkspace
+xcuserdata
+CordovaLib/javascript/cordova-*.js
+CordovaLib/CordovaLibApp/www/cordova.ios.js
+console.log
\ No newline at end of file
diff --git a/lib/cordova-ios/.gitmodules b/lib/cordova-ios/.gitmodules
new file mode 100644
index 0000000..3625844
--- /dev/null
+++ b/lib/cordova-ios/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "PhoneGapLibTest/www"]
+	path = PhoneGapLibTest/www
+	url = git://github.com/phonegap/mobile-spec.git
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDV.h b/lib/cordova-ios/CordovaLib/Classes/CDV.h
index 5a0ae6a..15d9316 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDV.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDV.h
@@ -33,7 +33,6 @@
 #import "CDVContact.h"
 #import "CDVContacts.h"
 #import "CDVDebug.h"
-#import "CDVDebugConsole.h"
 #import "CDVDevice.h"
 #import "CDVFile.h"
 #import "CDVFileTransfer.h"
@@ -47,6 +46,7 @@
 #import "CDVLocalStorage.h"
 #import "CDVInAppBrowser.h"
 #import "CDVScreenOrientationDelegate.h"
+#import "CDVTimer.h"
 
 #import "NSArray+Comparisons.h"
 #import "NSData+Base64.h"
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVAvailability.h b/lib/cordova-ios/CordovaLib/Classes/CDVAvailability.h
index 947ae2d..b288522 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVAvailability.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVAvailability.h
@@ -40,6 +40,7 @@
 #define __CORDOVA_2_4_0 20400
 #define __CORDOVA_2_5_0 20500
 #define __CORDOVA_2_6_0 20600
+#define __CORDOVA_2_7_0 20700
 #define __CORDOVA_NA 99999      /* not available */
 
 /*
@@ -50,7 +51,7 @@
  #endif
  */
 #ifndef CORDOVA_VERSION_MIN_REQUIRED
-    #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_6_0
+    #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_7_0
 #endif
 
 /*
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVDebugConsole.m b/lib/cordova-ios/CordovaLib/Classes/CDVDebugConsole.m
deleted file mode 100644
index 29cbb91..0000000
--- a/lib/cordova-ios/CordovaLib/Classes/CDVDebugConsole.m
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements.  See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership.  The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License.  You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied.  See the License for the
- specific language governing permissions and limitations
- under the License.
- */
-
-#import "CDVDebugConsole.h"
-
-@implementation CDVDebugConsole
-
-- (void)log:(CDVInvokedUrlCommand*)command
-{
-    NSString* message = [command.arguments objectAtIndex:0];
-    NSDictionary* options = [command.arguments objectAtIndex:1];
-    NSString* log_level = @"INFO";
-
-    if ([options objectForKey:@"logLevel"]) {
-        log_level = [options objectForKey:@"logLevel"];
-    }
-
-    NSLog(@"[%@] %@", log_level, message);
-}
-
-@end
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVFile.m b/lib/cordova-ios/CordovaLib/Classes/CDVFile.m
index 8c65270..10908ce 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVFile.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVFile.m
@@ -227,10 +227,9 @@
     NSURL* testUri = [NSURL URLWithString:strUri];
     CDVPluginResult* result = nil;
 
-    if (!testUri || ![testUri isFileURL]) {
-        // issue ENCODING_ERR
+    if (!testUri) {
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
-    } else {
+    } else if ([testUri isFileURL]) {
         NSFileManager* fileMgr = [[NSFileManager alloc] init];
         NSString* path = [testUri path];
         // NSLog(@"url path: %@", path);
@@ -262,7 +261,13 @@
             // return NOT_FOUND_ERR
             result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
         }
+    } else if ([strUri hasPrefix:@"assets-library://"]) {
+        NSDictionary* fileSystem = [self getDirectoryEntry:strUri isDirectory:NO];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+    } else {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
     }
+
     if (result != nil) {
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
     }
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h
index d82cdd3..233a114 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h
@@ -53,10 +53,15 @@
                                   AndHttpStatus:(int)httpStatus
                                         AndBody:(NSString*)body;
 @property (readonly) NSMutableDictionary* activeTransfers;
+@property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundTaskID;
 @end
 
+@class CDVFileTransferEntityLengthRequest;
+
 @interface CDVFileTransferDelegate : NSObject {}
 
+- (void)updateBytesExpected:(NSInteger)newBytesExpected;
+
 @property (strong) NSMutableData* responseData; // atomic
 @property (nonatomic, strong) CDVFileTransfer* command;
 @property (nonatomic, assign) CDVFileTransferDirection direction;
@@ -70,5 +75,7 @@
 @property (nonatomic, assign) NSInteger bytesTransfered;
 @property (nonatomic, assign) NSInteger bytesExpected;
 @property (nonatomic, assign) BOOL trustAllHosts;
+@property (strong) NSFileHandle* targetFileHandle;
+@property (nonatomic, strong) CDVFileTransferEntityLengthRequest* entityLengthRequest;
 
 @end;
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m
index 5741aca..5536715 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m
@@ -126,17 +126,19 @@
     // arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]
     // however, params is a JavaScript object and during marshalling is put into the options dict,
     // thus debug and chunkedMode are the 6th and 7th arguments
-    NSArray* arguments = command.arguments;
-    NSString* target = (NSString*)[arguments objectAtIndex:0];
-    NSString* server = (NSString*)[arguments objectAtIndex:1];
-    NSString* fileKey = [arguments objectAtIndex:2 withDefault:@"file"];
-    NSString* fileName = [arguments objectAtIndex:3 withDefault:@"no-filename"];
-    NSString* mimeType = [arguments objectAtIndex:4 withDefault:nil];
-    NSDictionary* options = [arguments objectAtIndex:5 withDefault:nil];
+    NSString* target = [command argumentAtIndex:0];
+    NSString* server = [command argumentAtIndex:1];
+    NSString* fileKey = [command argumentAtIndex:2 withDefault:@"file"];
+    NSString* fileName = [command argumentAtIndex:3 withDefault:@"no-filename"];
+    NSString* mimeType = [command argumentAtIndex:4 withDefault:nil];
+    NSDictionary* options = [command argumentAtIndex:5 withDefault:nil];
     //    BOOL trustAllHosts = [[arguments objectAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
-    BOOL chunkedMode = [[arguments objectAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
-    NSDictionary* headers = [arguments objectAtIndex:8 withDefault:nil];
-
+    BOOL chunkedMode = [[command argumentAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
+    NSDictionary* headers = [command argumentAtIndex:8 withDefault:nil];
+    // Allow alternative http method, default to POST. JS side checks
+    // for allowed methods, currently PUT or POST (forces POST for
+    // unrecognised values)
+    NSString* httpMethod = [command argumentAtIndex:10 withDefault:@"POST"];
     CDVPluginResult* result = nil;
     CDVFileTransferError errorCode = 0;
 
@@ -158,7 +160,8 @@
     }
 
     NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
-    [req setHTTPMethod:@"POST"];
+
+    [req setHTTPMethod:httpMethod];
 
     //    Magic value to set a cookie
     if ([options objectForKey:kOptionsKeyCookie]) {
@@ -212,6 +215,12 @@
         CFStreamCreateBoundPair(NULL, &readStream, &writeStream, kStreamBufferSize);
         [req setHTTPBodyStream:CFBridgingRelease(readStream)];
 
+        self.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
+                [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskID];
+                self.backgroundTaskID = UIBackgroundTaskInvalid;
+                NSLog(@"Background task to upload media finished.");
+            }];
+
         [self.commandDelegate runInBackground:^{
             if (CFWriteStreamOpen(writeStream)) {
                 NSData* chunks[] = {postBodyBeforeFile, fileData, postBodyAfterFile};
@@ -456,19 +465,56 @@
 
 @end
 
+@interface CDVFileTransferEntityLengthRequest : NSObject {
+    NSURLConnection* _connection;
+    CDVFileTransferDelegate* __weak _originalDelegate;
+}
+
+- (CDVFileTransferEntityLengthRequest*)initWithOriginalRequest:(NSURLRequest*)originalRequest andDelegate:(CDVFileTransferDelegate*)originalDelegate;
+
+@end;
+
+@implementation CDVFileTransferEntityLengthRequest;
+
+- (CDVFileTransferEntityLengthRequest*)initWithOriginalRequest:(NSURLRequest*)originalRequest andDelegate:(CDVFileTransferDelegate*)originalDelegate
+{
+    if (self) {
+        DLog(@"Requesting entity length for GZIPped content...");
+
+        NSMutableURLRequest* req = [originalRequest mutableCopy];
+        [req setHTTPMethod:@"HEAD"];
+        [req setValue:@"identity" forHTTPHeaderField:@"Accept-Encoding"];
+
+        _originalDelegate = originalDelegate;
+        _connection = [NSURLConnection connectionWithRequest:req delegate:self];
+    }
+    return self;
+}
+
+- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
+{
+    DLog(@"HEAD request returned; content-length is %lld", [response expectedContentLength]);
+    [_originalDelegate updateBytesExpected:[response expectedContentLength]];
+}
+
+- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
+{}
+
+- (void)connectionDidFinishLoading:(NSURLConnection*)connection
+{}
+
+@end
+
 @implementation CDVFileTransferDelegate
 
-@synthesize callbackId, connection, source, target, responseData, command, bytesTransfered, bytesExpected, direction, responseCode, objectId;
+@synthesize callbackId, connection = _connection, source, target, responseData, command, bytesTransfered, bytesExpected, direction, responseCode, objectId, targetFileHandle;
 
 - (void)connectionDidFinishLoading:(NSURLConnection*)connection
 {
     NSString* uploadResponse = nil;
     NSString* downloadResponse = nil;
-    BOOL downloadWriteOK = NO;
     NSMutableDictionary* uploadResult;
     CDVPluginResult* result = nil;
-    NSError* __autoreleasing error = nil;
-    NSString* parentPath;
     BOOL bDirRequest = NO;
     CDVFile* file;
 
@@ -491,40 +537,15 @@
         }
     }
     if (self.direction == CDV_TRANSFER_DOWNLOAD) {
-        DLog(@"Write file %@", target);
-        // error=[[NSError alloc]init];
+        if (self.targetFileHandle) {
+            [self.targetFileHandle closeFile];
+            self.targetFileHandle = nil;
+            DLog(@"File Transfer Download success");
 
-        if ((self.responseCode >= 200) && (self.responseCode < 300)) {
-            @try {
-                parentPath = [self.target stringByDeletingLastPathComponent];
-
-                // check if the path exists => create directories if needed
-                if (![[NSFileManager defaultManager] fileExistsAtPath:parentPath]) {
-                    [[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:nil];
-                }
-
-                downloadWriteOK = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error];
-
-                if (downloadWriteOK == NO) {
-                    // send our results back
-                    downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
-                } else {
-                    DLog(@"File Transfer Download success");
-
-                    file = [[CDVFile alloc] init];
-
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[file getDirectoryEntry:target isDirectory:bDirRequest]];
-                }
-            }
-            @catch(id exception) {
-                // jump back to main thread
-                downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
-            }
+            file = [[CDVFile alloc] init];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[file getDirectoryEntry:target isDirectory:bDirRequest]];
         } else {
             downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
-
             result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
         }
     }
@@ -533,11 +554,28 @@
 
     // remove connection for activeTransfers
     [command.activeTransfers removeObjectForKey:objectId];
+
+    // remove background id task in case our upload was done in the background
+    [[UIApplication sharedApplication] endBackgroundTask:self.command.backgroundTaskID];
+    self.command.backgroundTaskID = UIBackgroundTaskInvalid;
+}
+
+- (void)cancelTransferWithError:(NSURLConnection*)connection errorMessage:(NSString*)errorMessage
+{
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[self.command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:self.source AndTarget:self.target AndHttpStatus:self.responseCode AndBody:errorMessage]];
+
+    NSLog(@"File Transfer Error: %@", errorMessage);
+    [connection cancel];
+    [self.command.activeTransfers removeObjectForKey:self.objectId];
+    [self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
 }
 
 - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
 {
+    NSError* __autoreleasing error = nil;
+
     self.mimeType = [response MIMEType];
+    self.targetFileHandle = nil;
 
     // required for iOS 4.3, for some reason; response is
     // a plain NSURLResponse, not the HTTP subclass
@@ -546,6 +584,11 @@
 
         self.responseCode = [httpResponse statusCode];
         self.bytesExpected = [response expectedContentLength];
+        if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode == 200) && (self.bytesExpected == NSURLResponseUnknownLength)) {
+            // Kick off HEAD request to server to get real length
+            // bytesExpected will be updated when that response is returned
+            self.entityLengthRequest = [[CDVFileTransferEntityLengthRequest alloc] initWithOriginalRequest:connection.currentRequest andDelegate:self];
+        }
     } else if ([response.URL isFileURL]) {
         NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:[response.URL path] error:nil];
         self.responseCode = 200;
@@ -554,6 +597,31 @@
         self.responseCode = 200;
         self.bytesExpected = NSURLResponseUnknownLength;
     }
+    if ((self.direction == CDV_TRANSFER_DOWNLOAD) && (self.responseCode >= 200) && (self.responseCode < 300)) {
+        // Download response is okay; begin streaming output to file
+        NSString* parentPath = [self.target stringByDeletingLastPathComponent];
+
+        // create parent directories if needed
+        if ([[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:&error] == NO) {
+            if (error) {
+                [self cancelTransferWithError:connection errorMessage:[NSString stringWithFormat:@"Could not create path to save downloaded file: %@", [error localizedDescription]]];
+            } else {
+                [self cancelTransferWithError:connection errorMessage:@"Could not create path to save downloaded file"];
+            }
+            return;
+        }
+        // create target file
+        if ([[NSFileManager defaultManager] createFileAtPath:self.target contents:nil attributes:nil] == NO) {
+            [self cancelTransferWithError:connection errorMessage:@"Could not create target file"];
+            return;
+        }
+        // open target file for writing
+        self.targetFileHandle = [NSFileHandle fileHandleForWritingAtPath:self.target];
+        if (self.targetFileHandle == nil) {
+            [self cancelTransferWithError:connection errorMessage:@"Could not open target file for writing"];
+        }
+        DLog(@"Streaming to file %@", target);
+    }
 }
 
 - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
@@ -571,10 +639,30 @@
 - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
 {
     self.bytesTransfered += data.length;
-    [self.responseData appendData:data];
+    if (self.targetFileHandle) {
+        [self.targetFileHandle writeData:data];
+    } else {
+        [self.responseData appendData:data];
+    }
+    [self updateProgress];
+}
 
+- (void)updateBytesExpected:(NSInteger)newBytesExpected
+{
+    DLog(@"Updating bytesExpected to %d", newBytesExpected);
+    self.bytesExpected = newBytesExpected;
+    [self updateProgress];
+}
+
+- (void)updateProgress
+{
     if (self.direction == CDV_TRANSFER_DOWNLOAD) {
         BOOL lengthComputable = (self.bytesExpected != NSURLResponseUnknownLength);
+        // If the response is GZipped, and we have an outstanding HEAD request to get
+        // the length, then hold off on sending progress events.
+        if (!lengthComputable && (self.entityLengthRequest != nil)) {
+            return;
+        }
         NSMutableDictionary* downloadProgress = [NSMutableDictionary dictionaryWithCapacity:3];
         [downloadProgress setObject:[NSNumber numberWithBool:lengthComputable] forKey:@"lengthComputable"];
         [downloadProgress setObject:[NSNumber numberWithInt:self.bytesTransfered] forKey:@"loaded"];
@@ -618,6 +706,7 @@
 {
     if ((self = [super init])) {
         self.responseData = [NSMutableData data];
+        self.targetFileHandle = nil;
     }
     return self;
 }
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h
index f63250a..343f40d 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h
@@ -24,28 +24,21 @@
 
 @class CDVInAppBrowserViewController;
 
-@protocol CDVInAppBrowserNavigationDelegate <NSObject>
-
-- (void)browserLoadStart:(NSURL*)url;
-- (void)browserLoadStop:(NSURL*)url;
-- (void)browserLoadError:(NSError*)error forUrl:(NSURL*)url;
-- (void)browserExit;
-
-@end
-
-@interface CDVInAppBrowser : CDVPlugin <CDVInAppBrowserNavigationDelegate>
+@interface CDVInAppBrowser : CDVPlugin {
+    BOOL _injectedIframeBridge;
+}
 
 @property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController;
 @property (nonatomic, copy) NSString* callbackId;
 
 - (void)open:(CDVInvokedUrlCommand*)command;
 - (void)close:(CDVInvokedUrlCommand*)command;
+- (void)injectScriptCode:(CDVInvokedUrlCommand*)command;
 
 @end
 
 @interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate>{
     @private
-    NSURL* _requestedURL;
     NSString* _userAgent;
     NSString* _prevUserAgent;
     NSInteger _userAgentLockToken;
@@ -61,7 +54,8 @@
 @property (nonatomic, strong) IBOutlet UIToolbar* toolbar;
 
 @property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate;
-@property (nonatomic, weak) id <CDVInAppBrowserNavigationDelegate> navigationDelegate;
+@property (nonatomic, weak) CDVInAppBrowser* navigationDelegate;
+@property (nonatomic) NSURL* requestedURL;
 
 - (void)close;
 - (void)navigateTo:(NSURL*)url;
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m
index d001dfd..f366bd8 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m
@@ -20,6 +20,7 @@
 #import "CDVInAppBrowser.h"
 #import "CDVPluginResult.h"
 #import "CDVUserAgentUtil.h"
+#import "CDVJSON.h"
 
 #define    kInAppBrowserTargetSelf @"_self"
 #define    kInAppBrowserTargetSystem @"_system"
@@ -159,35 +160,161 @@
     }
 }
 
-#pragma mark CDVInAppBrowserNavigationDelegate
+// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
+// provides a consistent method for injecting JavaScript code into the document.
+//
+// If a wrapper string is supplied, then the source string will be JSON-encoded (adding
+// quotes) and wrapped using string formatting. (The wrapper string should have a single
+// '%@' marker).
+//
+// If no wrapper is supplied, then the source string is executed directly.
 
-- (void)browserLoadStart:(NSURL*)url
+- (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper
 {
+    if (!_injectedIframeBridge) {
+        _injectedIframeBridge = YES;
+        // Create an iframe bridge in the new document to communicate with the CDVInAppBrowserViewController
+        [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){var e = _cdvIframeBridge = d.createElement('iframe');e.style.display='none';d.body.appendChild(e);})(document)"];
+    }
+
+    if (jsWrapper != nil) {
+        NSString* sourceArrayString = [@[source] JSONString];
+        if (sourceArrayString) {
+            NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)];
+            NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString];
+            [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject];
+        }
+    } else {
+        [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
+    }
+}
+
+- (void)injectScriptCode:(CDVInvokedUrlCommand*)command
+{
+    NSString* jsWrapper = nil;
+
+    if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+        jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+window.escape(JSON.stringify([eval(%%@)]));", command.callbackId];
+    }
+    [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectScriptFile:(CDVInvokedUrlCommand*)command
+{
+    NSString* jsWrapper;
+
+    if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+        jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+    } else {
+        jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)";
+    }
+    [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectStyleCode:(CDVInvokedUrlCommand*)command
+{
+    NSString* jsWrapper;
+
+    if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+        jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+    } else {
+        jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)";
+    }
+    [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectStyleFile:(CDVInvokedUrlCommand*)command
+{
+    NSString* jsWrapper;
+
+    if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+        jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+    } else {
+        jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
+    }
+    [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+/**
+ * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging
+ * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no
+ * other code execution is possible.
+ *
+ * To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form:
+ *
+ * gap-iab://<callbackId>/<arguments>
+ *
+ * where <callbackId> is the string id of the callback to trigger (something like "InAppBrowser0123456789")
+ *
+ * If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded
+ * value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION
+ * is returned if the JSON is invalid.
+ */
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+    NSURL* url = request.URL;
+
+    // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute,
+    // and the path, if present, should be a JSON-encoded value to pass to the callback.
+    if ([[url scheme] isEqualToString:@"gap-iab"]) {
+        NSString* scriptCallbackId = [url host];
+        CDVPluginResult* pluginResult = nil;
+
+        if ([scriptCallbackId hasPrefix:@"InAppBrowser"]) {
+            NSString* scriptResult = [url path];
+            NSError* __autoreleasing error = nil;
+
+            // The message should be a JSON-encoded array of the result of the script which executed.
+            if ((scriptResult != nil) && ([scriptResult length] > 1)) {
+                scriptResult = [scriptResult substringFromIndex:1];
+                NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
+                if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) {
+                    pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult];
+                } else {
+                    pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION];
+                }
+            } else {
+                pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
+            }
+            [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId];
+            return NO;
+        }
+    }
+    return YES;
+}
+
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+    _injectedIframeBridge = NO;
     if (self.callbackId != nil) {
+        NSString* url = [[self.inAppBrowserViewController requestedURL] absoluteString];
         CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
-                                                      messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}];
+                                                      messageAsDictionary:@{@"type":@"loadstart", @"url":url}];
         [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
 
         [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
     }
 }
 
-- (void)browserLoadStop:(NSURL*)url
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
 {
     if (self.callbackId != nil) {
+        // TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected).
+        NSString* url = [[self.inAppBrowserViewController requestedURL] absoluteString];
         CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
-                                                      messageAsDictionary:@{@"type":@"loadstop", @"url":[url absoluteString]}];
+                                                      messageAsDictionary:@{@"type":@"loadstop", @"url":url}];
         [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
 
         [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
     }
 }
 
-- (void)browserLoadError:(NSError*)error forUrl:(NSURL*)url
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
 {
     if (self.callbackId != nil) {
+        NSString* url = [[self.inAppBrowserViewController requestedURL] absoluteString];
         CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
-                                                      messageAsDictionary:@{@"type":@"loaderror", @"url":[url absoluteString], @"code": [NSNumber numberWithInt:error.code], @"message": error.localizedDescription}];
+                                                      messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInt:error.code], @"message": error.localizedDescription}];
         [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
 
         [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
@@ -214,6 +341,8 @@
 
 @implementation CDVInAppBrowserViewController
 
+@synthesize requestedURL = _requestedURL;
+
 - (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent
 {
     self = [super init];
@@ -431,18 +560,13 @@
     self.forwardButton.enabled = theWebView.canGoForward;
 
     [self.spinner startAnimating];
+
+    return [self.navigationDelegate webViewDidStartLoad:theWebView];
 }
 
-- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
 {
-    if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadStart:)]) {
-        NSURL* url = request.URL;
-        if (url == nil) {
-            url = _requestedURL;
-        }
-        [self.navigationDelegate browserLoadStart:url];
-    }
-    return YES;
+    return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
 }
 
 - (void)webViewDidFinishLoad:(UIWebView*)theWebView
@@ -471,10 +595,7 @@
         [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
     }
 
-    if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadStop:)]) {
-        NSURL* url = theWebView.request.URL;
-        [self.navigationDelegate browserLoadStop:url];
-    }
+    [self.navigationDelegate webViewDidFinishLoad:theWebView];
 }
 
 - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
@@ -488,10 +609,7 @@
 
     self.addressLabel.text = @"Load Error";
 
-    if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadError:forUrl:)]) {
-        NSURL* url = theWebView.request.URL;
-        [self.navigationDelegate browserLoadError:error forUrl:url];
-    }
+    [self.navigationDelegate webView:theWebView didFailLoadWithError:error];
 }
 
 #pragma mark CDVScreenOrientationDelegate
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m b/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
index 90c96d2..93cafb8 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
@@ -20,8 +20,15 @@
 #import "CDVJpegHeaderWriter.h"
 #include "CDVExif.h"
 
-// tag info shorthand, tagno: tag number, typecode: data type:, components: number of components
+/* macros for tag info shorthand:
+   tagno        : tag number
+   typecode     : data type
+   components   : number of components
+   appendString (TAGINF_W_APPEND only) : string to append to data
+      Exif date data format include an extra 0x00 to the end of the data
+ */
 #define TAGINF(tagno, typecode, components) [NSArray arrayWithObjects: tagno, typecode, components, nil]
+#define TAGINF_W_APPEND(tagno, typecode, components, appendString) [NSArray arrayWithObjects: tagno, typecode, components, appendString, nil]
 
 const uint mJpegId = 0xffd8; // JPEG format marker
 const uint mExifMarker = 0xffe1; // APP1 jpeg header marker
@@ -38,7 +45,7 @@
     // supported tags for exif IFD
     IFD0TagFormatDict = [[NSDictionary alloc] initWithObjectsAndKeys:
                   //      TAGINF(@"010e", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"ImageDescription",
-                        TAGINF(@"0132", [NSNumber numberWithInt:EDT_ASCII_STRING], @20), @"DateTime",
+                        TAGINF_W_APPEND(@"0132", [NSNumber numberWithInt:EDT_ASCII_STRING], @20, @"00"), @"DateTime",
                         TAGINF(@"010f", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Make",
                         TAGINF(@"0110", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Model",
                         TAGINF(@"0131", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Software",
@@ -68,8 +75,8 @@
                            //TAGINF(@"9202",[NSNumber numberWithInt:EDT_URATIONAL],@1), @"ApertureValue",
                            //TAGINF(@"9203",[NSNumber numberWithInt:EDT_SRATIONAL],@1), @"BrightnessValue",
                            TAGINF(@"a001",[NSNumber numberWithInt:EDT_USHORT],@1), @"ColorSpace",
-                           TAGINF(@"9004",[NSNumber numberWithInt:EDT_ASCII_STRING],@20), @"DateTimeDigitized",
-                           TAGINF(@"9003",[NSNumber numberWithInt:EDT_ASCII_STRING],@20), @"DateTimeOriginal",
+                           TAGINF_W_APPEND(@"9004",[NSNumber numberWithInt:EDT_ASCII_STRING],@20,@"00"), @"DateTimeDigitized",
+                           TAGINF_W_APPEND(@"9003",[NSNumber numberWithInt:EDT_ASCII_STRING],@20,@"00"), @"DateTimeOriginal",
                            TAGINF(@"a402", [NSNumber numberWithInt:EDT_USHORT], @1), @"ExposureMode",
                            TAGINF(@"8822", [NSNumber numberWithInt:EDT_USHORT], @1), @"ExposureProgram",
                            //TAGINF(@"829a", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"ExposureTime",
@@ -167,12 +174,14 @@
     NSString * tiffheader = @"4d4d002a";
     //first IFD offset from the Tiff header to IFD0. Since we are writing it, we know it's address 0x08
     NSString * ifd0offset = @"00000008";
+    // current offset to next data area
+    int currentDataOffset = 0;
     
     //data labeled as TIFF in UIImagePickerControllerMediaMetaData is part of the EXIF IFD0 portion of APP1
-    exifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{TIFF}"] withFormatDict: IFD0TagFormatDict isIFD0:YES];
+    exifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{TIFF}"] withFormatDict: IFD0TagFormatDict isIFD0:YES currentDataOffset:&currentDataOffset];
 
     //data labeled as EXIF in UIImagePickerControllerMediaMetaData is part of the EXIF Sub IFD portion of APP1
-    subExifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{Exif}"] withFormatDict: SubIFDTagFormatDict isIFD0:NO];
+    subExifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{Exif}"] withFormatDict: SubIFDTagFormatDict isIFD0:NO currentDataOffset:&currentDataOffset];
     /*
     NSLog(@"SUB EXIF IFD %@  WITH SIZE: %d",exifIFD,[exifIFD length]);
     
@@ -192,8 +201,11 @@
 }
 
 // returns hex string representing a valid exif information file directory constructed from the datadict and formatdict
-- (NSString*) createExifIFDFromDict : (NSDictionary*) datadict withFormatDict : (NSDictionary*) formatdict isIFD0 : (BOOL) ifd0flag {
-    NSArray * datakeys = [datadict allKeys]; // all known data keys 
+- (NSString*) createExifIFDFromDict : (NSDictionary*) datadict
+                     withFormatDict : (NSDictionary*) formatdict
+                             isIFD0 : (BOOL) ifd0flag
+                  currentDataOffset : (int*) dataoffset {
+    NSArray * datakeys = [datadict allKeys]; // all known data keys
     NSArray * knownkeys = [formatdict  allKeys]; // only keys in knowkeys are considered for entry in this IFD
     NSMutableArray * ifdblock = [[NSMutableArray alloc] initWithCapacity: [datadict count]]; // all ifd entries
     NSMutableArray * ifddatablock = [[NSMutableArray alloc] initWithCapacity: [datadict count]]; // data block entries
@@ -225,13 +237,13 @@
     NSMutableString * exifstr = [[NSMutableString alloc] initWithCapacity: [ifdblock count] * 24];
     NSMutableString * dbstr = [[NSMutableString alloc] initWithCapacity: 100];
     
-    int addr=0; // current offset/address in datablock
+    int addr=*dataoffset; // current offset/address in datablock
     if (ifd0flag) {
         // calculate offset to datablock based on ifd file entry count
-        addr = 14+(12*([ifddatablock count]+1)); // +1 for tag 0x8769, exifsubifd offset
+        addr += 14+(12*([ifddatablock count]+1)); // +1 for tag 0x8769, exifsubifd offset
     } else {
-        // same calculation as above, but no exifsubifd offset
-        addr = 14+12*[ifddatablock count];
+        // current offset + numSubIFDs (2-bytes) + 12*numSubIFDs + endMarker (4-bytes)
+        addr += 2+(12*[ifddatablock count])+4;
     }
     
     for (int i = 0; i < [ifdblock count]; i++) {
@@ -244,8 +256,14 @@
             [exifstr appendFormat : @"%@%@", entry, data];
         } else {
             [exifstr appendFormat : @"%@%08x", entry, addr];
-            [dbstr appendFormat: @"%@", data]; 
+            [dbstr appendFormat: @"%@", data];
             addr+= [data length] / 2;
+            /*
+            NSLog(@"=====data-length[%i]=======",[data length]);
+            NSLog(@"addr-offset[%i]",addr);
+            NSLog(@"entry[%@]",entry);
+            NSLog(@"data[%@]",data);
+             */
         }
     }
     
@@ -258,7 +276,8 @@
         [self appendExifOffsetTagTo: exifstr
                         withOffset : offset];
         entrycount++;
-    } 
+    }
+    *dataoffset = addr;
     return [[NSString alloc] initWithFormat: @"%04x%@%@%@",
             entrycount,
             exifstr,
@@ -307,6 +326,7 @@
     NSMutableString * datastr = nil;
     NSNumber * tmp = nil;
     NSNumber * formatcode = [dataformat objectAtIndex:1];
+    NSUInteger formatItemsCount = [dataformat count];
     NSNumber * num = @0;
     NSNumber * denom = @0;
     
@@ -318,6 +338,11 @@
             for (int i = 0; i < [data length]; i++) {
                 [datastr appendFormat:@"%02x",[data characterAtIndex:i]];
             }
+            if (formatItemsCount > 3) {
+                // We have additional data to append.
+                // currently used by Date format to append final 0x00 but can be used by other data types as well in the future
+                [datastr appendString:[dataformat objectAtIndex:3]];
+            }
             if ([datastr length] < 8) {
                 NSString * format = [NSString stringWithFormat:@"%%0%dd", 8 - [datastr length]];
                 [datastr appendFormat:format,0];
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
index 6e25a27..33ba1c4 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
@@ -23,10 +23,10 @@
 #import "NSMutableArray+QueueAdditions.h"
 #import "CDVCommandDelegate.h"
 
-NSString* const CDVPageDidLoadNotification;
-NSString* const CDVPluginHandleOpenURLNotification;
-NSString* const CDVPluginResetNotification;
-NSString* const CDVLocalNotification;
+extern NSString* const CDVPageDidLoadNotification;
+extern NSString* const CDVPluginHandleOpenURLNotification;
+extern NSString* const CDVPluginResetNotification;
+extern NSString* const CDVLocalNotification;
 
 @interface CDVPlugin : NSObject {}
 
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVSound.m b/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
index 88fbbd6..71eab59 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
@@ -273,6 +273,9 @@
     } else {
         audioFile = [[self soundCache] objectForKey:mediaId];
         audioFile.volume = volume;
+        if (audioFile.player) {
+            audioFile.player.volume = [volume floatValue];
+        }
         [[self soundCache] setObject:audioFile forKey:mediaId];
     }
 
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVDebugConsole.h b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.h
similarity index 85%
rename from lib/cordova-ios/CordovaLib/Classes/CDVDebugConsole.h
rename to lib/cordova-ios/CordovaLib/Classes/CDVTimer.h
index 6a0a185..6d31593 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVDebugConsole.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.h
@@ -18,11 +18,10 @@
  */
 
 #import <Foundation/Foundation.h>
-#import <UIKit/UIKit.h>
-#import "CDVPlugin.h"
 
-@interface CDVDebugConsole : CDVPlugin {}
+@interface CDVTimer : NSObject
 
-- (void)log:(CDVInvokedUrlCommand*)command;
++ (void)start:(NSString*)name;
++ (void)stop:(NSString*)name;
 
 @end
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m
new file mode 100644
index 0000000..784e94d
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m
@@ -0,0 +1,123 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import "CDVTimer.h"
+
+#pragma mark CDVTimerItem
+
+@interface CDVTimerItem : NSObject
+
+@property (nonatomic, strong) NSString* name;
+@property (nonatomic, strong) NSDate* started;
+@property (nonatomic, strong) NSDate* ended;
+
+- (void)log;
+
+@end
+
+@implementation CDVTimerItem
+
+- (void)log
+{
+    NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0);
+}
+
+@end
+
+#pragma mark CDVTimer
+
+@interface CDVTimer ()
+
+@property (nonatomic, strong) NSMutableDictionary* items;
+
+@end
+
+@implementation CDVTimer
+
+#pragma mark object methods
+
+- (id)init
+{
+    if (self = [super init]) {
+        self.items = [NSMutableDictionary dictionaryWithCapacity:6];
+    }
+
+    return self;
+}
+
+- (void)add:(NSString*)name
+{
+    if ([self.items objectForKey:[name lowercaseString]] == nil) {
+        CDVTimerItem* item = [CDVTimerItem new];
+        item.name = name;
+        item.started = [NSDate new];
+        [self.items setObject:item forKey:[name lowercaseString]];
+    } else {
+        NSLog(@"Timer called '%@' already exists.", name);
+    }
+}
+
+- (void)remove:(NSString*)name
+{
+    CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]];
+
+    if (item != nil) {
+        item.ended = [NSDate new];
+        [item log];
+        [self.items removeObjectForKey:[name lowercaseString]];
+    } else {
+        NSLog(@"Timer called '%@' does not exist.", name);
+    }
+}
+
+- (void)removeAll
+{
+    [self.items removeAllObjects];
+}
+
+#pragma mark class methods
+
++ (void)start:(NSString*)name
+{
+    [[CDVTimer sharedInstance] add:name];
+}
+
++ (void)stop:(NSString*)name
+{
+    [[CDVTimer sharedInstance] remove:name];
+}
+
++ (void)clearAll
+{
+    [[CDVTimer sharedInstance] removeAll];
+}
+
++ (CDVTimer*)sharedInstance
+{
+    static dispatch_once_t pred = 0;
+    __strong static CDVTimer* _sharedObject = nil;
+
+    dispatch_once(&pred, ^{
+            _sharedObject = [[self alloc] init];
+        });
+
+    return _sharedObject;
+}
+
+@end
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
index c7ad01b..94f4552 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
@@ -128,7 +128,7 @@
 
 - (void)keyboardWillShowOrHide:(NSNotification*)notif
 {
-    if (![@"true" isEqualToString:self.settings[@"KeyboardShrinksView"]]) {
+    if (![@"true" isEqualToString : self.settings[@"KeyboardShrinksView"]]) {
         return;
     }
     BOOL showEvent = [notif.name isEqualToString:UIKeyboardWillShowNotification];
@@ -290,18 +290,18 @@
         [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification
                                                           object:nil
                                                            queue:[NSOperationQueue mainQueue]
-                                                      usingBlock:^(NSNotification * notification) {
-                // we can't hide it here because the accessory bar hasn't been created yet, so we delay on the queue
-                [weakSelf performSelector:@selector(hideKeyboardFormAccessoryBar) withObject:nil afterDelay:0];
-            }];
+                                                      usingBlock:^(NSNotification* notification) {
+            // we can't hide it here because the accessory bar hasn't been created yet, so we delay on the queue
+            [weakSelf performSelector:@selector(hideKeyboardFormAccessoryBar) withObject:nil afterDelay:0];
+        }];
     }
 
     /*
      * Fire up CDVLocalStorage to work-around WebKit storage limitations: on all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for cloud backup.
      */
-    if (IsAtLeastiOSVersion (@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) ||
-            ([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion (@"6.0")))) {
-        [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass ([CDVLocalStorage class])];
+    if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) ||
+        ([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion(@"6.0")))) {
+        [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])];
     }
 
     /*
@@ -342,7 +342,7 @@
     /*
      * iOS 6.0 UIWebView properties
      */
-    if (IsAtLeastiOSVersion (@"6.0")) {
+    if (IsAtLeastiOSVersion(@"6.0")) {
         BOOL keyboardDisplayRequiresUserAction = YES; // KeyboardDisplayRequiresUserAction - defaults to YES
         if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] != nil) {
             if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"]) {
@@ -368,8 +368,16 @@
         }
     }
 
-    for (NSString* pluginName in self.startupPluginNames) {
-        [self getCommandInstance:pluginName];
+    if ([self.startupPluginNames count] > 0) {
+        [CDVTimer start:@"TotalPluginStartup"];
+
+        for (NSString* pluginName in self.startupPluginNames) {
+            [CDVTimer start:pluginName];
+            [self getCommandInstance:pluginName];
+            [CDVTimer stop:pluginName];
+        }
+
+        [CDVTimer stop:@"TotalPluginStartup"];
     }
 
     // TODO: Remove this explicit instantiation once we move to cordova-CLI.
@@ -379,16 +387,16 @@
 
     // /////////////////
     [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
-            _userAgentLockToken = lockToken;
-            [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
-            if (!loadErr) {
-                NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
-                [self.webView loadRequest:appReq];
-            } else {
-                NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
-                [self.webView loadHTMLString:html baseURL:nil];
-            }
-        }];
+        _userAgentLockToken = lockToken;
+        [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
+        if (!loadErr) {
+            NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
+            [self.webView loadRequest:appReq];
+        } else {
+            NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
+            [self.webView loadHTMLString:html baseURL:nil];
+        }
+    }];
 }
 
 - (void)hideKeyboardFormAccessoryBar
@@ -607,12 +615,6 @@
     // It's safe to release the lock even if this is just a sub-frame that's finished loading.
     [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
 
-    // The .onNativeReady().fire() will work when cordova.js is already loaded.
-    // The _nativeReady = true; is used when this is run before cordova.js is loaded.
-    NSString* nativeReady = @"try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}";
-    // Don't use [commandDelegate evalJs] here since it relies on cordova.js being loaded already.
-    [self.webView stringByEvaluatingJavaScriptFromString:nativeReady];
-
     /*
      * Hide the Top Activity THROBBER in the Battery Bar
      */
@@ -783,7 +785,7 @@
 
     id obj = [self.pluginObjects objectForKey:className];
     if (!obj) {
-        obj = [[NSClassFromString (className)alloc] initWithWebView:webView];
+        obj = [[NSClassFromString(className)alloc] initWithWebView:webView];
 
         if (obj != nil) {
             [self registerPlugin:obj withClassName:className];
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
index 8a89a22..a4d78bd 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
@@ -30,6 +30,7 @@
     NSInteger _loadCount;
     NSInteger _state;
     NSInteger _curLoadToken;
+    NSInteger _loadStartPollCount;
 }
 
 - (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate;
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
index a72bfb9..fd9c032 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
@@ -17,14 +17,77 @@
  under the License.
  */
 
+//
+// Testing shows:
+//
+// In all cases, webView.request.URL is the previous page's URL (or empty) during the didStartLoad callback.
+// When loading a page with a redirect:
+// 1. shouldStartLoading (requestURL is target page)
+// 2. didStartLoading
+// 3. shouldStartLoading (requestURL is redirect target)
+// 4. didFinishLoad (request.URL is redirect target)
+//
+// Note the lack of a second didStartLoading **
+//
+// When loading a page with iframes:
+// 1. shouldStartLoading (requestURL is main page)
+// 2. didStartLoading
+// 3. shouldStartLoading (requestURL is one of the iframes)
+// 4. didStartLoading
+// 5. didFinishLoad
+// 6. didFinishLoad
+//
+// Note there is no way to distinguish which didFinishLoad maps to which didStartLoad **
+//
+// Loading a page by calling window.history.go(-1):
+// 1. didStartLoading
+// 2. didFinishLoad
+//
+// Note the lack of a shouldStartLoading call **
+// Actually - this is fixed on iOS6. iOS6 has a shouldStart. **
+//
+// Loading a page by calling location.reload()
+// 1. shouldStartLoading
+// 2. didStartLoading
+// 3. didFinishLoad
+//
+// Loading a page with an iframe that fails to load:
+// 1. shouldStart (main page)
+// 2. didStart
+// 3. shouldStart (iframe)
+// 4. didStart
+// 5. didFailWithError
+// 6. didFinish
+//
+// Loading a page with an iframe that fails to load due to an invalid URL:
+// 1. shouldStart (main page)
+// 2. didStart
+// 3. shouldStart (iframe)
+// 5. didFailWithError
+// 6. didFinish
+//
+// This case breaks our logic since there is a missing didStart. To prevent this,
+// we check URLs in shouldStart and return NO if they are invalid.
+//
+// Loading a page with an invalid URL
+// 1. shouldStart (main page)
+// 2. didFailWithError
+//
+// TODO: Record order when page is re-navigated before the first navigation finishes.
+//
+
 #import "CDVWebViewDelegate.h"
 #import "CDVAvailability.h"
 
+// #define VerboseLog NSLog
+#define VerboseLog(...) do {} while (0)
+
 typedef enum {
-    STATE_NORMAL,
-    STATE_SHOULD_LOAD_MISSING,
-    STATE_WAITING_FOR_START,
-    STATE_WAITING_FOR_FINISH
+    STATE_IDLE,
+    STATE_WAITING_FOR_LOAD_START,
+    STATE_WAITING_FOR_LOAD_FINISH,
+    STATE_IOS5_POLLING_FOR_LOAD_START,
+    STATE_IOS5_POLLING_FOR_LOAD_FINISH
 } State;
 
 @implementation CDVWebViewDelegate
@@ -35,7 +98,7 @@
     if (self != nil) {
         _delegate = delegate;
         _loadCount = -1;
-        _state = STATE_NORMAL;
+        _state = STATE_IDLE;
     }
     return self;
 }
@@ -62,31 +125,41 @@
 
 - (void)pollForPageLoadStart:(UIWebView*)webView
 {
-    if ((_state != STATE_WAITING_FOR_START) && (_state != STATE_SHOULD_LOAD_MISSING)) {
+    if (_state != STATE_IOS5_POLLING_FOR_LOAD_START) {
         return;
     }
     if (![self isJsLoadTokenSet:webView]) {
-        _state = STATE_WAITING_FOR_FINISH;
+        VerboseLog(@"Polled for page load start. result = YES!");
+        _state = STATE_IOS5_POLLING_FOR_LOAD_FINISH;
         [self setLoadToken:webView];
         if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
             [_delegate webViewDidStartLoad:webView];
         }
         [self pollForPageLoadFinish:webView];
+    } else {
+        VerboseLog(@"Polled for page load start. result = NO");
+        // Poll only for 1 second, and then fall back on checking only when delegate methods are called.
+        ++_loadStartPollCount;
+        if (_loadStartPollCount < (1000 * .05)) {
+            [self performSelector:@selector(pollForPageLoadStart:) withObject:webView afterDelay:.05];
+        }
     }
 }
 
 - (void)pollForPageLoadFinish:(UIWebView*)webView
 {
-    if (_state != STATE_WAITING_FOR_FINISH) {
+    if (_state != STATE_IOS5_POLLING_FOR_LOAD_FINISH) {
         return;
     }
     if ([self isPageLoaded:webView]) {
-        _state = STATE_SHOULD_LOAD_MISSING;
+        VerboseLog(@"Polled for page load finish. result = YES!");
+        _state = STATE_IDLE;
         if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
             [_delegate webViewDidFinishLoad:webView];
         }
     } else {
-        [self performSelector:@selector(pollForPageLoaded) withObject:webView afterDelay:50];
+        VerboseLog(@"Polled for page load finish. result = NO");
+        [self performSelector:@selector(pollForPageLoadFinish:) withObject:webView afterDelay:.05];
     }
 }
 
@@ -98,73 +171,163 @@
         shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
     }
 
+    VerboseLog(@"webView shouldLoad=%d (before) state=%d loadCount=%d URL=%@", shouldLoad, _state, _loadCount, request.URL);
+
     if (shouldLoad) {
         BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
         if (isTopLevelNavigation) {
-            _loadCount = 0;
-            _state = STATE_NORMAL;
+            switch (_state) {
+                case STATE_WAITING_FOR_LOAD_FINISH:
+                    // Redirect case.
+                    // We expect loadCount == 1.
+                    if (_loadCount != 1) {
+                        NSLog(@"CDVWebViewDelegate: Detected redirect when loadCount=%d", _loadCount);
+                    }
+                    break;
+
+                case STATE_IDLE:
+                case STATE_IOS5_POLLING_FOR_LOAD_START:
+                    // Page navigation start.
+                    _loadCount = 0;
+                    _state = STATE_WAITING_FOR_LOAD_START;
+                    break;
+
+                default:
+                    NSLog(@"CDVWebViewDelegate: Navigation started when state=%d", _state);
+                    _loadCount = 0;
+                    _state = STATE_WAITING_FOR_LOAD_START;
+                    if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+                        [_delegate webView:webView didFailLoadWithError:nil];
+                    }
+            }
+        } else {
+            // Deny invalid URLs so that we don't get the case where we go straight from
+            // webViewShouldLoad -> webViewDidFailLoad (messes up _loadCount).
+            shouldLoad = shouldLoad && [NSURLConnection canHandleRequest:request];
         }
+        VerboseLog(@"webView shouldLoad=%d (after) isTopLevelNavigation=%d state=%d loadCount=%d", shouldLoad, isTopLevelNavigation, _state, _loadCount);
     }
     return shouldLoad;
 }
 
 - (void)webViewDidStartLoad:(UIWebView*)webView
 {
-    if (_state == STATE_NORMAL) {
-        if (_loadCount == 0) {
-            if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
-                [_delegate webViewDidStartLoad:webView];
+    VerboseLog(@"webView didStartLoad (before). state=%d loadCount=%d", _state, _loadCount);
+    BOOL fireCallback = NO;
+    switch (_state) {
+        case STATE_IDLE:
+            if (IsAtLeastiOSVersion(@"6.0")) {
+                break;
             }
-            _loadCount += 1;
-        } else if (_loadCount > 0) {
-            _loadCount += 1;
-        } else if (!IsAtLeastiOSVersion(@"6.0")) {
             // If history.go(-1) is used pre-iOS6, the shouldStartLoadWithRequest function is not called.
             // Without shouldLoad, we can't distinguish an iframe from a top-level navigation.
             // We could try to distinguish using [UIWebView canGoForward], but that's too much complexity,
             // and would work only on the first time it was used.
 
             // Our work-around is to set a JS variable and poll until it disappears (from a naviagtion).
-            _state = STATE_WAITING_FOR_START;
+            _state = STATE_IOS5_POLLING_FOR_LOAD_START;
+            _loadStartPollCount = 0;
             [self setLoadToken:webView];
-        }
-    } else {
-        [self pollForPageLoadStart:webView];
-        [self pollForPageLoadFinish:webView];
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_WAITING_FOR_LOAD_START:
+            if (_loadCount != 0) {
+                NSLog(@"CDVWebViewDelegate: Unexpected loadCount in didStart. count=%d", _loadCount);
+            }
+            fireCallback = YES;
+            _state = STATE_WAITING_FOR_LOAD_FINISH;
+            _loadCount = 1;
+            break;
+
+        case STATE_WAITING_FOR_LOAD_FINISH:
+            _loadCount += 1;
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_START:
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+            [self pollForPageLoadFinish:webView];
+            break;
+
+        default:
+            NSLog(@"CDVWebViewDelegate: Unexpected didStart with state=%d loadCount=%d", _state, _loadCount);
+    }
+    VerboseLog(@"webView didStartLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
+    if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
+        [_delegate webViewDidStartLoad:webView];
     }
 }
 
 - (void)webViewDidFinishLoad:(UIWebView*)webView
 {
-    if (_state == STATE_NORMAL) {
-        if (_loadCount == 1) {
-            if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
-                [_delegate webViewDidFinishLoad:webView];
+    VerboseLog(@"webView didFinishLoad (before). state=%d loadCount=%d", _state, _loadCount);
+    BOOL fireCallback = NO;
+    switch (_state) {
+        case STATE_IDLE:
+            break;
+
+        case STATE_WAITING_FOR_LOAD_START:
+            NSLog(@"CDVWebViewDelegate: Unexpected didFinish while waiting for load start.");
+            break;
+
+        case STATE_WAITING_FOR_LOAD_FINISH:
+            if (_loadCount == 1) {
+                fireCallback = YES;
+                _state = STATE_IDLE;
             }
-            _loadCount = -1;
-        } else if (_loadCount > 1) {
             _loadCount -= 1;
-        }
-    } else {
-        [self pollForPageLoadStart:webView];
-        [self pollForPageLoadFinish:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_START:
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+            [self pollForPageLoadFinish:webView];
+            break;
+    }
+    VerboseLog(@"webView didFinishLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
+    if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
+        [_delegate webViewDidFinishLoad:webView];
     }
 }
 
 - (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
 {
-    if (_state == STATE_NORMAL) {
-        if (_loadCount == 1) {
-            if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
-                [_delegate webView:webView didFailLoadWithError:error];
+    VerboseLog(@"webView didFailLoad (before). state=%d loadCount=%d", _state, _loadCount);
+    BOOL fireCallback = NO;
+
+    switch (_state) {
+        case STATE_IDLE:
+            break;
+
+        case STATE_WAITING_FOR_LOAD_START:
+            _state = STATE_IDLE;
+            fireCallback = YES;
+            break;
+
+        case STATE_WAITING_FOR_LOAD_FINISH:
+            if (_loadCount == 1) {
+                _state = STATE_IDLE;
+                fireCallback = YES;
             }
             _loadCount = -1;
-        } else if (_loadCount > 1) {
-            _loadCount -= 1;
-        }
-    } else {
-        [self pollForPageLoadStart:webView];
-        [self pollForPageLoadFinish:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_START:
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+            [self pollForPageLoadFinish:webView];
+            break;
+    }
+    VerboseLog(@"webView didFailLoad (after). state=%d loadCount=%d, fireCallback=%d", _state, _loadCount, fireCallback);
+    if (fireCallback && [_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+        [_delegate webView:webView didFailLoadWithError:error];
     }
 }
 
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
index 3741e94..e339dd0 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
@@ -23,14 +23,11 @@
 
 @interface CDVWhitelist : NSObject
 
-@property (nonatomic, readonly, strong) NSArray* whitelist;
-@property (nonatomic, readonly, strong) NSArray* expandedWhitelist;
-@property (nonatomic, readonly, assign) BOOL allowAll;
 @property (nonatomic, copy) NSString* whitelistRejectionFormatString;
 
 - (id)initWithArray:(NSArray*)array;
-- (BOOL)URLIsAllowed:(NSURL*)url;
 - (BOOL)schemeIsAllowed:(NSString*)scheme;
+- (BOOL)URLIsAllowed:(NSURL*)url;
 - (NSString*)errorStringForURL:(NSURL*)url;
 
 @end
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
index 77e20ac..db7aa32 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
@@ -20,12 +20,12 @@
 #import "CDVWhitelist.h"
 
 NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
+NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme";
 
 @interface CDVWhitelist ()
 
 @property (nonatomic, readwrite, strong) NSArray* whitelist;
-@property (nonatomic, readwrite, strong) NSArray* expandedWhitelist;
-@property (nonatomic, readwrite, assign) BOOL allowAll;
+@property (nonatomic, readwrite, strong) NSDictionary* expandedWhitelists;
 
 - (void)processWhitelist;
 
@@ -33,15 +33,14 @@
 
 @implementation CDVWhitelist
 
-@synthesize whitelist, expandedWhitelist, allowAll, whitelistRejectionFormatString;
+@synthesize whitelist, expandedWhitelists, whitelistRejectionFormatString;
 
 - (id)initWithArray:(NSArray*)array
 {
     self = [super init];
     if (self) {
         self.whitelist = array;
-        self.expandedWhitelist = nil;
-        self.allowAll = NO;
+        self.expandedWhitelists = nil;
         self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;
         [self processWhitelist];
     }
@@ -94,6 +93,17 @@
     }
 }
 
+- (NSString*)extractSchemeFromUrlString:(NSString*)url
+{
+    NSURL* aUrl = [NSURL URLWithString:url];
+
+    if ((aUrl != nil) && ([aUrl scheme] != nil)) { // found scheme
+        return [aUrl scheme];
+    } else {
+        return kCDVDefaultSchemeName;
+    }
+}
+
 - (void)processWhitelist
 {
     if (self.whitelist == nil) {
@@ -101,70 +111,91 @@
         return;
     }
 
-    NSMutableArray* expanded = [NSMutableArray arrayWithCapacity:[self.whitelist count]];
-
-    // iterate through settings ExternalHosts, check for equality
-    NSEnumerator* enumerator = [self.whitelist objectEnumerator];
-    id externalHost = nil;
+    NSMutableDictionary* _expandedWhitelists = [@{kCDVDefaultSchemeName: [NSMutableArray array]} mutableCopy];
 
     // only allow known TLDs (since Aug 23rd 2011), and two character country codes
     // does not match internationalized domain names with non-ASCII characters
     NSString* tld_match = @"(aero|asia|arpa|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx|[a-z][a-z])";
 
-    while (externalHost = [enumerator nextObject]) {
-        NSString* regex = [self extractHostFromUrlString:externalHost];
-        BOOL is_ip = [self isIPv4Address:regex];
+    // iterate through settings ExternalHosts, check for equality
+    for (NSString* externalHost in self.whitelist) {
+        NSString* host = [self extractHostFromUrlString:externalHost];
+        NSString* scheme = [self extractSchemeFromUrlString:externalHost];
 
         // check for single wildcard '*', if found set allowAll to YES
-        if ([regex isEqualToString:@"*"]) {
-            self.allowAll = YES;
-            self.expandedWhitelist = [NSArray arrayWithObject:regex];
-            break;
+        if ([host isEqualToString:@"*"]) {
+            [_expandedWhitelists setObject:[NSArray arrayWithObject:host] forKey:scheme];
+            continue;
+        }
+
+        // if this is the first value for this scheme, create a new entry
+        if ([_expandedWhitelists objectForKey:scheme] == nil) {
+            [_expandedWhitelists setObject:[NSMutableArray array] forKey:scheme];
         }
 
         // starts with wildcard match - we make the first '.' optional (so '*.org.apache.cordova' will match 'org.apache.cordova')
         NSString* prefix = @"*.";
-        if ([regex hasPrefix:prefix]) {
+        if ([host hasPrefix:prefix]) {
             // replace the first two characters '*.' with our regex
-            regex = [regex stringByReplacingCharactersInRange:NSMakeRange(0, [prefix length]) withString:@"(\\s{0}|*.)"]; // the '*' and '.' will be substituted later
+            host = [host stringByReplacingCharactersInRange:NSMakeRange(0, [prefix length]) withString:@"(\\s{0}|*.)"]; // the '*' and '.' will be substituted later
         }
 
         // ends with wildcard match for TLD
-        if (!is_ip && [regex hasSuffix:@".*"]) {
+        if (![self isIPv4Address:host] && [host hasSuffix:@".*"]) {
             // replace * with tld_match
-            regex = [regex stringByReplacingCharactersInRange:NSMakeRange([regex length] - 1, 1) withString:tld_match];
+            host = [host stringByReplacingCharactersInRange:NSMakeRange([host length] - 1, 1) withString:tld_match];
         }
         // escape periods - since '.' means any character in regex
-        regex = [regex stringByReplacingOccurrencesOfString:@"." withString:@"\\."];
+        host = [host stringByReplacingOccurrencesOfString:@"." withString:@"\\."];
         // wildcard is match 1 or more characters (to make it simple, since we are not doing verification whether the hostname is valid)
-        regex = [regex stringByReplacingOccurrencesOfString:@"*" withString:@".*"];
+        host = [host stringByReplacingOccurrencesOfString:@"*" withString:@".*"];
 
-        [expanded addObject:regex];
+        [[_expandedWhitelists objectForKey:scheme] addObject:host];
     }
 
-    self.expandedWhitelist = expanded;
+    self.expandedWhitelists = _expandedWhitelists;
 }
 
 - (BOOL)schemeIsAllowed:(NSString*)scheme
 {
-    return [scheme isEqualToString:@"http"] ||
-           [scheme isEqualToString:@"https"] ||
-           [scheme isEqualToString:@"ftp"] ||
-           [scheme isEqualToString:@"ftps"];
+    if ([scheme isEqualToString:@"http"] ||
+        [scheme isEqualToString:@"https"] ||
+        [scheme isEqualToString:@"ftp"] ||
+        [scheme isEqualToString:@"ftps"]) {
+        return YES;
+    }
+
+    return (self.expandedWhitelists != nil) && ([self.expandedWhitelists objectForKey:scheme] != nil);
 }
 
 - (BOOL)URLIsAllowed:(NSURL*)url
 {
-    if (self.expandedWhitelist == nil) {
+    NSString* scheme = [url scheme];
+
+    // http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list
+    if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) {
+        NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", kCDVDefaultSchemeName, [url host]]];
+        // If it is allowed, we are done.  If not, continue to check for the actual scheme-specific list
+        if ([self URLIsAllowed:newUrl]) {
+            return YES;
+        }
+    }
+
+    // Check that the scheme is supported
+    if (![self schemeIsAllowed:scheme]) {
         return NO;
     }
 
-    if (self.allowAll) {
+    NSArray* expandedWhitelist = [self.expandedWhitelists objectForKey:scheme];
+
+    // Are we allowing everything for this scheme?
+    // TODO: consider just having a static sentinel value for the "allow all" list, so we can use object equality
+    if (([expandedWhitelist count] == 1) && [[expandedWhitelist objectAtIndex:0] isEqualToString:@"*"]) {
         return YES;
     }
 
     // iterate through settings ExternalHosts, check for equality
-    NSEnumerator* enumerator = [self.expandedWhitelist objectEnumerator];
+    NSEnumerator* enumerator = [expandedWhitelist objectEnumerator];
     id regex = nil;
     NSString* urlHost = [url host];
 
diff --git a/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
index e14f1f6..c637d60 100644
--- a/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
+++ b/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
@@ -47,6 +47,8 @@
 		68B7516E16FD18190076A8B4 /* CDVJpegHeaderWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B7516B16FD18190076A8B4 /* CDVJpegHeaderWriter.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		68B7516F16FD18190076A8B4 /* CDVJpegHeaderWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B7516C16FD18190076A8B4 /* CDVJpegHeaderWriter.m */; };
 		68B7517016FD19F80076A8B4 /* CDVExif.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B7516A16FD18190076A8B4 /* CDVExif.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		7E14B5A81705050A0032169E /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E14B5A61705050A0032169E /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		7E14B5A91705050A0032169E /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E14B5A71705050A0032169E /* CDVTimer.m */; };
 		8852C43A14B65FD800F0E735 /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 8852C43614B65FD800F0E735 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8852C43C14B65FD800F0E735 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8852C43714B65FD800F0E735 /* CDVViewController.m */; };
 		8887FD661090FBE7009987E8 /* CDVCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD261090FBE7009987E8 /* CDVCamera.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -55,8 +57,6 @@
 		8887FD691090FBE7009987E8 /* NSDictionary+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */; };
 		8887FD6A1090FBE7009987E8 /* CDVContacts.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD2A1090FBE7009987E8 /* CDVContacts.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8887FD6B1090FBE7009987E8 /* CDVContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD2B1090FBE7009987E8 /* CDVContacts.m */; };
-		8887FD6C1090FBE7009987E8 /* CDVDebugConsole.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD2C1090FBE7009987E8 /* CDVDebugConsole.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		8887FD6D1090FBE7009987E8 /* CDVDebugConsole.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD2D1090FBE7009987E8 /* CDVDebugConsole.m */; };
 		8887FD701090FBE7009987E8 /* CDVFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD301090FBE7009987E8 /* CDVFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8887FD711090FBE7009987E8 /* CDVFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD311090FBE7009987E8 /* CDVFile.m */; };
 		8887FD741090FBE7009987E8 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -147,6 +147,8 @@
 		68B7516A16FD18190076A8B4 /* CDVExif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVExif.h; path = Classes/CDVExif.h; sourceTree = "<group>"; };
 		68B7516B16FD18190076A8B4 /* CDVJpegHeaderWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVJpegHeaderWriter.h; path = Classes/CDVJpegHeaderWriter.h; sourceTree = "<group>"; };
 		68B7516C16FD18190076A8B4 /* CDVJpegHeaderWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVJpegHeaderWriter.m; path = Classes/CDVJpegHeaderWriter.m; sourceTree = "<group>"; };
+		7E14B5A61705050A0032169E /* CDVTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVTimer.h; path = Classes/CDVTimer.h; sourceTree = "<group>"; };
+		7E14B5A71705050A0032169E /* CDVTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVTimer.m; path = Classes/CDVTimer.m; sourceTree = "<group>"; };
 		8220B5C316D5427E00EC3921 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; };
 		8852C43614B65FD800F0E735 /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVViewController.h; path = Classes/CDVViewController.h; sourceTree = "<group>"; };
 		8852C43714B65FD800F0E735 /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVViewController.m; path = Classes/CDVViewController.m; sourceTree = "<group>"; };
@@ -156,8 +158,6 @@
 		8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Extensions.m"; path = "Classes/NSDictionary+Extensions.m"; sourceTree = "<group>"; };
 		8887FD2A1090FBE7009987E8 /* CDVContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVContacts.h; path = Classes/CDVContacts.h; sourceTree = "<group>"; };
 		8887FD2B1090FBE7009987E8 /* CDVContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVContacts.m; path = Classes/CDVContacts.m; sourceTree = "<group>"; };
-		8887FD2C1090FBE7009987E8 /* CDVDebugConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDebugConsole.h; path = Classes/CDVDebugConsole.h; sourceTree = "<group>"; };
-		8887FD2D1090FBE7009987E8 /* CDVDebugConsole.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVDebugConsole.m; path = Classes/CDVDebugConsole.m; sourceTree = "<group>"; };
 		8887FD301090FBE7009987E8 /* CDVFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVFile.h; path = Classes/CDVFile.h; sourceTree = "<group>"; };
 		8887FD311090FBE7009987E8 /* CDVFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVFile.m; path = Classes/CDVFile.m; sourceTree = "<group>"; };
 		8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVInvokedUrlCommand.h; path = Classes/CDVInvokedUrlCommand.h; sourceTree = "<group>"; };
@@ -307,8 +307,6 @@
 				1F3C04CD12BC247D004F9E10 /* CDVContact.m */,
 				8887FD2A1090FBE7009987E8 /* CDVContacts.h */,
 				8887FD2B1090FBE7009987E8 /* CDVContacts.m */,
-				8887FD2C1090FBE7009987E8 /* CDVDebugConsole.h */,
-				8887FD2D1090FBE7009987E8 /* CDVDebugConsole.m */,
 				EB80C2AA15DEA63D004D9E7B /* CDVEcho.h */,
 				EB80C2AB15DEA63D004D9E7B /* CDVEcho.m */,
 				8887FD301090FBE7009987E8 /* CDVFile.h */,
@@ -354,6 +352,8 @@
 				30E563CE13E217EC00C949AA /* NSMutableArray+QueueAdditions.m */,
 				8887FD501090FBE7009987E8 /* NSData+Base64.h */,
 				8887FD511090FBE7009987E8 /* NSData+Base64.m */,
+				7E14B5A61705050A0032169E /* CDVTimer.h */,
+				7E14B5A71705050A0032169E /* CDVTimer.m */,
 			);
 			name = Util;
 			sourceTree = "<group>";
@@ -380,7 +380,6 @@
 				8887FD661090FBE7009987E8 /* CDVCamera.h in Headers */,
 				8887FD681090FBE7009987E8 /* NSDictionary+Extensions.h in Headers */,
 				8887FD6A1090FBE7009987E8 /* CDVContacts.h in Headers */,
-				8887FD6C1090FBE7009987E8 /* CDVDebugConsole.h in Headers */,
 				8887FD701090FBE7009987E8 /* CDVFile.h in Headers */,
 				8887FD741090FBE7009987E8 /* CDVInvokedUrlCommand.h in Headers */,
 				8887FD851090FBE7009987E8 /* CDVLocation.h in Headers */,
@@ -420,6 +419,7 @@
 				30F3930B169F839700B22307 /* CDVJSON.h in Headers */,
 				EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */,
 				EB96673B16A8970A00D86CDF /* CDVUserAgentUtil.h in Headers */,
+				7E14B5A81705050A0032169E /* CDVTimer.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -480,7 +480,6 @@
 				8887FD671090FBE7009987E8 /* CDVCamera.m in Sources */,
 				8887FD691090FBE7009987E8 /* NSDictionary+Extensions.m in Sources */,
 				8887FD6B1090FBE7009987E8 /* CDVContacts.m in Sources */,
-				8887FD6D1090FBE7009987E8 /* CDVDebugConsole.m in Sources */,
 				8887FD711090FBE7009987E8 /* CDVFile.m in Sources */,
 				8887FD751090FBE7009987E8 /* CDVInvokedUrlCommand.m in Sources */,
 				8887FD861090FBE7009987E8 /* CDVLocation.m in Sources */,
@@ -516,6 +515,7 @@
 				EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */,
 				EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */,
 				68B7516F16FD18190076A8B4 /* CDVJpegHeaderWriter.m in Sources */,
+				7E14B5A91705050A0032169E /* CDVTimer.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/lib/cordova-ios/CordovaLib/VERSION b/lib/cordova-ios/CordovaLib/VERSION
index e70b452..59b7056 100644
--- a/lib/cordova-ios/CordovaLib/VERSION
+++ b/lib/cordova-ios/CordovaLib/VERSION
@@ -1 +1 @@
-2.6.0
+2.7.0rc1
diff --git a/lib/cordova-ios/CordovaLib/cordova.ios.js b/lib/cordova-ios/CordovaLib/cordova.ios.js
index 4df7891..844816f 100644
--- a/lib/cordova-ios/CordovaLib/cordova.ios.js
+++ b/lib/cordova-ios/CordovaLib/cordova.ios.js
@@ -1,8 +1,8 @@
 // Platform: ios
 
-// commit 125dca530923a44a8f44f68f5e1970cbdd4e7faf
+// commit 360bd3e65c33ce4f01e2efb82d641a565ef3c333
 
-// File generated at :: Thu Apr 04 2013 10:21:55 GMT-0700 (PDT)
+// File generated at :: Fri Apr 19 2013 18:36:09 GMT-0700 (PDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -219,6 +219,10 @@
             }
             else {
               setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
                   documentEventHandlers[type].fire(evt);
               }, 0);
             }
@@ -742,6 +746,7 @@
 // Channels that must fire before "deviceready" is fired.
 channel.waitForInitialization('onCordovaReady');
 channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
 
 module.exports = channel;
 
@@ -2372,7 +2377,7 @@
 
     if (typeof file == 'string') {
         // Deprecated in Cordova 2.4.
-        console.warning('Using a string argument with FileReader.readAs functions is deprecated.');
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
         reader._fileName = file;
     } else if (typeof file.fullPath == 'string') {
         reader._fileName = file.fullPath;
@@ -2730,7 +2735,7 @@
         var origin = protocol + url.host;
 
         // check whether there are the username:password credentials in the url
-        if (url.href.indexOf(origin) != 0) { // credentials found
+        if (url.href.indexOf(origin) !== 0) { // credentials found
             var atIndex = url.href.indexOf("@");
             credentials = url.href.substring(protocol.length, atIndex);
         }
@@ -2779,15 +2784,11 @@
     var params = null;
     var chunkedMode = true;
     var headers = null;
-
+    var httpMethod = null;
     var basicAuthHeader = getBasicAuthHeader(server);
     if (basicAuthHeader) {
-        if (!options) {
-            options = new FileUploadOptions();
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2796,6 +2797,12 @@
         fileName = options.fileName;
         mimeType = options.mimeType;
         headers = options.headers;
+        httpMethod = options.httpMethod || "POST";
+        if (httpMethod.toUpperCase() == "PUT"){
+            httpMethod = "PUT";
+        } else {
+            httpMethod = "POST";
+        }
         if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
             chunkedMode = options.chunkedMode;
         }
@@ -2822,7 +2829,7 @@
             successCallback && successCallback(result);
         }
     };
-    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
 };
 
 /**
@@ -2840,12 +2847,8 @@
 
     var basicAuthHeader = getBasicAuthHeader(source);
     if (basicAuthHeader) {
-        if (!options) {
-            options = {};
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2884,12 +2887,11 @@
 };
 
 /**
- * Aborts the ongoing file transfer on this object
- * @param successCallback {Function}  Callback to be invoked upon success
- * @param errorCallback {Function}    Callback to be invoked upon error
+ * Aborts the ongoing file transfer on this object. The original error
+ * callback for the file transfer will be called if necessary.
  */
-FileTransfer.prototype.abort = function(successCallback, errorCallback) {
-    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
+FileTransfer.prototype.abort = function() {
+    exec(null, null, 'FileTransfer', 'abort', [this._id]);
 };
 
 module.exports = FileTransfer;
@@ -2933,12 +2935,13 @@
  * @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, headers) {
+var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers, httpMethod) {
     this.fileKey = fileKey || null;
     this.fileName = fileName || null;
     this.mimeType = mimeType || null;
     this.params = params || null;
     this.headers = headers || null;
+    this.httpMethod = httpMethod || null;
 };
 
 module.exports = FileUploadOptions;
@@ -3273,6 +3276,7 @@
 
 var exec = require('cordova/exec');
 var channel = require('cordova/channel');
+var modulemapper = require('cordova/modulemapper');
 
 function InAppBrowser() {
    this.channels = {
@@ -3301,6 +3305,26 @@
         if (eventname in this.channels) {
             this.channels[eventname].unsubscribe(f);
         }
+    },
+
+    executeScript: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectScriptCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectScriptFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('executeScript requires exactly one of code or file to be specified');
+        }
+    },
+
+    insertCSS: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectStyleCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectStyleFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('insertCSS requires exactly one of code or file to be specified');
+        }
     }
 };
 
@@ -3309,6 +3333,13 @@
     var cb = function(eventname) {
        iab._eventHandler(eventname);
     };
+
+    // Don't catch calls that write to existing frames (e.g. named iframes).
+    if (window.frames && window.frames[strWindowName]) {
+        var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
+        return origOpenFunc.apply(window, arguments);
+    }
+
     exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
     return iab;
 };
@@ -4275,7 +4306,7 @@
 console.assert = function(expression) {
     if (expression) return;
 
-    var message = utils.vformat(arguments[1], [].slice.call(arguments, 2));
+    var message = logger.format.apply(logger.format, [].slice.call(arguments, 1));
     console.log("ASSERT: " + message);
 };
 
@@ -5257,93 +5288,6 @@
 
 });
 
-// file: lib/ios/plugin/ios/console.js
-define("cordova/plugin/ios/console", function(require, exports, module) {
-
-var exec = require('cordova/exec');
-
-/**
- * create a nice string for an object
- */
-function stringify(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 (typeof message === "undefined") ? "undefined" : message.toString();
-        }
-    } catch (e) {
-        return e.toString();
-    }
-}
-
-/**
- * Wrapper one of the console logging methods, so that
- * the Cordova logging native is called, then the original.
- */
-function wrappedMethod(console, method) {
-    var origMethod = console[method];
-
-    return function() {
-
-        var args = [].slice.call(arguments),
-            len = args.length,
-            i = 0,
-            res = [];
-
-        for ( ; i < len; i++) {
-            res.push(stringify(args[i]));
-        }
-
-        exec(null, null,
-            'Debug Console', 'log',
-            [ res.join(' '), { logLevel: method.toUpperCase() } ]
-        );
-
-        if (!origMethod) return;
-
-        origMethod.apply(console, arguments);
-    };
-}
-
-var console = window.console || {};
-
-// 2012-10-06 pmuellr - marking setLevel() method and logLevel property
-// on console as deprecated;
-// it didn't do anything useful, since the level constants weren't accessible
-// to anyone
-
-console.setLevel = function() {};
-console.logLevel = 0;
-
-// wrapper the logging messages
-
-var methods = ["log", "debug", "info", "warn", "error"];
-
-for (var i=0; i<methods.length; i++) {
-    var method = methods[i];
-
-    console[method] = wrappedMethod(console, method);
-}
-
-module.exports = console;
-
-});
-
-// file: lib/ios/plugin/ios/console/symbols.js
-define("cordova/plugin/ios/console/symbols", function(require, exports, module) {
-
-var modulemapper = require('cordova/modulemapper');
-
-modulemapper.clobbers('cordova/plugin/ios/console', 'console');
-
-});
-
 // file: lib/ios/plugin/ios/contacts.js
 define("cordova/plugin/ios/contacts", function(require, exports, module) {
 
@@ -5421,6 +5365,16 @@
 
 });
 
+// file: lib/ios/plugin/ios/logger/symbols.js
+define("cordova/plugin/ios/logger/symbols", function(require, exports, module) {
+
+
+var modulemapper = require('cordova/modulemapper');
+
+modulemapper.clobbers('cordova/plugin/logger', 'console');
+
+});
+
 // file: lib/ios/plugin/ios/notification.js
 define("cordova/plugin/ios/notification", function(require, exports, module) {
 
@@ -5605,10 +5559,10 @@
  * Parameters passed after message are used applied to
  * the message with utils.format()
  */
-logger.logLevel = function(level, message /* , ... */) {
+logger.logLevel = function(level /* , ... */) {
     // format the message with the parameters
-    var formatArgs = [].slice.call(arguments, 2);
-    message    = utils.vformat(message, formatArgs);
+    var formatArgs = [].slice.call(arguments, 1);
+    var message    = logger.format.apply(logger.format, formatArgs);
 
     if (LevelsMap[level] === null) {
         throw new Error("invalid logging level: " + level);
@@ -5643,6 +5597,92 @@
     }
 };
 
+
+/**
+ * Formats a string and arguments following it ala console.log()
+ *
+ * Any remaining arguments will be appended to the formatted string.
+ *
+ * for rationale, see FireBug's Console API:
+ *    http://getfirebug.com/wiki/index.php/Console_API
+ */
+logger.format = function(formatString, args) {
+    return __format(arguments[0], [].slice.call(arguments,1)).join(' ');
+};
+
+
+//------------------------------------------------------------------------------
+/**
+ * Formats a string and arguments following it ala vsprintf()
+ *
+ * format chars:
+ *   %j - format arg as JSON
+ *   %o - format arg as JSON
+ *   %c - format arg as ''
+ *   %% - replace with '%'
+ * any other char following % will format it's
+ * arg via toString().
+ *
+ * Returns an array containing the formatted string and any remaining
+ * arguments.
+ */
+function __format(formatString, args) {
+    if (formatString === null || formatString === undefined) return [""];
+    if (arguments.length == 1) return [formatString.toString()];
+
+    if (typeof formatString != "string")
+        formatString = formatString.toString();
+
+    var pattern = /(.*?)%(.)(.*)/;
+    var rest    = formatString;
+    var result  = [];
+
+    while (args.length) {
+        var match = pattern.exec(rest);
+        if (!match) break;
+
+        var arg   = args.shift();
+        rest = match[3];
+        result.push(match[1]);
+
+        if (match[2] == '%') {
+            result.push('%');
+            args.unshift(arg);
+            continue;
+        }
+
+        result.push(__formatted(arg, match[2]));
+    }
+
+    result.push(rest);
+
+    var remainingArgs = [].slice.call(args);
+    remainingArgs.unshift(result.join(''));
+    return remainingArgs;
+}
+
+function __formatted(object, formatChar) {
+
+    try {
+        switch(formatChar) {
+            case 'j':
+            case 'o': return JSON.stringify(object);
+            case 'c': return '';
+        }
+    }
+    catch (e) {
+        return "error JSON.stringify()ing argument: " + e;
+    }
+
+    if ((object === null) || (object === undefined)) {
+        return Object.prototype.toString.call(object);
+    }
+
+    return object.toString();
+}
+
+
+//------------------------------------------------------------------------------
 // when deviceready fires, log queued messages
 logger.__onDeviceReady = function() {
     if (DeviceReady) return;
@@ -5811,13 +5851,13 @@
             console.log("Notification.confirm(string, function, string, string) is deprecated.  Use Notification.confirm(string, function, string, array).");
         }
 
-        // Android and iOS take an array of button label names.
+        // Some platforms take an array of button label names.
         // Other platforms take a comma separated list.
         // For compatibility, we convert to the desired type based on the platform.
-        if (platform.id == "android" || platform.id == "ios") {
+        if (platform.id == "android" || platform.id == "ios" || platform.id == "windowsphone") {
             if (typeof _buttonLabels === 'string') {
                 var buttonLabelString = _buttonLabels;
-                _buttonLabels = buttonLabelString.split(",");
+                _buttonLabels = _buttonLabels.split(","); // not crazy about changing the var type here
             }
         } else {
             if (Array.isArray(_buttonLabels)) {
@@ -6168,62 +6208,6 @@
     }
 };
 
-/**
- * Formats a string and arguments following it ala sprintf()
- *
- * see utils.vformat() for more information
- */
-utils.format = function(formatString /* ,... */) {
-    var args = [].slice.call(arguments, 1);
-    return utils.vformat(formatString, args);
-};
-
-/**
- * Formats a string and arguments following it ala vsprintf()
- *
- * format chars:
- *   %j - format arg as JSON
- *   %o - format arg as JSON
- *   %c - format arg as ''
- *   %% - replace with '%'
- * any other char following % will format it's
- * arg via toString().
- *
- * for rationale, see FireBug's Console API:
- *    http://getfirebug.com/wiki/index.php/Console_API
- */
-utils.vformat = function(formatString, args) {
-    if (formatString === null || formatString === undefined) return "";
-    if (arguments.length == 1) return formatString.toString();
-    if (typeof formatString != "string") return formatString.toString();
-
-    var pattern = /(.*?)%(.)(.*)/;
-    var rest    = formatString;
-    var result  = [];
-
-    while (args.length) {
-        var arg   = args.shift();
-        var match = pattern.exec(rest);
-
-        if (!match) break;
-
-        rest = match[3];
-
-        result.push(match[1]);
-
-        if (match[2] == '%') {
-            result.push('%');
-            args.unshift(arg);
-            continue;
-        }
-
-        result.push(formatted(arg, match[2]));
-    }
-
-    result.push(rest);
-
-    return result.join('');
-};
 
 //------------------------------------------------------------------------------
 function UUIDcreatePart(length) {
@@ -6238,26 +6222,6 @@
     return uuidpart;
 }
 
-//------------------------------------------------------------------------------
-function formatted(object, formatChar) {
-
-    try {
-        switch(formatChar) {
-            case 'j':
-            case 'o': return JSON.stringify(object);
-            case 'c': return '';
-        }
-    }
-    catch (e) {
-        return "error JSON.stringify()ing argument: " + e;
-    }
-
-    if ((object === null) || (object === undefined)) {
-        return Object.prototype.toString.call(object);
-    }
-
-    return object.toString();
-}
 
 });
 
@@ -6267,6 +6231,25 @@
 // file: lib/scripts/bootstrap.js
 
 (function (context) {
+    var channel = require('cordova/channel');
+    var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
+
+    function logUnfiredChannels(arr) {
+        for (var i = 0; i < arr.length; ++i) {
+            if (arr[i].state != 2) {
+                console.log('Channel not fired: ' + arr[i].type);
+            }
+        }
+    }
+
+    window.setTimeout(function() {
+        if (channel.onDeviceReady.state != 2) {
+            console.log('deviceready has not fired after 5 seconds.');
+            logUnfiredChannels(platformInitChannelsArray);
+            logUnfiredChannels(channel.deviceReadyChannelsArray);
+        }
+    }, 5000);
+
     // Replace navigator before any modules are required(), to ensure it happens as soon as possible.
     // We replace it so that properties that can't be clobbered can instead be overridden.
     function replaceNavigator(origNavigator) {
@@ -6288,8 +6271,6 @@
         context.navigator = replaceNavigator(context.navigator);
     }
 
-    var channel = require("cordova/channel");
-
     // _nativeReady is global variable that the native side can set
     // to signify that the native code is ready. It is a global since
     // it may be called before any cordova JS is ready.
@@ -6298,32 +6279,33 @@
     }
 
     /**
-     * Create all cordova objects once page has fully loaded and native side is ready.
+     * Create all cordova objects once native side is ready.
      */
     channel.join(function() {
-        var builder = require('cordova/builder'),
-            platform = require('cordova/platform');
-
-        builder.buildIntoButDoNotClobber(platform.defaults, context);
-        builder.buildIntoAndClobber(platform.clobbers, context);
-        builder.buildIntoAndMerge(platform.merges, context);
-
         // Call the platform-specific initialization
-        platform.initialize();
+        require('cordova/platform').initialize();
 
         // Fire event to notify that all objects are created
         channel.onCordovaReady.fire();
 
-        // Fire onDeviceReady event once all constructors have run and
-        // cordova info has been received from native side.
+        // Fire onDeviceReady event once page has fully loaded, all
+        // constructors have run and cordova info has been received from native
+        // side.
+        // This join call is deliberately made after platform.initialize() in
+        // order that plugins may manipulate channel.deviceReadyChannelsArray
+        // if necessary.
         channel.join(function() {
             require('cordova').fireDocumentEvent('deviceready');
         }, channel.deviceReadyChannelsArray);
 
-    }, [ channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady ]);
+    }, platformInitChannelsArray);
 
 }(window));
 
+// file: lib/scripts/bootstrap-ios.js
+
+require('cordova/channel').onNativeReady.fire();
+
 // file: lib/scripts/plugin_loader.js
 
 // Tries to load all plugins' js-modules.
@@ -6399,31 +6381,27 @@
         }
     }
 
-    // Try to XHR the cordova_plugins.json file asynchronously.
-    try { // we commented we were going to try, so let us actually try and catch 
-        var xhr = new context.XMLHttpRequest();
-        xhr.onreadystatechange = function() {
-            if (this.readyState != 4) { // not DONE
-                return;
-            }
 
+    // Try to XHR the cordova_plugins.json file asynchronously.
+    try { // we commented we were going to try, so let us actually try and catch
+        var xhr = new context.XMLHttpRequest();
+        xhr.onload = function() {
             // If the response is a JSON string which composes an array, call handlePluginsObject.
             // If the request fails, or the response is not a JSON array, just call finishPluginLoading.
-            if (this.status == 200) {
-                var obj = JSON.parse(this.responseText);
-                if (obj && obj instanceof Array && obj.length > 0) {
-                    handlePluginsObject(obj);
-                } else {
-                    finishPluginLoading();
-                }
+            var obj = JSON.parse(this.responseText);
+            if (obj && obj instanceof Array && obj.length > 0) {
+                handlePluginsObject(obj);
             } else {
                 finishPluginLoading();
             }
         };
+        xhr.onerror = function() {
+            finishPluginLoading();
+        };
         xhr.open('GET', 'cordova_plugins.json', true); // Async
         xhr.send();
     }
-    catch(err) {
+    catch(err){
         finishPluginLoading();
     }
 }(window));
diff --git a/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m b/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
index 172b62e..21576a3 100644
--- a/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
+++ b/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
@@ -149,9 +149,10 @@
     CDVWhitelist* whitelist = [[CDVWhitelist alloc] initWithArray:allowedHosts];
 
     STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://apache.org"]], nil);
-    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://build.apache.prg"]], nil);
-    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://MyDangerousSite.org"]], nil);
-    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://apache.org.SuspiciousSite.com"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"https://build.apache.prg"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"ftp://MyDangerousSite.org"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"ftps://apache.org.SuspiciousSite.com"]], nil);
+    STAssertFalse([whitelist URLIsAllowed:[NSURL URLWithString:@"gopher://apache.org"]], nil);
 }
 
 - (void)testWildcardInHostname
@@ -274,4 +275,19 @@
     STAssertTrue([expectedErrorString isEqualToString:errorString], @"Customized whitelist rejection string has unexpected value.");
 }
 
+- (void)testSpecificProtocol
+{
+    NSArray* allowedHosts = [NSArray arrayWithObjects:
+        @"http://www.apache.org",
+        @"cordova://www.google.com",
+        nil];
+
+    CDVWhitelist* whitelist = [[CDVWhitelist alloc] initWithArray:allowedHosts];
+
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://www.apache.org"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"cordova://www.google.com"]], nil);
+    STAssertFalse([whitelist URLIsAllowed:[NSURL URLWithString:@"cordova://www.apache.org"]], nil);
+    STAssertFalse([whitelist URLIsAllowed:[NSURL URLWithString:@"http://www.google.com"]], nil);
+}
+
 @end
diff --git a/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml b/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
index 5610e5e..f8a2fad 100644
--- a/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
+++ b/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
@@ -25,7 +25,6 @@
         <plugin name="Camera" value="CDVCamera" />
         <plugin name="NetworkStatus" value="CDVConnection" />
         <plugin name="Contacts" value="CDVContacts" />
-        <plugin name="Debug Console" value="CDVDebugConsole" />
         <plugin name="File" value="CDVFile" />
         <plugin name="FileTransfer" value="CDVFileTransfer" />
         <plugin name="Geolocation" value="CDVLocation" />
diff --git a/lib/cordova-ios/RELEASENOTES.md b/lib/cordova-ios/RELEASENOTES.md
index aa501b0..ffe0c0b 100644
--- a/lib/cordova-ios/RELEASENOTES.md
+++ b/lib/cordova-ios/RELEASENOTES.md
@@ -22,6 +22,45 @@
  
  Cordova is a static library that enables developers to include the Cordova API in their iOS application projects easily, and also create new Cordova-based iOS application projects through the command-line.
 
+### 2.7.0 (201304XX) ###
+
+* Fix NPE in InAppBrowser's error callback.
+* [CB-2849] Fix bin/create when CordovaLib parent dir has a space
+* [CB-3069] Fix InAppBrowser load events (for non-redirecting pages)
+* InAppBrowser: Don't inject iframe bridge until necessary.
+* Fix FileTransfer unit test. HTTP Method was being set to null.
+* [CB-2305] Add InAppBrowser injectSriptCode command to support InAppBrowser.executeScript and InAppBrowser.insertCSS APIs
+* [CB-2653] Simplify InAppBrowser.injectScriptCode.
+* [CB-2537] Implement streaming downloads for FileTransfer
+* [CB-2190] Allow FileTransfer uploads to continue in background
+* [CB-1518] Request content length in parallel with download for gzipped content
+* [CB-2653] Delay executeScript/insertCSS callback until resources have loaded; pass JS results to callback
+* [CB-2824] Remove DebugConsole plugin
+* [CB-3066] Fire onNativeReady from JS, as bridge is available immediately
+* [CB-2725] Fix www deploy issues with symlinks
+* [CB-2725] follow links in www copy script
+* [CB-3039] iOS Exif date length mismtach
+* [CB-3052] iOS Exif SubIFD offsets incorrect
+* [CB-51] Added httpMethod for file transfer options (defaults to POST)
+* [CB-2732] Only set camera device when allowed.
+* [CB-2911] Updated resolveLocalFileSystemURI.
+* [CB-3032] Add whitelist support for custom schemes.
+* [CB-3048] Add --arc flag to create script, support arc in template.
+* [CB-3067]: fixing ios5 whitelist of file url
+* [CB-3067] Revert CDVURLProtocol to not whitelist file urls
+* [CB-2788] add ./bin/check_reqs script to iOS
+* [CB-2587] Added plugin timing for plugins that are loaded on startup (plugin 'onload' attribute)
+* [CB-2848] ShowSplashScreenSpinner not used
+* [CB-2960] Changing the volume of a sound already playing
+* [CB-3021] Can no longer import CDVPlugin.h from plugin Objective-C++ code
+* [CB-2790] added splice function to header writer: accepts jpeg as NSData, and splices in exif data specified by a string
+* [CB-2790] removed old splice code, replaced with JpegHeaderWriter api calls
+* [CB-2896] split writing of working tags off here, multipart tags not supported
+* [CB-2896] fixed error in exif subifd offset calculation for tag 8769
+* [CB-2902] re-added long/short tags to template dict, fixed subExifIFD offset
+
+<br />
+
 ### 2.6.0 (20130401) ###
 
 * [CB-2732] Only set camera device when allowed.
diff --git a/lib/cordova-ios/bin/check_reqs b/lib/cordova-ios/bin/check_reqs
new file mode 100755
index 0000000..27d00c3
--- /dev/null
+++ b/lib/cordova-ios/bin/check_reqs
@@ -0,0 +1,34 @@
+#! /bin/sh
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+XCODEBUILD_LOCATION=$(which xcodebuild)
+if [ $? != 0 ]; then
+	echo "Xcode is (probably) not installed, specifically the command 'xcodebuild' is unavailable."
+	exit 1
+fi
+
+XCODEBUILD_MIN_VERSION="4.5"
+XCODEBUILD_VERSION=$(xcodebuild -version | head -n 1 | sed -e 's/Xcode //')
+
+if [[ "$XCODEBUILD_VERSION" < "$XCODEBUILD_MIN_VERSION" ]]; then
+	echo "Cordova can only run in Xcode version $XCODEBUILD_MIN_VERSION or greater."
+	exit 1
+fi
diff --git a/lib/cordova-ios/bin/create b/lib/cordova-ios/bin/create
index bef0f57..d617f09 100755
--- a/lib/cordova-ios/bin/create
+++ b/lib/cordova-ios/bin/create
@@ -31,8 +31,9 @@
 set -e
 
 function usage() {
-  echo "Usage: $0 [--shared] <path_to_new_project> <package_name> <project_name>"
+  echo "Usage: $0 [--shared] [--arc] <path_to_new_project> <package_name> <project_name>"
   echo "	--shared (optional): Link directly against the shared copy of the CordovaLib instead of a copy of it."
+  echo "	--arc (optional): Enable ARC."
   echo "	<path_to_new_project>: Path to your new Cordova iOS project"
   echo "	<package_name>: Package name, following reverse-domain style convention"
   echo "	<project_name>: Project name"
@@ -40,10 +41,15 @@
 }
 
 USE_SHARED=0
+USE_ARC=0
 if [[ $1 == "--shared" ]]; then
     USE_SHARED=1
     shift;
 fi
+if [[ $1 == "--arc" ]]; then
+    USE_ARC=1
+    shift;
+fi
 
 # check whether it is a proper create command (at least 3 arguments)
 if [ $# -lt 3 ]; then
@@ -59,7 +65,7 @@
 
 BINDIR=$( cd "$( dirname "$SCRIPT" )" && pwd )
 CORDOVALIB_DIR="$BINDIR/../CordovaLib"
-CDV_VER=$(cat $CORDOVALIB_DIR/VERSION)
+CDV_VER=$(cat "$CORDOVALIB_DIR/VERSION")
 
 PROJECT_PATH=$1
 PACKAGE=$2
@@ -134,3 +140,6 @@
     "$BINDIR/update_cordova_subproject" "$R.xcodeproj/project.pbxproj" "$PROJECT_PATH/CordovaLib/CordovaLib.xcodeproj/project.pbxproj" > /dev/null
 fi
 
+if [[ $USE_ARC = 1 ]]; then
+    /usr/bin/sed -i '' 's/CLANG_ENABLE_OBJC_ARC = NO/CLANG_ENABLE_OBJC_ARC = YES/' "$R.xcodeproj/project.pbxproj" > /dev/null
+fi
diff --git a/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj b/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
index f5ed342..0af952e 100755
--- a/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
+++ b/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
@@ -404,7 +404,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "SRC_DIR=\"www\"\nDST_DIR=\"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/www\"\nCOPY_HIDDEN=\nORIG_IFS=$IFS\nIFS=$(echo -en \"\\n\\b\")\n\nif [[ ! -e \"$SRC_DIR\" ]]; then\n  echo \"Path does not exist: $SRC_DIR\"\n  exit 1\nfi\n\nif [[ -n $COPY_HIDDEN ]]; then\n  alias do_find='find \"$SRC_DIR\"'\nelse\n  alias do_find='find \"$SRC_DIR\" -name \".*\" -prune -o'\nfi\n\ntime (\n# Code signing files must be removed or else there are\n# resource signing errors.\nrm -rf \"$DST_DIR\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/_CodeSignature\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/PkgInfo\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/embedded.mobileprovision\"\n\n# Directories\nfor p in $(do_find -type d -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  mkdir \"$DST_DIR$subpath\" || exit 1\ndone\n\n# Symlinks\nfor p in $(do_find -type l -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  rsync -a \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" || exit 2\ndone\n\n# Files\nfor p in $(do_find -type f -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  if ! ln \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" 2>/dev/null; then\n    rsync -a \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" || exit 3\n  fi\ndone\n\n)\nIFS=$ORIG_IFS";
+			shellScript = "SRC_DIR=\"www/\"\nDST_DIR=\"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/www\"\nCOPY_HIDDEN=\nORIG_IFS=$IFS\nIFS=$(echo -en \"\\n\\b\")\n\nif [[ ! -e \"$SRC_DIR\" ]]; then\n  echo \"Path does not exist: $SRC_DIR\"\n  exit 1\nfi\n\nif [[ -n $COPY_HIDDEN ]]; then\n  alias do_find='find \"$SRC_DIR\"'\nelse\n  alias do_find='find -L \"$SRC_DIR\" -name \".*\" -prune -o'\nfi\n\ntime (\n# Code signing files must be removed or else there are\n# resource signing errors.\nrm -rf \"$DST_DIR\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/_CodeSignature\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/PkgInfo\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/embedded.mobileprovision\"\n\n# Directories\nfor p in $(do_find -type d -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  mkdir \"$DST_DIR$subpath\" || exit 1\ndone\n\n# Symlinks\nfor p in $(do_find -type l -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  source=$(readlink $SRC_DIR$subpath)\n  sourcetype=$(stat -f \"%HT%SY\" $source)\n  if [ \"$sourcetype\" = \"Directory\" ]; then\n    mkdir \"$DST_DIR$subpath\" || exit 2\n  else\n    rsync -a \"$source\" \"$DST_DIR$subpath\" || exit 3\n  fi\ndone\n\n# Files\nfor p in $(do_find -type f -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  if ! ln \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" 2>/dev/null; then\n    rsync -a \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" || exit 4\n  fi\ndone\n\n)\nIFS=$ORIG_IFS";
 		};
 /* End PBXShellScriptBuildPhase section */
 
diff --git a/lib/cordova-ios/bin/templates/project/__TESTING__/Classes/AppDelegate.m b/lib/cordova-ios/bin/templates/project/__TESTING__/Classes/AppDelegate.m
index 623ad8e..5c05ac8 100644
--- a/lib/cordova-ios/bin/templates/project/__TESTING__/Classes/AppDelegate.m
+++ b/lib/cordova-ios/bin/templates/project/__TESTING__/Classes/AppDelegate.m
@@ -45,7 +45,11 @@
 
     int cacheSizeMemory = 8 * 1024 * 1024; // 8MB
     int cacheSizeDisk = 32 * 1024 * 1024; // 32MB
-    NSURLCache* sharedCache = [[[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"] autorelease];
+#if __has_feature(objc_arc)
+        NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"];
+#else
+        NSURLCache* sharedCache = [[[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"] autorelease];
+#endif
     [NSURLCache setSharedURLCache:sharedCache];
 
     self = [super init];
@@ -61,10 +65,18 @@
 {
     CGRect screenBounds = [[UIScreen mainScreen] bounds];
 
-    self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease];
+#if __has_feature(objc_arc)
+        self.window = [[UIWindow alloc] initWithFrame:screenBounds];
+#else
+        self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease];
+#endif
     self.window.autoresizesSubviews = YES;
 
-    self.viewController = [[[MainViewController alloc] init] autorelease];
+#if __has_feature(objc_arc)
+        self.viewController = [[MainViewController alloc] init];
+#else
+        self.viewController = [[[MainViewController alloc] init] autorelease];
+#endif
     self.viewController.useSplashScreen = YES;
 
     // Set your app's start page by setting the <content src='foo.html' /> tag in config.xml.
diff --git a/lib/cordova-ios/bin/templates/project/__TESTING__/config.xml b/lib/cordova-ios/bin/templates/project/__TESTING__/config.xml
index 8889726..537705d 100644
--- a/lib/cordova-ios/bin/templates/project/__TESTING__/config.xml
+++ b/lib/cordova-ios/bin/templates/project/__TESTING__/config.xml
@@ -46,7 +46,6 @@
         <plugin name="Camera" value="CDVCamera" />
         <plugin name="NetworkStatus" value="CDVConnection" />
         <plugin name="Contacts" value="CDVContacts" />
-        <plugin name="Debug Console" value="CDVDebugConsole" />
         <plugin name="Echo" value="CDVEcho" />
         <plugin name="File" value="CDVFile" />
         <plugin name="FileTransfer" value="CDVFileTransfer" />
diff --git a/package.json b/package.json
index 89c0df8..b86ae89 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cordova",
-  "version": "2.6.2",
+  "version": "2.7.0-rc.1",
   "preferGlobal": "true",
   "description": "Cordova command line interface tool",
   "main": "cordova",