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:¤tDataOffset];
//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:¤tDataOffset];
/*
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",