Compare commits

...

277 Commits

Author SHA1 Message Date
Marcel Kinard
fb13ab6de2 update RELEASENOTES with latest commit 2014-09-30 20:59:47 -04:00
Joe Bowser
a272fd1531 CB-7674: Added sleep to avoid null error after most recent change to not break API 2014-09-30 17:58:13 -07:00
Marcel Kinard
90766ae0fa CB-7674 move preference activation back into onCreate()
The preference creation actually needs to be before
super.onCreate(savedInstance) in order to avoid the exception
"requestFeature() must be called before adding content". Also ran into an
issue in the native tests "Whitelist" and "User WebView/Client/Chrome" where
it would throw an exception that the CordovaWebView appView already had
a parent and needed to be removed from that parent before the invocation
to root.addView(appView). So I conditionally remove the wrong parent.
Also made a change to the native tests so the menus test would work.
I also put super.init() back into the template, though invoking it is optional
as loadUrl will call it automatically if needed.
2014-09-30 17:51:21 -07:00
Steven Gill
fcb6cc44f1 updated release notes 2014-09-30 13:10:40 -07:00
Steven Gill
ef4383561e Set VERSION to 3.6.4 (via coho) 2014-09-29 14:59:04 -07:00
Steven Gill
025ca36d3a Update JS snapshot to version 3.6.4 (via coho) 2014-09-29 14:59:04 -07:00
Andrew Grieve
c2bba3f5e3 CB-7634 Detect JAVA_HOME properly on Ubuntu 2014-09-29 10:16:52 -04:00
Andrew Grieve
1c3f7e6f93 CB-7579 Fix run script's ability to use non-arch-specific APKs 2014-09-26 15:04:21 +04:00
Vladimir Kotikov
e65bfa41a5 CB-6511 Fixes build for android when app name contains unicode characters. 2014-09-26 11:59:05 +04:00
Marcel Kinard
e2e38ad2b4 Set VERSION to 3.6.3 (via coho) 2014-09-12 15:50:25 -04:00
Marcel Kinard
caa82b2f26 CB-7383 Bump version to 3.6.1 2014-09-05 14:08:46 -04:00
Steven Gill
5652fdc030 updated releasenotes
(cherry picked from commit 5a82dd5110)
2014-09-05 11:05:33 -04:00
Joe Bowser
7dbc5b0965 CB-7463: Adding licences. I don't know what the gradle syntax is for comments, that still needs to be done. 2014-09-04 10:53:41 -07:00
Joe Bowser
60578f3f0d CB-7463: Looked at the Apache BigTop git, gradle uses C-style comments 2014-09-04 10:53:29 -07:00
Joe Bowser
afdd16a214 CB-7460: Fixing bug with KitKat where the background colour would override the CSS colours on the application 2014-09-03 15:26:16 -07:00
Steven Gill
014327c59a Set VERSION to 3.6.0 (via coho) 2014-08-29 16:34:11 -07:00
Marcel Kinard
0cde8819cf CB-7410 fix the menu test
Need to show the title in order for the options menu button to be visible.
2014-08-29 18:07:29 -04:00
Marcel Kinard
07632b0eeb CB-7410 Fix the errorUrl test
Make the error.html page a well-formed html document, otherwise it
won't display.
2014-08-29 17:38:03 -04:00
Marcel Kinard
4a7f825cfe CB-7410 Fix Basic Authentication test
Looks like the Chromium webview does not include the port number on the
hostname during the callback challenge, but the classic webview does
include the port number. Handle both cases here.
2014-08-29 16:41:51 -04:00
Ian Clelland
4bc2051f44 CB-3445: Allow build and run scripts to select APK by architecture 2014-08-29 16:00:13 -04:00
Ian Clelland
34dde53506 CB-3445: Add environment variable 'BUILD_MULTIPLE_APKS' for splitting APKs based on architecture 2014-08-28 16:18:02 -04:00
Ian Clelland
7a09182446 CB-3445: Ensure that JAR files in libs directory are included 2014-08-28 13:17:26 -04:00
Marcel Kinard
eb8cf56e8e CB-7267 update RELEASENOTES for 3.5.1 2014-08-28 09:42:53 -04:00
Marcel Kinard
12a27643db CB-7410 clarify the title 2014-08-27 13:56:31 -04:00
Marcel Kinard
c6ccde0558 CB-7385 update cordova.js for testing prior to branch/tag 2014-08-27 09:04:32 -04:00
Marcel Kinard
16e3ebd87b CB-7410 add whitelist entries to get iframe/GoogleMaps working 2014-08-26 17:20:58 -04:00
Marcel Kinard
94c096dd5b CB-7291 propogate change in method signature to the native tests 2014-08-26 16:38:04 -04:00
Ian Clelland
2e3e4ec3b2 Merge branch 'CB-7291' 2014-08-26 15:25:41 -04:00
Ian Clelland
6e222c3938 CB-7291: Restrict meaning of "*" in internal whitelist to just http and https 2014-08-26 15:23:24 -04:00
Ian Clelland
3b3bd9b6c9 CB-7291: Only add file, content and data URLs to internal whitelist 2014-08-21 16:27:48 -04:00
Ian Clelland
4e3331ba66 CB-7291: Add defaults to external whitelist 2014-08-21 16:27:48 -04:00
Ian Clelland
b6c5a5fc9a CB-3445: Read project.properties to configure gradle libraries 2014-08-20 11:42:04 -04:00
Refael Ackermann
94943a9a84 CB-7325 Fix error message in android_sdk_version.js when missing SDK on windows
github: close #113
2014-08-20 11:20:02 -04:00
Andrew Grieve
71e72f215d CB-7335 Add a .gitignore to android project template 2014-08-19 11:59:18 -04:00
Andrew Grieve
58cdfd86d0 CB-7330 Fix dangling function call in last commit (broke gradle builds) 2014-08-19 11:53:53 -04:00
Andrew Grieve
dfa66b9dd4 CB-7330 Don't run "android update" during creation
Instead, have the build script copy do the equivalent logic on each
build.

Advantages:
- Scripts run much faster
- No more duplicate CordovaLib entries in project.properties
- Building is more independent from create/update script (more robust)
2014-08-18 23:24:29 -04:00
Andrew Grieve
d56ea25816 CB-3445 Add gradle support clean command (plus some code cleanup)
* Don't run ant clean for gradle ever
* Don't set sdk.dir since ANDROID_HOME is not always set
* Don't export builders
2014-08-18 16:19:40 -04:00
Andrew Grieve
c91b272648 CB-7044 Fix typo in prev commit causing check_reqs to always fail. 2014-08-18 15:26:52 -04:00
Andrew Grieve
ca8bb75b40 CB-3445 Copy gradle wrapper in build instead of create
This should play nicer with updates to the android SDK.
2014-08-18 14:51:40 -04:00
Andrew Grieve
36eab713a1 CB-3445 Add .gradle template files for "update" as well as "create" 2014-08-18 14:50:27 -04:00
Andrew Grieve
7133576fe9 CB-7044 Add JAVA_HOME when not set. Be stricter about ANDROID_HOME
Also switches to using the which module over shelljs.which (better
support for .bat files)
2014-08-18 14:45:23 -04:00
Andrew Grieve
effffcba1d CB-3445 Speed up gradle building (incremental builds go from 10s -> 1.5s for me)
Biggest win is disabling the linter.
2014-08-18 14:17:34 -04:00
Ian Clelland
404ce8bc3e Merge branch 'gradle-build-3.x'
This introduces the gradle build system to Cordova-android, behind a flag currently
2014-08-18 09:58:01 -04:00
Ian Clelland
a91bd095b0 CB-3445: android: Copy Gradle wrapper from Android SDK rather than bundling a JAR 2014-08-18 09:48:31 -04:00
Ian Clelland
fd6a1e5ed0 CB-3445: Add which to checked-in node_modules 2014-08-18 09:48:31 -04:00
Ian Clelland
7d6ac87033 CB-3445: Add option to build and install with gradle
This gives build/cordova two new command-line arguments: --ant and
--gradle, and will select the build type from those. As a fallback
for the Cordova CLI, the environment variable ANDROID_BUILD can also be
used, set to either "ant" or "gradle".

The default is currently "ant", but it is intended for this to change in
the future.
2014-08-18 09:48:31 -04:00
Ian Clelland
8aa813b862 CB-3445: Add an initial set of Gradle build scripts
These scripts will build an android project, in debug and release mode.
They also support additional library projects, such as Crosswalk, being
added to libraries.gradle (and settings.gradle). A flag can be set in
libraries.gradle to enable multi-architecture builds.
2014-08-18 09:48:31 -04:00
Andrew Grieve
95aa5c9f1c CB-7321 Don't require ant for create script 2014-08-15 13:58:53 -04:00
Andrew Grieve
4319447cb5 CB-7044, CB-7299 Fix up PATH problems when possible.
Uses heuristics:
- Adds javac to PATH based on default install paths on Windows
- Adds javac to PATH based on JAVA_HOME
- Adds android and adb to PATH based on ANDROID_HOME
- Sets ANDROID_HOME based on location of "android"
2014-08-15 13:46:20 -04:00
Joe Bowser
50ea162251 Change in test's AndroidManifest.xml needed for the test to run properly. Forgot the manifest. 2014-08-14 14:30:31 -07:00
Joe Bowser
9c239804d3 Change in test's AndroidManifest.xml needed for the test to run properly 2014-08-14 14:29:54 -07:00
Joe Bowser
320e31bb10 Adding tests related to 3.5.1 2014-08-12 11:09:53 -07:00
Ian Clelland
8b55a16986 CB-7291: Add external-launch-whitelist and use it for filtering intent launches 2014-08-12 11:26:47 -04:00
Andrew Grieve
41125ea1e2 CB-7261 Fix setNativeToJsBridgeMode sometimes crashing when switching to ONLINE_EVENT 2014-08-07 16:18:56 -04:00
Martin Bektchiev
73219bf2d2 CB-7265 Fix crash when navigating to custom protocol (introduced in 3.5.1)
Conflicts:
	framework/src/org/apache/cordova/CordovaUriHelper.java

Github: close #111
2014-08-07 09:58:40 -04:00
Ian Clelland
d6eb723b7f Filter out non-launchable intents 2014-08-06 09:55:03 -04:00
Ian Clelland
993d73762c Handle unsupported protocol errors in webview better 2014-08-06 09:55:03 -04:00
Joe Bowser
48b51c451a CB-7238: I should have collapsed this, but Config.init() must go before the creation of CordovaWebView 2014-07-31 09:23:14 -07:00
Joe Bowser
3d191d5884 CB-7238: Minor band-aid to get tests running again, this has to go away before 3.6.0 is released, since this is an API change. 2014-07-31 08:17:31 -07:00
Ian Clelland
955133f173 Extend whitelist to handle URLs without // chars 2014-07-31 08:11:40 -07:00
Andrey Kurdyumov
c2cafb4b45 CB-7172 Force window to have focus after resume
Workaround for some devices (Samsung Galaxy Note 3 at least)

github: close #108
2014-07-22 22:33:53 -04:00
Jan Velecký
67f474ef42 CB-7159 Set background color of webView as well as its parent
github: close #109, close #110
2014-07-21 15:46:23 -04:00
Andrew Grieve
cd6c0e1de9 CB-7018 Fix setButtonPlumbedToJs never un-listening 2014-07-18 13:46:29 -04:00
Andrew Grieve
92be0033a8 Undeprecate some just-deprecated symbols in PluginManager.
Forgot about custom engines again :(
2014-07-14 14:28:27 -04:00
Andrew Grieve
b934c1be6a @Deprecate methods of PluginManager that were never meant to be public 2014-07-14 14:10:19 -04:00
Andrew Grieve
145b50a320 Move plugin instantiation and instance storing logic PluginEntry->PluginManager
Instantiation and storing of the instance should be owned privately by
PluginManager, not exposed via an unprotected public API. That said,
this refactoring does not make any breaking changes to the public API,
except for removing the createPlugin call in PluginEntry, which should
not be called by anyone other than PluginManager anyway.
2014-07-14 14:08:27 -04:00
Andrew Grieve
a33cdc9c7b Fix broken unit test due to missing Config.init() call 2014-07-14 14:06:47 -04:00
Matt Ray
62101e85ff Update to check for Google Glass APIs
This prevents the 'cordova build android' process from blowing up on this step if you assign the GDK as the target and want to rebuild via the CLI.

close #100
2014-07-14 10:30:23 -04:00
Eion Robb
0a3714e5e0 Fix for android not being in PATH check on Windows
close #103
2014-07-14 10:28:27 -04:00
Ankit Jain
86a2830d75 Displaying error when regex does not match.
On my ubuntu when `android` is not found typical output is:

```
   /bin/sh: 1: android: not found
```
close #104
2014-07-14 10:28:13 -04:00
Andrew Grieve
9300e97d2b Fix broken compile due to previous commit :( 2014-07-10 15:14:39 -04:00
Andrew Grieve
3792f75281 Tweak CordovaPlugin.initialize method to be less deprecated.
Thinking here is that we need a while for both initialize and
pluginInitialize to exist before plugin authors would bother not using
the deprecated one anyways. Really, no harm in keeping both for some
time.
2014-07-10 15:03:53 -04:00
Andrew Grieve
a14c794255 Un-deprecate CordovaActivity.init() - it's needed to tweak prefs in onCreate 2014-07-10 11:36:20 -04:00
Andrew Grieve
aef96e95e8 Tweak log messages in CordovaBridge with bridgeSecret is wrong 2014-07-10 11:36:20 -04:00
Andrew Grieve
cc860804f6 Backport CordovaBridge from 4.0.x -> master 2014-07-10 10:43:37 -04:00
Andrew Grieve
d8a19b5565 Update unit tests to not use most deprecated things (e.g. DroidGap) 2014-07-10 10:23:44 -04:00
Andrew Grieve
1c5b5e2ce6 Add non-String overloades for CordovaPreferences.set() 2014-07-10 10:23:26 -04:00
Andrew Grieve
2f24e42dc1 Make CordovaWebview resilient to init() not being called (for backwards-compatibility)
This can happen when apps are not utilizing CordovaActivity and instead
creating their own CordovaWebView.
2014-07-09 21:08:29 -04:00
Andrew Grieve
0c12aa163e Add node_module licenses to LICENSE 2014-07-09 16:08:18 -04:00
Andrew Grieve
ec47274fbd Update cordova.js snapshot to work with bridge changes 2014-07-09 13:31:44 -04:00
Andrew Grieve
04ccb06e3f Provide CordovaPlugin with CordovaPreferences. Add new Plugin.initialize()
This adds CordovaPlugin.initialize() (no args) and deprecates
CordovaPlugin.initialize(app, webView). This will allow us to refactor
more easily by using the package-private privateInitialize() to set
fields.
2014-07-08 14:26:21 -04:00
Andrew Grieve
d31ee20ba5 Convert usages of Config.* to use the non-static versions 2014-07-08 14:11:14 -04:00
Andrew Grieve
9b25d45b93 Change getProperty -> prefs.get* within CordovaActivity 2014-07-08 14:08:15 -04:00
Andrew Grieve
d51abdd73e Make CordovaUriHelper class package-private 2014-07-08 12:05:41 -04:00
Andrew Grieve
9ea8b2237a Fix PluginManager.setPluginEntries not removing old entries 2014-07-08 11:54:38 -04:00
Andrew Grieve
e86c2e5970 Move registration of App plugin from config.xml -> code
Less fragile this way.
2014-07-08 11:47:26 -04:00
Andrew Grieve
caeb86843d Make setWebViewClient an override instead of an overload. Delete Location-change JS->Native bridge mode (missed some of it). 2014-07-07 16:31:29 -04:00
Andrew Grieve
0f15608175 CB-4404 Revert setting android:windowSoftInputMode to "adjustPan"
"adjustResize" is what the value has been set to for the longest time (due to it
being in the wrong place in the manifest). "adjustResize" is a better default value anyways.
2014-07-07 13:07:51 -04:00
Andrew Grieve
705991e5b0 Refactor: Use ConfigXmlParser in activity. Adds CordovaWebView.init()
This does subtly change the API surface due to CordovaWebView.init(),
but only minimally, and is backwards compatibly with the default
generated projects from prior versions.
2014-07-04 16:32:09 -04:00
Andrew Grieve
b636874bd9 Deprecate some convenience methods on CordovaActivity
They don't add much convenience and the file is too big already.
2014-07-04 16:31:19 -04:00
Andrew Grieve
965e4e9b19 Fix CordovaPreferences not correctly parsing hex values (valueOf->decode) 2014-07-04 16:27:16 -04:00
Andrew Grieve
af77977fda Refactor: Move url-filter information into PluginEntry. 2014-07-04 14:53:00 -04:00
Andrew Grieve
e74baf188f Don't re-parse config.xml in onResume.
There shouldn't be any need to.
2014-07-04 12:19:28 -04:00
Andrew Grieve
663a71255f Move handling of Fullscreen preference to CordovaActivity
Makes more sense here since that's where the other FullScreen related
changes are.
2014-07-04 12:11:19 -04:00
Andrew Grieve
79aa3e159d Delete dead code from CordovaActivity 2014-07-04 11:46:03 -04:00
Andrew Grieve
95118398dd Update .classpath to make Eclipse happy (just re-orders one line) 2014-07-04 11:38:31 -04:00
Andrew Grieve
4d18a8e55f Delete "CB-3064: The errorUrl is..." Log message left over from debugging presumably 2014-07-04 11:38:16 -04:00
Andrew Grieve
3bab41f138 Refactor Config into ConfigXmlParser, CordovaPreferences
Intention here is to be 100% backwards compatible.
2014-07-04 11:31:32 -04:00
Andrew Grieve
f577af0886 Delete Location-change JS->Native bridge mode
It was always disabled, and there's really no reason to keep it around.
2014-07-03 22:18:18 -04:00
Andrew Grieve
aab47bd453 CB-5988 Allow exec() only from file: or start-up URL's domain
Uses prompt() to validate the origin of the calling JS.
This change also simplifies the start-up logic by explicitly disabling
the bridge during page transitions and explictly enabling it when the
JS asks for the bridgeSecret.

We now wait to fire onNativeReady in JS until the bridge is initialized.
It is therefore safe to delete the queue-clear/new exec race condition
code that was in PluginManager.
2014-07-03 22:06:09 -04:00
Andrew Grieve
445ddd89fb CB-6761 Fix native->JS bridge ceasing to fire when page changes and online is set to false and the JS loads quickly 2014-07-03 13:27:30 -04:00
Joe Bowser
6f21a96238 Update the errorurl to no longer use intents 2014-06-24 12:57:46 -07:00
Joe Bowser
c47bcb2f54 This breaks running the JUnit tests, we'll bring it back soon 2014-06-24 12:55:56 -07:00
Joe Bowser
b0b628ffc2 Refactoring the URI handling on Cordova, removing dead code 2014-06-24 12:30:34 -07:00
Andrew Grieve
4b4a2e9f9e CB-7018 Clean up and deprecation of some button-related functions 2014-06-24 15:08:47 -04:00
Andrew Grieve
58afd0b604 CB-7017 Fix onload=true being set on all subsequent plugins 2014-06-24 14:55:34 -04:00
Ian Clelland
4352456129 CB-5971: Fix package / project validation 2014-06-24 14:05:56 -04:00
Ian Clelland
bb141a70e8 CB-5971: Add unit tests to cordova-android 2014-06-24 14:05:56 -04:00
Ian Clelland
ff260c03ca CB-5971: Factor out package/project name validation logic 2014-06-24 14:05:56 -04:00
Andrew Grieve
297f862ccc Delete explicit activity.finish() in back button handling. No change in behaviour.
The default handling calls through to Activity.onBackPressed(), which
by default results in activity.finish(), but can be customized by the
app.
2014-06-23 14:18:54 -04:00
Joe Bowser
141bbfb051 CB-5971: This would have been a good first bug, too bad 2014-06-12 17:51:22 -07:00
Joe Bowser
663a919ed1 CB-4404: Changing where android:windowSoftInputMode is in the manifest so it works 2014-06-12 11:47:41 -07:00
Marcel Kinard
483babe3bc Add documentation referencing other implementation. 2014-06-09 15:50:20 -04:00
Andrew Grieve
b407641049 CB-6851 Deprecate WebView.sendJavascript() 2014-06-08 22:47:41 -04:00
Marcel Kinard
32e07c22d0 CB-6876 Show the correct executable name 2014-06-04 19:23:43 -04:00
Marcel Kinard
d427c52aac CB-6876 Fix the "print usage" 2014-06-04 19:12:55 -04:00
Joe Bowser
eb623a84d5 Trivial spelling fix in comments when reading CordovaResourceApi 2014-06-04 11:13:37 -07:00
Joe Bowser
07290277ba CB-6818: I want to remove this code, because Square didn't do their headers properly 2014-06-03 15:22:10 -07:00
Andrew Grieve
743541218f CB-6860 Add activity_name and launcher_name to AndroidManifest.xml & strings.xml 2014-06-03 16:08:35 -04:00
Andrew Grieve
94de0a7ce2 Add a comment to custom_rules.xml saying why we move AndroidManifest.xml 2014-06-03 15:29:06 -04:00
Andrew Grieve
36e9fb292b Remove +x from README.md 2014-06-03 15:29:06 -04:00
Marcel Kinard
2661e010d9 CB-6784 Add missing licenses
for ./test/assets/www/cordova_plugins.js and ./test/res/xml/config.xml
2014-05-30 15:10:54 -04:00
Marcel Kinard
7687becfee CB-6784 Add license to CONTRIBUTING.md 2014-05-30 11:45:13 -04:00
Michal Mocny
1641f09dc9 Revert "defaults.xml: Add AndroidLaunchMode preference"
This reverts commit 11fc6be328.
2014-05-29 14:50:01 -04:00
Steven Gill
1505673393 updated RELEASENOTES 2014-05-27 14:12:16 -07:00
Joe Bowser
629e05b7b1 CB-6315: Wrapping this so it runs on the UI thread 2014-05-22 12:00:42 -07:00
Marcel Kinard
e98f6ae570 CB-6723 Update package name for Robotium 2014-05-20 07:09:02 -04:00
Marcel Kinard
0b5bf0c098 CB-6707 Update minSdkVersion to 10 consistently
Update minSdkVersion in the AndroidManifest for the cordova.jar and the
test project.
2014-05-20 07:00:21 -04:00
Martin Gonzalez
1deefa48ee CB-5652 make visible cordova version
Log the cordova version using version string from CordovaWebView.java

This closes #101
2014-05-19 11:10:58 -04:00
Steven Gill
50c4aef873 Update JS snapshot to version 3.6.0-dev (via coho) 2014-05-08 15:32:28 -07:00
Joe Bowser
cf42d31214 Update JS snapshot to version 3.6.0-dev (via coho) 2014-05-06 15:51:17 -07:00
Joe Bowser
00caa1c0a0 Set VERSION to 3.6.0-dev (via coho) 2014-05-06 15:51:14 -07:00
Joe Bowser
93c8ba920a Added dash to test push 2014-05-06 13:59:13 -07:00
Andrew Grieve
8702c04d39 Revert accidentally removed lines from NOTICE 2014-05-06 15:18:31 -04:00
Steven Gill
c349892c5b CB-6552: updated author to apache software foundation in pacakge.json 2014-04-30 12:55:28 -07:00
Steven Gill
3d4b8ce99b CB-6552: updated test field 2014-04-30 11:59:29 -07:00
Steven Gill
64fd87134f CB-6552: added top level package.json 2014-04-30 10:54:58 -07:00
Marcel Kinard
82bba44538 CB-6491 add CONTRIBUTING.md 2014-04-30 08:44:21 -04:00
Ningxin Hu
e6adbb0e42 CB-6543 Fix cordova/run failure when no custom_rules.xml available
Github: Close #99
2014-04-28 22:50:22 -04:00
Fardjad Davari
11fc6be328 defaults.xml: Add AndroidLaunchMode preference 2014-04-25 00:27:33 +04:30
Andrew Grieve
0ec8f5d283 Add JavaDoc for CordovaResourceApi 2014-04-17 15:51:59 -04:00
Ian Clelland
b872df0f31 CB-6388: Handle binary data correctly in LOAD_URL bridge 2014-04-02 13:42:19 -04:00
Ian Clelland
0add4af208 Update JS snapshot to version 3.5.0-dev-81f9a00 2014-04-02 13:42:19 -04:00
Michal Mocny
298cd9e065 Fix CB-6048: Set launchMode=singleTop so tapping app icon does not always restart app 2014-04-01 11:33:40 -04:00
hadeslee
b715d20385 Remove incorrect usage of AlertDialog.Builder.create
AlertDialog.Builder.show() will create an AlertDialog before it show. This is the source code snippet:

        /**
         * Creates a {@link AlertDialog} with the arguments supplied to this builder and
         * {@link Dialog#show()}'s the dialog.
         */
        public AlertDialog show() {
            AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }

github: close #96
2014-03-28 13:40:43 -04:00
Andrew Grieve
9a00ccdacc Catch uncaught exceptions in from plugins and turn them into error responses.
When a plugin throws an unchecked exception, we're not catching it
anywhere and so the error callback is not being called.

This change adds a try/catch to catch such exceptions.
2014-03-06 21:27:44 -05:00
Andrew Grieve
e8d48e1f43 Add NOTICE file 2014-02-27 15:36:30 -05:00
Andrew Grieve
a2f8c9c75b CB-6047 Fix online sometimes getting in a bad state on page transitions.
The online bridge toggles between on & off. Turns out that starting with
Android 4.3, navigation resets the online state, so the code had a 50/50
chance of being caught in the wrong state.

We now forcefully reset the online state on page transitions.
2014-02-26 20:23:07 -05:00
Andrew Grieve
5fb83e7f52 Add another convenience overload for CordovaResourceApi.copyResource 2014-02-26 20:20:10 -05:00
Andrew Grieve
dd6bf568d1 Update framework's .classpath to what Eclipse wants it to be. 2014-02-26 20:18:35 -05:00
Andrew Magee
00ee164cef README.md: android update to android-19. 2014-02-18 22:12:03 -05:00
Andrew Grieve
448071b02d Fix NPE when POLLING bridge mode is used. 2014-02-18 22:11:38 -05:00
Andrew Grieve
4dad9d0e37 Add RELEASENOTES for 3.4.0 2014-02-18 22:11:38 -05:00
Joe Bowser
3a2117c5d3 Updating NOTICE to include Square for OkHttp 2014-02-18 18:31:28 -08:00
Joe Bowser
26a3f6ddc3 Update JS snapshot to version 3.5.0-dev (via coho) 2014-02-14 13:52:31 -08:00
Andrew Grieve
7741312673 CB-5398 Apply KitKat content URI fix to all content URIs 2014-02-14 11:02:17 -05:00
Andrew Grieve
954a1723f1 CB-5398 Work-around for KitKat content: URLs not rendering in <img> tags 2014-02-13 16:10:40 -05:00
Marcel Kinard
87285d94f7 CB-5908: add splascreen images to template
This will enable the default splashscreens to reside in the correct place
after creating a new project. Before only the default icons were present.
I copied these from cordova-app-hello-world.
2014-02-10 16:56:45 -05:00
arudenko
d260d0c182 Added Log.e when Config is not initialised but accessed 2014-02-08 21:24:20 -05:00
Ian Clelland
137eb40fab CB-5395: Make scheme and host (but not path) case-insensitive in whitelist 2014-02-07 15:02:09 -05:00
Andrew Grieve
af440460e1 Fix broken build from prev. commit (copy & paste error?) 2014-02-04 10:00:58 -05:00
Andrew Grieve
a5c8472a37 Ignore multiple onPageFinished() callbacks & onReceivedError due to stopLoading()
I believe this happens only when using custom schemes.
2014-02-04 00:12:11 -05:00
Joe Bowser
dfae37421d Removing addJavascriptInterface support from all Android versions lower than 4.2 due to security vulnerability 2014-02-03 10:11:53 -08:00
Bas Bosman
438a8d8b75 CB-4984 Don't create on CordovaActivity name 2014-01-28 14:25:26 -08:00
Andrew Grieve
ac2034561d CB-5917 Add a loadUrlIntoView overload that doesn't recreate plugins. 2014-01-28 10:30:39 -05:00
Andrew Grieve
c42cd4233d Use thread pool for load timeout. 2014-01-28 10:30:39 -05:00
Andrew Grieve
5b2a73e3eb CB-5715 For CLI, hide assets/www and res/xml/config.xml by default
Add a helper file under assets to say how to re-show the hidden files.
Move the config.xml, www, merges entries to the root to be more discoverable.
2014-01-28 00:28:42 -05:00
Andrew Grieve
6f163a6ba5 CB-5793 ant builds: Rename AndroidManifest during -post-build to avoid Eclipse detecting ant-build/ as a project when importing 2014-01-25 21:58:28 -05:00
Andrew Grieve
cc94cc7d01 CB-5889 Make update script find project name instead of using "null" for CordovaLib 2014-01-24 10:40:40 -05:00
Andrew Grieve
94934ae2cf CB-5889 Add a message in the update script about needing to import CordovaLib when using an IDE. 2014-01-24 10:29:04 -05:00
Joe Bowser
e361f88501 Update JS snapshot to version 3.5.0-dev (via coho) 2014-01-22 16:30:05 -08:00
Joe Bowser
708c042b61 Set VERSION to 3.5.0-dev (via coho) 2014-01-22 16:30:02 -08:00
Andrew Grieve
600599f49e Fix type "LANCH" -> "LAUNCH" 2014-01-22 14:53:40 -05:00
Andrew Grieve
1fe7bbbbc4 CB-5793 Make ant work-around work on windows. 2014-01-21 21:14:32 -05:00
Andrew Grieve
f83d7a7cd1 CB-5793 Add work-around for library references not working with custom output directory (ugh). 2014-01-21 15:09:15 -05:00
Andrew Grieve
7094047b3d CB-5793 Forgot to update ant path for clean. 2014-01-20 10:26:53 -05:00
Andrew Grieve
11d3607688 CB-5793 Don't clean before build and change output directory to ant-build to avoid conflicts with Eclipse. 2014-01-19 23:09:00 -05:00
Andrew Grieve
fcae58d355 CB-4910 Fix CLI's eclipse project template not working on windows due to "*" in the virtual folder name. 2014-01-17 12:00:29 -05:00
Andrew Grieve
ef9ace9e65 CB-5803 Fix cordova/emulate on windows. 2014-01-16 13:19:39 -05:00
Andrew Grieve
22e4039133 CB-5801 Add spawn work-around on windows for it not being able to execute .cmd files
More info: https://github.com/joyent/node/issues/2318
2014-01-16 12:11:31 -05:00
Andrew Grieve
4971670e56 CB-5801 exec->spawn in build to make sure compile errors are shown. 2014-01-15 11:41:34 -05:00
Andrew Grieve
e16cab6b9c CB-5799 Update version of OkHTTP to 1.3 2014-01-15 11:36:43 -05:00
Andrew Grieve
a643c3dba6 Remove package.json within bin/ since we never intend to ship bin/ as an npm module 2014-01-14 16:21:57 -05:00
Andrew Grieve
51abf5b0a6 CB-4910 Update CLI project template to point to config.xml at the root now that it's not in www/ by default. 2014-01-14 12:29:53 -05:00
Andrew Grieve
1cee6e309b Silence excessive logging from scroll events 2014-01-13 12:07:54 -05:00
Joe Bowser
0777a660bf CB-5504: Adding onDestroy to app plugin to deregister telephonyReceiver 2014-01-08 13:12:00 -08:00
Andrew Grieve
5e0479e414 CB-5715 Add Eclipse .project file to create template.
Adds resource filter to hide CordovaLib/, platform_www/, and cordova/.
Adds linked resources to root www/, merges/, config.xml
2014-01-02 16:40:48 -05:00
Andrew Grieve
942c77816d CB-5447 Removed android:debuggable=“true” from project template. 2013-12-30 11:27:17 -05:00
Vyacheslav Shabelnik
8e260d5c40 CB-5714 Fix of android build when too big output stops build with error due to buffer overflow. 2013-12-30 11:06:37 -05:00
Andrew Grieve
7951eee8a3 Fix incorrect MIME type for .js files loaded through CordovaResourceAPI.
This fixes devtools complaining about .js files being served as "text/plain"
when they have gone through remapUrl().
2013-12-23 15:04:54 -05:00
Justin Wark
be2f7d7a8a Remove 2 X console.log from exec.js
With these console.log statements the output from commands such as 'list-devices' is very messy and hence difficult to parse the output programatically.
2013-12-23 08:54:46 -05:00
Andrew Grieve
59c8e8b46e CB-5592 Set MIME type for openExternal when scheme is file: 2013-12-20 11:17:24 -05:00
Andrew Grieve
98c8b28bf3 Add RELEASENOTES for 3.3.0 release 2013-12-16 13:58:45 -05:00
Andrew Grieve
7bb5bc01b7 Backfill 3.2.0 release notes 2013-12-16 13:58:45 -05:00
Marcel Kinard
1482c07ae4 CB-5489: clean up docs for deprecated methods
- fixed grammer in log message in Config.java
- updated the class javadoc in CordovaActivity to catch up to reality: use config.xml instead of set*Property() methods.
- added deprecation message for SetFullscreen
2013-12-14 08:02:58 -05:00
Joe Bowser
adba84ae6a CB-5504: Moving code to the App plugin inside Cordova, the place where the grey area beween plugin and platform exists 2013-12-09 14:03:22 -08:00
Michal Mocny
146e296826 CB-5047: Adding a defaults.xml template
This template will be consumed by the CLI, instead of the default
platform config.xml which is consumed by the bin/create workflow.  We
remove the user app specific settings since those are to be edited in
the top level app config.xml by the user, and are injected by the CLI.
2013-12-05 14:33:21 -05:00
Håkon Nilsen
28c10dba09 CB-5481 Fix for Cordova trying to get config.xml from the wrong namespace 2013-12-05 12:45:34 -05:00
charles bourasseau
e646a0840d Add missing semicolon 2013-12-05 12:41:28 -05:00
Josh Soref
74ea6bf00a Spelling fixes 2013-12-05 12:20:58 -05:00
Rich Trott
d7ad784809 CB-5144 Spelling & grammar fixes in README. 2013-12-05 12:13:07 -05:00
Joe Bowser
642bd10dcc Forgot Apache Headers on MessageTest 2013-12-03 15:52:38 -08:00
Joe Bowser
7c566c36f4 Update JS snapshot to version 3.4.0-dev (via coho) 2013-12-03 15:44:23 -08:00
Joe Bowser
dbbe038939 Set VERSION to 3.4.0-dev (via coho) 2013-12-03 15:44:21 -08:00
ignisvulpis
e3430a916c prevent ClassNotFound exception for emtpy class name
Signed-off-by: Joe Bowser <bowserj@apache.org>
2013-12-03 15:09:32 -08:00
Joe Bowser
ea1f041e11 CB-5487: Remote Debugging is on when your Android app is debuggable. 2013-11-29 15:54:07 -08:00
Joe Bowser
0fe6d9f367 Updating the README 2013-11-29 14:28:11 -08:00
Joe Bowser
46e7359372 Making the object less chatty 2013-11-29 14:28:11 -08:00
Joe Bowser
41cace9a96 Updating tests to KitKat, and making the tests more thread-safe 2013-11-29 14:28:10 -08:00
Joe Bowser
4638331cb4 Incrementing API target 2013-11-29 14:28:10 -08:00
Joe Bowser
e339a7583c CB-5445: Adding onScrollChanged and the ScrollEvent object. (Forgot to add the WebView) 2013-11-29 14:28:10 -08:00
Joe Bowser
0b7570c9ee CB-5445: Adding onScrollChanged and the ScrollEvent object 2013-11-29 14:28:10 -08:00
Joe Bowser
a85acfcfc5 Updated CordovaWebView to experiment with onScrollChanged messages 2013-11-29 14:28:10 -08:00
Michal Mocny
3d4ccbec23 Moving the console.log out of run() method
Since cordova-cli calls the check_req library run() method, we do not
want to always console.log on success in there (not usually a useful side
effect).
2013-11-29 14:39:09 -05:00
Mark Koudritsky
2f66ec60db CB-5422: Don't require JAVA_HOME to be defined
JAVA_HOME is not necessarily defined on Linux and Mac.
Print out the value of JAVA_HOME in the error message
in case "java -version" fails.
2013-11-29 14:31:48 -05:00
Joe Bowser
f1cfe2b07b Thanks for Benn Mapes for making this process easy. Updating the Android API level. 2013-11-27 12:09:17 -08:00
Marcel Kinard
6160ca6e30 CB-5490: add javadoc target to ant script
- add javadoc target to ant script. It must be invoked explicitly to run.
- ignore the generated javadoc html directories.
- clean up javadoc errors in source files.
- upon invoking 'clean' target, erase generated jar and javadoc
2013-11-26 13:06:28 -05:00
Marcel Kinard
b621c3e4c4 CB-5471: add deprecation javadoc/annotation 2013-11-26 12:05:23 -05:00
Marcel Kinard
64d2ae9ad4 Add javadoc comments to source classes 2013-11-22 17:42:58 -05:00
Joe Bowser
39fc45b8d8 CB-5255: Checking in the Google Check, TODO: Add Amazon FireOS check 2013-11-20 09:35:23 -08:00
Andrew Grieve
fd954adc81 CB-5232 Change create script to use Cordova as a library. 2013-11-15 13:17:56 -05:00
Andrew Grieve
8b379cbf56 Remove Application settings from framework/AndroidManifest.xml
They aren't needed since framework is a library.
2013-11-15 12:01:12 -05:00
Marcel Kinard
59c0b04602 CB-5346: remove dependency on device plugin
- remove dependency on the device plugin in the js and html
- clean out dollar signs chars that were accidently appended to the license
- fix punctuation in the viewport value
2013-11-13 15:09:19 -05:00
Marcel Kinard
11b3cf3bfd CB-5346: delete a stale file that isn't needed 2013-11-13 13:54:33 -05:00
Joe Bowser
9254f5a8a5 Updating instructions to indicate that the device plugin is required to be installed via plugman 2013-11-12 14:34:14 -08:00
Joe Bowser
e5b68f4a3c Fixing plugins configuration, Device shouldn't be checked in here 2013-11-12 14:27:09 -08:00
Joe Bowser
95babc01e3 Removing device plugin 2013-11-12 14:25:37 -08:00
Joe Bowser
4dd792a49f Removing the plugins directory after the plugins were incorrectly removed 2013-11-12 14:24:30 -08:00
Joe Bowser
207c50e500 This should use plugman to install plugins. Adding path depenencies for plugins is wrong, and shouldn't be done 2013-11-12 13:59:17 -08:00
Steven Gill
763e34e861 CB-5349: fixed regression in update script 2013-11-11 16:32:34 -08:00
Marcel Kinard
b895a0c335 CB-5346 Fix and cleanup broken Android unit test
- Found stale cordova.js in the test project. Changed ant script to copy in
  a fresh one in the pre-build stage. Removed stale copies from git, added
  to .gitignore. This is what was causing the test failure when sending
  javascript to the webview.
- Found almost-stale copy of device plugin, did the same as above.
- Fixed spacing in log messages in CordovaWebView.
- Updated README.md with current information.
- Removed dollar signs that were previously accidently appended to the license.
- Added cordova version to html pages, next to the other metadata.
- Fix incorrect separator in the viewport meta tag.
- Changed old-style <log> tag to <preference name="loglevel"> in config.xml.
- In BackButtonMultiPageTest, increased the TIMEOUT so the deviceready event
  can trigger the referenced javascript, improved the setup() method to
  explicitly load the starting point.
2013-11-11 16:19:01 -05:00
Josh Soref
c5767eb545 CB-5307 Remove references to Callback and Incubator 2013-11-07 09:02:17 -05:00
Joe Bowser
94fb79c17f CB-5302: Massive movement to get tests working again 2013-11-06 15:06:20 -08:00
Joe Bowser
6856b02aa7 Set VERSION to 3.3.0-dev (via coho) 2013-11-06 15:06:19 -08:00
Marcel Kinard
04f812c136 CB-5301 add missing license headers 2013-11-06 16:27:46 -05:00
Braden Shepherdson
8783cf03b2 [CB-4996] Fix paths with spaces while launching on emulator and device 2013-11-06 10:37:59 -05:00
Marcel Kinard
4e1156e083 CB-5284 Fixing the version from coho
Fixing the hardcoded versions, it got auto-incremented by accident.
2013-11-05 14:43:01 -05:00
Joe Bowser
1d6e1d416b Fixing the VERSION file, it got auto-incremented by coho by accident 2013-11-01 15:29:08 -07:00
Joe Bowser
fd02e5a07e Update JS snapshot to version 2.10.0-dev (via coho) 2013-11-01 11:11:13 -07:00
Joe Bowser
937056fcaf Set VERSION to 2.10.0-dev (via coho) 2013-11-01 11:11:09 -07:00
Carlos Santana
41ed18684d CB-5209 Win: Cannot build Android app if project path contains spaces 2013-10-29 16:29:19 -04:00
Joe Bowser
96d4a22215 CB-5209: Dirty, Dirty Fix for Building. This works, but needs to be prettier. 2013-10-29 14:27:46 -04:00
Andrew Grieve
6e4ef508e8 CB-5193 Fix Android WebSQL sometime throwing SECURITY_ERR.
Turns out our Quota logic has been wrong all along. If we were to
actually track the quota needed, we'd need to store a map of
dbName->quota for all dbNames. Instead, we just set a really high quota
since we don't ever want to decline storage.

I *think* this fixes all of the SECURITY_ERR exceptions we've been
seeing. Even those on Honeycomb / ICS.
2013-10-24 11:08:02 -04:00
Andrew Grieve
4437d7f3c4 CB-5191 Deprecate <url-filter> 2013-10-23 22:02:49 -04:00
Braden Shepherdson
001570e941 Updating shelljs to 0.2.6. Copy now preserves mode bits. 2013-10-23 15:22:36 -04:00
Tim Kim
28c41294bb CB-4872 - moved version script to promise model 2013-10-18 17:13:57 -07:00
Tim Kim
82898da507 CB-4872 - make sure to copy over version scripts to project 2013-10-18 17:13:57 -07:00
Tim Kim
eabcdbf129 [CB-4872] - added android version scripts 2013-10-18 17:13:57 -07:00
mbillau
5ab11edad2 CB-5117: Output confirmation message if check_reqs passes. 2013-10-18 14:22:27 -04:00
Braden Shepherdson
b57317bdc2 Refactoring Android project-level and platform scripts to use Q.js
This eliminates the use of shelljs.exec's sync mode, which is the source
of the filehandle leaks that can cause EMFILE on OSX, and are
CPU-intensive everywhere.

Tested locally, needs poking before it gets released.
2013-10-16 13:10:53 -04:00
Braden Shepherdson
1f49f29183 Updating to latest shelljs, old version doesn't preserve +x bits 2013-10-16 13:10:39 -04:00
Andrew Grieve
638fbfabdc Remove cordova.xml fallback from Config.java (it was removed from PluginManager for 3.0) 2013-10-15 12:17:31 -04:00
Andrew Grieve
16de12a3ba CB-5080 Find resources in a way that works with aapt's --rename-manifest-package 2013-10-15 12:17:14 -04:00
Andrew Grieve
1316578ba3 Update JS snapshot to version 3.2.0-dev (via coho) 2013-10-02 12:40:53 -04:00
Andrew Grieve
1926c50f09 Remove a couple incorrect lines from RELEASENOTES.md 2013-10-01 15:13:07 -04:00
Joe Bowser
b858a4a5d7 CB-4961: shell.js returns the full path on ls, rebuilding the full path isn't really needed 2013-09-30 16:01:42 -07:00
Joe Bowser
ec9741443b Updating README.md to have latest Android SDK 2013-09-26 14:35:00 -07:00
Joe Bowser
fe7b2a36ec CB-4527: This was an easy fix, since the script deletes batch files 2013-09-24 10:52:41 -07:00
Andrew Grieve
2d88a726b7 [CB-4892] Fix create script only escaping the first space instead of all spaces. 2013-09-23 10:16:58 +02:00
Andrew Grieve
a51edd3579 Fix update script to clobber cordova.js file (missing -f) 2013-09-19 15:47:49 -04:00
Andrew Grieve
b7ede8f9ba Add missing copyright header for Whitelist.java. 2013-09-17 14:20:57 -04:00
Andrew Grieve
15f36cc19d [CB-4832] Add 3.1.0 RELEASENOTES.md 2013-09-17 13:08:07 -04:00
Andrew Grieve
95edd970bc Update JS snapshot to version 3.2.0-dev (via coho) 2013-09-17 11:03:52 -04:00
Andrew Grieve
2c6285d4b3 Set VERSION to 3.2.0-dev (via coho) 2013-09-17 11:03:51 -04:00
Andrew Grieve
1d1cdb5ea4 Always have create script rebuild jar when on a dev version 2013-09-16 14:25:12 -04:00
Andrew Grieve
34bdef9c45 [CB-4817] Remove unused assets in project template. 2013-09-13 23:07:41 -04:00
Andrew Grieve
cad673f8cd [CB-4817] Avoid generating unused files in create script
by using "android update project" instead of "android create project"
2013-09-13 22:49:15 -04:00
Andrew Grieve
7c446b222d [CB-3542] Convert update script to be node-based. 2013-09-13 22:49:15 -04:00
Andrew Grieve
1bd0f8fcf4 [CB-3542] Delete old ant-based bin/create.xml file. 2013-09-13 22:49:14 -04:00
Andrew Grieve
dd3c261ba2 [CB-3542] Only update framework/ project when building the jar.
No need to do it during every check_reqs.
This also extracts the jar building into a helper function.
2013-09-13 22:49:14 -04:00
Andrew Grieve
70cc711ec1 [CB-3542] Delete custom replaceInFile with shelljs.sed(). 2013-09-13 22:07:38 -04:00
Andrew Grieve
485f2ee923 [CB-3542] Fail create script if a copy fails. 2013-09-13 22:07:35 -04:00
Andrew Grieve
129be6e476 [CB-3542] Delete extra message when check_reqs fails.
check_reqs already outputs a message.
2013-09-13 22:07:31 -04:00
Andrew Grieve
c08b64efb3 [CB-3542] Fix create script's check for whether cordova.jar exists. 2013-09-13 22:07:23 -04:00
Andrew Grieve
f4a0f55b13 Fail fast in create script if package name is not com.foo.bar. 2013-09-13 16:08:45 -04:00
Joe Bowser
39d32bcb5b CB-4620: Throwing this over the fence, since there is a problem actually in the wild here 2013-09-11 15:02:00 -07:00
Andrew Grieve
3351fdbc74 Restore deleted classes (FileHelper, etc) and deprecate them instead.
The classes are:
[CB-4766] JSONUtils.java
[CB-4765] ExifHelper.java
[CB-4764] DirectoryManager.java
[CB-4763] FileHelper.java
2013-09-11 11:13:57 -04:00
378 changed files with 24417 additions and 20799 deletions

5
.gitignore vendored
View File

@@ -14,11 +14,16 @@ framework/assets/www/.DS_Store
framework/assets/www/cordova-*.js
framework/assets/www/phonegap-*.js
framework/libs
framework/javadoc-public
framework/javadoc-private
test/libs
example
./test
test/bin
test/assets/www/.tmp*
test/assets/www/cordova.js
test/cordova/plugins/org.apache.cordova.device/www/device.js
test/cordova/plugins/org.apache.cordova.device/src/android/Device.java
tmp/**
.metadata
tmp/**/*

37
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,37 @@
<!--
#
# 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.
#
-->
# Contributing to Apache Cordova
Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/#contribute).
The details are explained there, but the important items are:
- Sign and submit an Apache ICLA (Contributor License Agreement).
- Have a Jira issue open that corresponds to your contribution.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

87
LICENSE
View File

@@ -199,4 +199,89 @@
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.
limitations under the License.
ADDITIONAL LICENSES:
================================================================================
bin/node_modules/q
================================================================================
Copyright 20092012 Kristopher Michael Kowal. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
================================================================================
bin/node_modules/shelljs
================================================================================
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
All rights reserved.
You may use this project under the terms of the New BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Artur Adib nor the
names of the contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
bin/node_modules/shelljs
================================================================================
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

8
NOTICE
View File

@@ -1,5 +1,5 @@
Apache Cordova
Copyright 2012 The Apache Software Foundation
Copyright 2014 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org)
@@ -10,8 +10,8 @@ The Apache Software Foundation (http://www.apache.org)
== in this case for the Android-specific code. ==
=========================================================================
Android Code
Copyright 2005-2008 The Android Open Source Project
This product includes software developed as part of
The Android Open Source Project (http://source.android.com).
This software includes software developed at Square, Inc.
Copyright (C) 2013 Square, Inc.

24
README.md Executable file → Normal file
View File

@@ -7,9 +7,9 @@
# 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
@@ -23,27 +23,27 @@ Cordova Android
Cordova Android is an Android application library that allows for Cordova-based
projects to be built for the Android Platform. Cordova based applications are,
at the core, applications written with web technology: HTML, CSS and JavaScript.
at the core, applications written with web technology: HTML, CSS and JavaScript.
[Apache Cordova](http://cordova.io) is a project at The Apache Software Foundation (ASF).
[Apache Cordova](http://cordova.io) is a project of The Apache Software Foundation (ASF).
Requires
---
- Java JDK 1.5 or greater
- Apache ANT 1.8.0 or greater
- Apache Ant 1.8.0 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
Cordova Android Developer Tools
---
The Cordova developer tooling is split between general tooling and project level tooling.
The Cordova developer tooling is split between general tooling and project level tooling.
General Commands
./bin/create [path package activity] ... create the ./example app or a cordova android project
./bin/create [path package activity] ... creates the ./example app or a cordova android project
./bin/check_reqs ....................... checks that your environment is set up for cordova-android development
./bin/update [path] .................... updates an existing cordova-android project to the version of the framework
@@ -53,7 +53,7 @@ These commands live in a generated Cordova Android project. Any interactions wit
./cordova/clean ........................ cleans the project
./cordova/build ........................ calls `clean` then compiles the project
./cordova/log ........................ stream device or emulate logs to stdout
./cordova/log ........................ streams device or emulator logs to STDOUT
./cordova/run ........................ calls `build` then deploys to a connected Android device. If no Android device is detected, will launch an emulator and deploy to it.
./cordova/version ...................... returns the cordova-android version of the current project
@@ -69,13 +69,13 @@ Importing a Cordova Android Project into Eclipse
Building without the Tooling
---
Note: The Developer Tools handle this. This is only to be done if the tooling fails, or if
Note: The Developer Tools handle this. This is only to be done if the tooling fails, or if
you are developing directly against the framework.
To create your `cordova.jar` file, run in the framework directory:
android update project -p . -t android-17
android update project -p . -t android-19
ant jar
@@ -84,7 +84,7 @@ Running Tests
Please see details under test/README.md.
Further Reading
---
----
- [http://developer.android.com](http://developer.android.com)
- [http://cordova.apache.org/](http://cordova.apache.org)

276
RELEASENOTES.md Normal file
View File

@@ -0,0 +1,276 @@
<!--
#
# 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.
#
-->
## Release Notes for Cordova (Android) ##
### 3.6.4 (Sept 30, 2014) ###
* Set VERSION to 3.6.4 (via coho)
* Update JS snapshot to version 3.6.4 (via coho)
* CB-7634 Detect JAVA_HOME properly on Ubuntu
* CB-7579 Fix run script's ability to use non-arch-specific APKs
* CB-6511 Fixes build for android when app name contains unicode characters.
* CB-7463: Adding licences. I don't know what the gradle syntax is for comments, that still needs to be done.
* CB-7463: Looked at the Apache BigTop git, gradle uses C-style comments
* CB-7460: Fixing bug with KitKat where the background colour would override the CSS colours on the application
* CB-7674 Preference activation no longer occurs in CordovaActivity.onCreate()
### 3.6.0 (Sept 2014) ###
* Set VERSION to 3.6.0 (via coho)
* CB-7410 fix the menu test
* CB-7410 Fix the errorUrl test
* CB-7410 Fix Basic Authentication test
* CB-3445: Allow build and run scripts to select APK by architecture
* CB-3445: Add environment variable 'BUILD_MULTIPLE_APKS' for splitting APKs based on architecture
* CB-3445: Ensure that JAR files in libs directory are included
* CB-7267 update RELEASENOTES for 3.5.1
* CB-7410 clarify the title
* CB-7385 update cordova.js for testing prior to branch/tag
* CB-7410 add whitelist entries to get iframe/GoogleMaps working
* CB-7291 propogate change in method signature to the native tests
* CB-7291: Restrict meaning of "\*" in internal whitelist to just http and https
* CB-7291: Only add file, content and data URLs to internal whitelist
* CB-7291: Add defaults to external whitelist
* CB-7291: Add external-launch-whitelist and use it for filtering intent launches
* CB-3445: Read project.properties to configure gradle libraries
* CB-7325 Fix error message in android_sdk_version.js when missing SDK on windows
* CB-7335 Add a .gitignore to android project template
* CB-7330 Fix dangling function call in last commit (broke gradle builds)
* CB-7330 Don't run "android update" during creation
* CB-3445 Add gradle support clean command (plus some code cleanup)
* CB-7044 Fix typo in prev commit causing check_reqs to always fail.
* CB-3445 Copy gradle wrapper in build instead of create
* CB-3445 Add .gradle template files for "update" as well as "create"
* CB-7044 Add JAVA_HOME when not set. Be stricter about ANDROID_HOME
* CB-3445 Speed up gradle building (incremental builds go from 10s -> 1.5s for me)
* CB-3445: android: Copy Gradle wrapper from Android SDK rather than bundling a JAR
* CB-3445: Add which to checked-in node_modules
* CB-3445: Add option to build and install with gradle
* CB-3445: Add an initial set of Gradle build scripts
* CB-7321 Don't require ant for create script
* CB-7044, CB-7299 Fix up PATH problems when possible.
* Change in test's AndroidManifest.xml needed for the test to run properly. Forgot the manifest.
* Change in test's AndroidManifest.xml needed for the test to run properly
* Adding tests related to 3.5.1
* CB-7261 Fix setNativeToJsBridgeMode sometimes crashing when switching to ONLINE_EVENT
* CB-7265 Fix crash when navigating to custom protocol (introduced in 3.5.1)
* Filter out non-launchable intents
* Handle unsupported protocol errors in webview better
* CB-7238: I should have collapsed this, but Config.init() must go before the creation of CordovaWebView
* CB-7238: Minor band-aid to get tests running again, this has to go away before 3.6.0 is released, since this is an API change.
* Extend whitelist to handle URLs without // chars
* CB-7172 Force window to have focus after resume
* CB-7159 Set background color of webView as well as its parent
* CB-7018 Fix setButtonPlumbedToJs never un-listening
* Undeprecate some just-deprecated symbols in PluginManager.
* @Deprecate methods of PluginManager that were never meant to be public
* Move plugin instantiation and instance storing logic PluginEntry->PluginManager
* Fix broken unit test due to missing Config.init() call
* Update to check for Google Glass APIs
* Fix for `android` not being in PATH check on Windows
* Displaying error when regex does not match.
* Fix broken compile due to previous commit :(
* Tweak CordovaPlugin.initialize method to be less deprecated.
* Un-deprecate CordovaActivity.init() - it's needed to tweak prefs in onCreate
* Tweak log messages in CordovaBridge with bridgeSecret is wrong
* Backport CordovaBridge from 4.0.x -> master
* Update unit tests to not use most deprecated things (e.g. DroidGap)
* Add non-String overloades for CordovaPreferences.set()
* Make CordovaWebview resilient to init() not being called (for backwards-compatibility)
* Add node_module licenses to LICENSE
* Update cordova.js snapshot to work with bridge changes
* Provide CordovaPlugin with CordovaPreferences. Add new Plugin.initialize()
* Convert usages of Config.\* to use the non-static versions
* Change getProperty -> prefs.get\* within CordovaActivity
* Make CordovaUriHelper class package-private
* Fix PluginManager.setPluginEntries not removing old entries
* Move registration of App plugin from config.xml -> code
* Make setWebViewClient an override instead of an overload. Delete Location-change JS->Native bridge mode (missed some of it).
* CB-4404 Revert setting android:windowSoftInputMode to "adjustPan"
* Refactor: Use ConfigXmlParser in activity. Adds CordovaWebView.init()
* Deprecate some convenience methods on CordovaActivity
* Fix CordovaPreferences not correctly parsing hex values (valueOf->decode)
* Refactor: Move url-filter information into PluginEntry.
* Don't re-parse config.xml in onResume.
* Move handling of Fullscreen preference to CordovaActivity
* Delete dead code from CordovaActivity
* Update .classpath to make Eclipse happy (just re-orders one line)
* Delete "CB-3064: The errorUrl is..." Log message left over from debugging presumably
* Refactor Config into ConfigXmlParser, CordovaPreferences
* Delete Location-change JS->Native bridge mode
* CB-5988 Allow exec() only from file: or start-up URL's domain
* CB-6761 Fix native->JS bridge ceasing to fire when page changes and online is set to false and the JS loads quickly
* Update the errorurl to no longer use intents
* This breaks running the JUnit tests, we'll bring it back soon
* Refactoring the URI handling on Cordova, removing dead code
* CB-7018 Clean up and deprecation of some button-related functions
* CB-7017 Fix onload=true being set on all subsequent plugins
* CB-5971: Fix package / project validation
* CB-5971: Add unit tests to cordova-android
* CB-5971: Factor out package/project name validation logic
* Delete explicit activity.finish() in back button handling. No change in behaviour.
* CB-5971: This would have been a good first bug, too bad
* CB-4404: Changing where android:windowSoftInputMode is in the manifest so it works
* Add documentation referencing other implementation.
* CB-6851 Deprecate WebView.sendJavascript()
* CB-6876 Show the correct executable name
* CB-6876 Fix the "print usage"
* Trivial spelling fix in comments when reading CordovaResourceApi
* CB-6818: I want to remove this code, because Square didn't do their headers properly
* CB-6860 Add activity_name and launcher_name to AndroidManifest.xml & strings.xml
* Add a comment to custom_rules.xml saying why we move AndroidManifest.xml
* Remove +x from README.md
* CB-6784 Add missing licenses
* CB-6784 Add license to CONTRIBUTING.md
* Revert "defaults.xml: Add AndroidLaunchMode preference"
* updated RELEASENOTES
* CB-6315: Wrapping this so it runs on the UI thread
* CB-6723 Update package name for Robotium
* CB-6707 Update minSdkVersion to 10 consistently
* CB-5652 make visible cordova version
* Update JS snapshot to version 3.6.0-dev (via coho)
* Update JS snapshot to version 3.6.0-dev (via coho)
* Set VERSION to 3.6.0-dev (via coho)
### 3.5.1 (August 2014) ###
This was a security update to address CVE-2014-3500, CVE-2014-3501,
and CVE-2014-3502. For more information, see
http://cordova.apache.org/announcements/2014/08/04/android-351.html
* Filter out non-launchable intents
* Handle unsupported protocol errors in webview better
* Update the errorurl to no longer use intents
* Refactoring the URI handling on Cordova, removing dead code
### 3.5.0 (May 2014) ###
* OkHttp has broken headers. Updating for ASF compliance.
* Revert accidentally removed lines from NOTICE
* CB-6552: added top level package.json
* CB-6491 add CONTRIBUTING.md
* CB-6543 Fix cordova/run failure when no custom_rules.xml available
* defaults.xml: Add AndroidLaunchMode preference
* Add JavaDoc for CordovaResourceApi
* CB-6388: Handle binary data correctly in LOAD_URL bridge
* Fix CB-6048: Set launchMode=singleTop so tapping app icon does not always restart app
* Remove incorrect usage of AlertDialog.Builder.create
* Catch uncaught exceptions in from plugins and turn them into error responses.
* Add NOTICE file
* CB-6047 Fix online sometimes getting in a bad state on page transitions.
* Add another convenience overload for CordovaResourceApi.copyResource
* Update framework's .classpath to what Eclipse wants it to be.
* README.md: `android update` to `android-19`.
* Fix NPE when POLLING bridge mode is used.
* Updating NOTICE to include Square for OkHttp
* CB-5398 Apply KitKat content URI fix to all content URIs
* CB-5398 Work-around for KitKat content: URLs not rendering in <img> tags
* CB-5908: add splascreen images to template
* CB-5395: Make scheme and host (but not path) case-insensitive in whitelist
* Ignore multiple onPageFinished() callbacks & onReceivedError due to stopLoading()
* Removing addJavascriptInterface support from all Android versions lower than 4.2 due to security vu
* CB-4984 Don't create on CordovaActivity name
* CB-5917 Add a loadUrlIntoView overload that doesn't recreate plugins.
* Use thread pool for load timeout.
* CB-5715 For CLI, hide assets/www and res/xml/config.xml by default
* CB-5793 ant builds: Rename AndroidManifest during -post-build to avoid Eclipse detecting ant-build/
* CB-5889 Make update script find project name instead of using "null" for CordovaLib
* CB-5889 Add a message in the update script about needing to import CordovaLib when using an IDE.
### 3.4.0 (Feb 2014) ###
43 commits from 10 authors. Highlights include:
* Removing addJavascriptInterface support from all Android versions lower than 4.2 due to security vulnerability
* CB-5917 Add a loadUrlIntoView overload that doesn't recreate plugins.
* CB-5889 Make update script find project name instead of using "null" for CordovaLib
* CB-5889 Add a message in the update script about needing to import CordovaLib when using an IDE.
* CB-5793 Don't clean before build and change output directory to ant-build to avoid conflicts with Eclipse.
* CB-5803 Fix cordova/emulate on windows.
* CB-5801 exec->spawn in build to make sure compile errors are shown.
* CB-5799 Update version of OkHTTP to 1.3
* CB-4910 Update CLI project template to point to config.xml at the root now that it's not in www/ by default.
* CB-5504 Adding onDestroy to app plugin to deregister telephonyReceiver
* CB-5715 Add Eclipse .project file to create template. For CLI projects, it adds refs for root www/ & config.xml and hides platform versions
* CB-5447 Removed android:debuggable=“true” from project template.
* CB-5714 Fix of android build when too big output stops build with error due to buffer overflow.
* CB-5592 Set MIME type for openExternal when scheme is file:
### 3.3.0 (Dec 2013) ###
41 commits from 11 authors. Highlights include:
* CB-5481 Fix for Cordova trying to get config.xml from the wrong namespace
* CB-5487 Enable Remote Debugging when your Android app is debuggable.
* CB-5445 Adding onScrollChanged and the ScrollEvent object
* CB-5422 Don't require JAVA_HOME to be defined
* CB-5490 Add javadoc target to ant script
* CB-5471 Deprecated DroidGap class
* CB-5255 Prefer Google API targets over android-## targets when building.
* CB-5232 Change create script to use Cordova as a Library Project instead of a .jar
* CB-5302 Massive movement to get tests working again
* CB-4996 Fix paths with spaces while launching on emulator and device
* CB-5209 Cannot build Android app if project path contains spaces
### 3.2.0 (Nov 2013) ###
27 commits from 7 authors. Highlights include:
* CB-5193 Fix Android WebSQL sometime throwing SECURITY_ERR.
* CB-5191 Deprecate <url-filter>
* Updating shelljs to 0.2.6. Copy now preserves mode bits.
* CB-4872 Added android version scripts (android_sdk_version, etc)
* CB-5117 Output confirmation message if check_reqs passes.
* CB-5080 Find resources in a way that works with aapt's --rename-manifest-package
* CB-4527 Don't delete .bat files even when on non-windows platform
* CB-4892 Fix create script only escaping the first space instead of all spaces.
### 3.1.0 (Sept 2013) ###
55 commits from 9 authors. Highlights include:
* [CB-4817] Remove unused assets in project template.
* Fail fast in create script if package name is not com.foo.bar.
* [CB-4782] Convert ApplicationInfo.java -> appinfo.js
* [CB-4766] Deprecated JSONUtils.java (moved into plugins)
* [CB-4765] Deprecated ExifHelper.java (moved into plugins)
* [CB-4764] Deprecated DirectoryManager.java (moved into plugins)
* [CB-4763] Deprecated FileHelper.java (moved into plugins), Move getMimeType() into CordovaResourceApi.
* [CB-4725] Add CordovaWebView.CORDOVA_VERSION constant
* Incrementing version check for Android 4.3 API Level 18
* [CB-3542] rewrote cli tooling scripts in node
* Allow CordovaChromeClient subclasses access to CordovaInterface and CordovaWebView members
* Refactor CordovaActivity.init so that subclasses can easily override factory methods for webview objects
* [CB-4652] Allow default project template to be overridden on create
* Tweak the online bridge to not send excess online events.
* [CB-4495] Modify start-emulator script to exit immediately on a fatal emulator error.
* Log WebView IOExceptions only when they are not 404s
* Use a higher threshold for slow exec() warnings when debugger is attached.
* Fix data URI decoding in CordovaResourceApi
* [CB-3819] Made it easier to set SplashScreen delay.
* [CB-4013] Fixed loadUrlTimeoutValue preference.
* Upgrading project to Android 4.3
* [CB-4198] bin/create script should be better at handling non-word characters in activity name. Patched windows script as well.
* [CB-4198] bin/create should handle spaces in activity better.
* [CB-4096] Implemented new unified whitelist for android
* [CB-3384] Fix thread assertion when plugins remap URIs

View File

@@ -1 +1 @@
dev
3.6.4

29
bin/android_sdk_version Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env node
/*
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 android_sdk_version = require('./lib/android_sdk_version');
android_sdk_version.run().done(null, function(err) {
console.log(err);
process.exit(2);
});

View File

@@ -0,0 +1,26 @@
:: 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.
@ECHO OFF
SET script_path="%~dp0android_sdk_version"
IF EXIST %script_path% (
node "%script_path%" %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2
EXIT /B 1
)

View File

@@ -21,7 +21,11 @@
var check_reqs = require('./lib/check_reqs');
if(!check_reqs.run()) {
process.exit(2);
}
check_reqs.run().done(
function success() {
console.log('Looks like your environment fully supports cordova-android development!');
}, function fail(err) {
console.log(err);
process.exit(2);
}
);

View File

@@ -18,15 +18,19 @@
specific language governing permissions and limitations
under the License.
*/
var path = require('path');
var args = process.argv;
var path = require('path');
var create = require('./lib/create');
var args = require('./lib/simpleargs').getArgs(process.argv);
// Support basic help commands
if(args.length < 3 || (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help')) {
create.help();
} else {
create.run(args[2], args[3], args[4], args[5]);
if (args['--help'] || args._.length === 0) {
console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'create')) + ' <path_to_new_project> <package_name> <project_name> [<template_path>] [--shared]');
console.log(' <path_to_new_project>: Path to your new Cordova Android project');
console.log(' <package_name>: Package name, following reverse-domain style convention');
console.log(' <project_name>: Project name');
console.log(' <template_path>: Path to a custom application template to use');
console.log(' --shared will use the CordovaLib project directly instead of making a copy.');
process.exit(1);
}
create.createProject(args._[0], args._[1], args._[2], args._[3], args['--shared'], args['--cli']).done();

View File

@@ -1,98 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
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 name="cordova" default="create" basedir="../">
<property name="project.path" value="${basedir}/example"/>
<property name="package" value="org.apache.cordova.example"/>
<property name="activity" value="cordovaExample"/>
<target name="create">
<!-- this stuff is seriously stupid -->
<echo file="tmp/package.tmp">package-as-path=${package}</echo>
<replace file="tmp/package.tmp" token="." value="\\" />
<property file="tmp/package.tmp" />
<property name="activity.path" value="${project.path}/src/${package-as-path}/${activity}.java" />
<property name="manifest.path" value="${project.path}/AndroidManifest.xml" />
<!-- get the highest target on this machine -->
<!-- this stuff is also seriously stupid -->
<exec executable="cmd" osfamily="windows" output="tmp/target.list.tmp">
<arg line="/c android.bat list targets"/>
</exec>
<exec executable="android" osfamily="mac" output="tmp/target.list.tmp">
<arg line="list targets"/>
</exec>
<replaceregexp file="tmp/target.list.tmp" match=".*id:\s([0-9]).*" replace="target=\1" flags="s" />
<property file="tmp/target.list.tmp" />
<!-- var VERSION=read('VERSION').replace(/\r\n/,'').replace(/\n/,''); -->
<copy file="VERSION" tofile="tmp/VERSION.tmp" overwrite="true" />
<replaceregexp file="tmp/VERSION.tmp" match="^" replace="version=" />
<replaceregexp file="tmp/VERSION.tmp" match="\r\n" replace="" />
<property file="tmp/VERSION.tmp" />
<!-- clobber any existing example -->
<!-- create the project -->
<exec executable="cmd" osfamily="windows">
<arg line="/c android.bat create project --target ${target} --path ${project.path} --package ${package} --activity ${activity}"/>
</exec>
<exec executable="android" osfamily="mac">
<arg line="create project --target ${target} --path ${project.path} --package ${package} --activity ${activity}"/>
</exec>
<!-- update the framework dir -->
<exec executable="cmd" osfamily="windows">
<arg line="/c android.bat update project --target ${target} --path ${basedir}/framework"/>
</exec>
<exec executable="android" osfamily="mac">
<arg line="update project --target ${target} --path ${basedir}/framework"/>
</exec>
<!-- compile cordova.js and cordova.jar -->
<!-- // if you see an error about "Unable to resolve target" then you may need to
// update your android tools or install an additional Android platform version -->
<ant antfile="${basedir}/framework/build.xml" useNativeBasedir="true" inheritAll="false" />
<!-- copy in the project template -->
<copy todir="${project.path}" overwrite="true">
<fileset dir="${basedir}/bin/templates/project"/>
</copy>
<!-- copy in cordova.js -->
<copy file="${basedir}/framework/assets/www/cordova-${version}.js" todir="${project.path}/assets/www/" />
<!-- copy in cordova.jar -->
<copy file="${basedir}/framework/cordova-${version}.jar" todir="${project.path}/libs/" />
<!-- copy in default activity -->
<copy file="${basedir}/bin/templates/Activity.java" tofile="${activity.path}" overwrite="true" />
<!-- interpolate the activity name and package -->
<replaceregexp file="${activity.path}" match="__ACTIVITY__" replace="${activity}" />
<replaceregexp file="${activity.path}" match="__ID__" replace="${package}" />
<replaceregexp file="${manifest.path}" match="__ACTIVITY__" replace="${activity}" />
<replaceregexp file="${manifest.path}" match="__PACKAGE__" replace="${package}" />
</target>
</project>

65
bin/lib/android_sdk_version.js Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env node
/*
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 shell = require('shelljs'),
child_process = require('child_process'),
Q = require('q');
get_highest_sdk = function(results){
var reg = /\d+/;
var apiLevels = [];
for(var i=0;i<results.length;i++){
apiLevels[i] = parseInt(results[i].match(reg)[0]);
}
apiLevels.sort(function(a,b){return b-a});
console.log(apiLevels[0]);
}
get_sdks = function() {
var d = Q.defer();
child_process.exec('android list targets', function(err, stdout, stderr) {
if (err) d.reject(stderr);
else d.resolve(stdout);
});
return d.promise.then(function(output) {
var reg = /android-\d+/gi;
var results = output.match(reg);
if(results.length===0){
return Q.reject(new Error('No android sdks installed.'));
}else{
get_highest_sdk(results);
}
return Q();
}, function(stderr) {
if (stderr.match(/command\snot\sfound/) || stderr.match(/'android' is not recognized/)) {
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
} else {
return Q.reject(new Error('An error occurred while listing Android targets'));
}
});
}
module.exports.run = function() {
return Q.all([get_sdks()]);
}

View File

@@ -19,67 +19,166 @@
under the License.
*/
var shell = require('shelljs'),
var shelljs = require('shelljs'),
child_process = require('child_process'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
which = require('which'),
ROOT = path.join(__dirname, '..', '..');
var isWindows = process.platform == 'win32';
function forgivingWhichSync(cmd) {
try {
return which.sync(cmd);
} catch (e) {
return '';
}
}
function tryCommand(cmd, errMsg) {
var d = Q.defer();
child_process.exec(cmd, function(err, stdout, stderr) {
if (err) d.reject(new Error(errMsg));
else d.resolve(stdout);
});
return d.promise;
}
// Get valid target from framework/project.properties
module.exports.get_target = function() {
if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
} else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
// if no target found, we're probably in a project and project.properties is in ROOT.
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties'));
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
// this is called on the project itself, and can support Google APIs AND Vanilla Android
var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
shelljs.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
if(target == "" || !target) {
// Try Google Glass APIs
target = shelljs.grep(/target=Google Inc.:Glass Development Kit Preview:[\d+]/, path.join(ROOT, 'project.properties'));
}
return target.split('=')[1].replace('\n', '').replace('\r', '');
}
}
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function() {
var test = shell.exec('ant -version', {silent:true, async:false});
if(test.code > 0) {
console.error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.');
return false;
}
return true;
}
return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function() {
var sdkDir = process.env['ANDROID_HOME'];
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
if (!fs.existsSync(wrapperDir)) {
return Q.reject(new Error('Could not find gradle wrapper within android sdk. Might need to update your Android SDK.\n' +
'Looked here: ' + wrapperDir));
}
return Q.when();
};
// Returns a promise.
module.exports.check_java = function() {
if(process.env.JAVA_HOME) {
var test = shell.exec('java', {silent:true, async:false});
if(test.code > 0) {
console.error('ERROR : executing command \'java\', make sure you java environment is set up. Including your JDK and JRE.');
return false;
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().then(function() {
if (hasJavaHome) {
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
if (!javacPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
}
} else {
if (javacPath) {
// OS X has a command for finding JAVA_HOME.
if (fs.existsSync('/usr/libexec/java_home')) {
return tryCommand('/usr/libexec/java_home', 'Failed to run: /usr/libexec/java_home')
.then(function(stdout) {
process.env['JAVA_HOME'] = stdout.trim();
});
} else {
// See if we can derive it from javac's location.
// fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
var maybeJavaHome = path.dirname(path.dirname(fs.realpathSync(javacPath)));
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
process.env['JAVA_HOME'] = maybeJavaHome;
} else {
throw new Error('Could not find JAVA_HOME. Try setting the environment variable manually');
}
}
} else if (isWindows) {
// Try to auto-detect java in the default install paths.
var firstJdkDir =
shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
if (firstJdkDir) {
// shelljs always uses / in paths.
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
if (!javacPath) {
process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
}
process.env['JAVA_HOME'] = firstJdkDir;
}
}
}
return true;
} else {
console.error('ERROR : Make sure JAVA_HOME is set, as well as paths to your JDK and JRE for java.');
return false;
}
}).then(function() {
var msg =
'Failed to run "java -version", make sure your java environment is set up\n' +
'including JDK and JRE.\n' +
'Your JAVA_HOME variable is: ' + process.env['JAVA_HOME'];
return tryCommand('java -version', msg)
}).then(function() {
msg = 'Failed to run "javac -version", make sure you have a Java JDK (not just a JRE) installed.';
return tryCommand('javac -version', msg)
});
}
// Returns a promise.
module.exports.check_android = function() {
var valid_target = this.get_target();
var targets = shell.exec('android list targets', {silent:true, async:false});
if(targets.code > 0 && targets.output.match(/command\snot\sfound/)) {
console.error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.');
return false;
} else if(!targets.output.match(valid_target)) {
console.error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest 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.');
return false;
} else {
var cmd = 'android update project -p ' + ROOT + ' -t ' + valid_target + ' 1> /dev/null 2>&1';
var result = shell.exec(cmd, {silent:false, async:true});
if(result.code > 0) {
console.error('Error updating the Cordova library to work with your Android environment.');
return false;
return Q().then(function() {
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = !!forgivingWhichSync('adb');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
if (hasAndroidHome && !androidCmdPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
}
}
return true;
if (androidCmdPath && !hasAndroidHome) {
var parentDir = path.dirname(androidCmdPath);
if (path.basename(parentDir) == 'tools') {
process.env['ANDROID_HOME'] = path.dirname(parentDir);
hasAndroidHome = true;
}
}
if (hasAndroidHome && !adbInPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
}
if (!process.env['ANDROID_HOME']) {
throw new Error('ANDROID_HOME is not set and "android" command not in your PATH. You must fulfill at least one of these conditions.');
}
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
throw new Error('ANDROID_HOME is set to a non-existant path: ' + process.env['ANDROID_HOME']);
}
// Check that the target sdk level is installed.
return module.exports.check_android_target(module.exports.get_target());
});
};
module.exports.check_android_target = function(valid_target) {
var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
return tryCommand('android list targets', msg)
.then(function(output) {
if (!output.match(valid_target)) {
throw new Error('Please install Android target "' + valid_target + '".\n' +
'Hint: Run "android" from your command-line to open the SDK manager.');
}
});
};
// Returns a promise.
module.exports.run = function() {
return Q.all([this.check_java(), this.check_android()]);
}
module.exports.run = function() {
return this.check_ant() && this.check_java && this.check_android();
}

View File

@@ -19,11 +19,169 @@
under the License.
*/
var shell = require('shelljs'),
child_process = require('child_process'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
check_reqs = require('./check_reqs'),
ROOT = path.join(__dirname, '..', '..');
// Returns a promise.
function exec(command, opt_cwd) {
var d = Q.defer();
console.log('Running: ' + command);
child_process.exec(command, { cwd: opt_cwd }, function(err, stdout, stderr) {
stdout && console.log(stdout);
stderr && console.error(stderr);
if (err) d.reject(err);
else d.resolve(stdout);
});
return d.promise;
}
function setShellFatal(value, func) {
var oldVal = shell.config.fatal;
shell.config.fatal = value;
func();
shell.config.fatal = oldVal;
}
function getFrameworkDir(projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
function copyJsAndLibrary(projectPath, shared, projectName) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
shell.cp('-f', path.join(ROOT, 'framework', 'assets', 'www', 'cordova.js'), path.join(projectPath, 'assets', 'www', 'cordova.js'));
// Don't fail if there are no old jars.
setShellFatal(false, function() {
shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function(oldJar) {
console.log("Deleting " + oldJar);
shell.rm('-f', oldJar);
});
// Delete old library project if it existed.
if (shared) {
shell.rm('-rf', nestedCordovaLibPath);
} else {
// Delete only the src, since eclipse can't handle its .project file being deleted.
shell.rm('-rf', path.join(nestedCordovaLibPath, 'src'));
}
});
if (!shared) {
shell.mkdir('-p', nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
// Create an eclipse project file and set the name of it to something unique.
// Without this, you can't import multiple CordovaLib projects into the same workspace.
var eclipseProjectFilePath = path.join(nestedCordovaLibPath, '.project');
if (!fs.existsSync(eclipseProjectFilePath)) {
var data = '<?xml version="1.0" encoding="UTF-8"?><projectDescription><name>' + projectName + '-' + 'CordovaLib</name></projectDescription>';
fs.writeFileSync(eclipseProjectFilePath, data, 'utf8');
}
}
}
function extractSubProjectPaths(data) {
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
var m;
while (m = r.exec(data)) {
ret[m[1]] = 1;
}
return Object.keys(ret);
}
function writeProjectProperties(projectPath, target_api, shared) {
var dstPath = path.join(projectPath, 'project.properties');
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
var data = fs.readFileSync(srcPath, 'utf8');
data = data.replace(/^target=.*/m, 'target=' + target_api);
var subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function(p) {
return !(/^CordovaLib$/m.exec(p) ||
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
/^(\.\.[\\\/])+framework$/m.exec(p)
);
});
subProjects.unshift(shared ? path.relative(projectPath, path.join(ROOT, 'framework')) : 'CordovaLib');
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
if (!/\n$/.exec(data)) {
data += '\n';
}
for (var i = 0; i < subProjects.length; ++i) {
data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
}
fs.writeFileSync(dstPath, data);
}
function copyBuildRules(projectPath) {
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'settings.gradle'), projectPath);
}
function copyScripts(projectPath) {
var srcScriptsDir = path.join(ROOT, 'bin', 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
// Delete old scripts directory if this is an update.
shell.rm('-rf', destScriptsDir);
// Copy in the new ones.
shell.cp('-r', srcScriptsDir, projectPath);
shell.cp('-r', path.join(ROOT, 'bin', 'node_modules'), destScriptsDir);
shell.cp(path.join(ROOT, 'bin', 'check_reqs'), path.join(destScriptsDir, 'check_reqs'));
shell.cp(path.join(ROOT, 'bin', 'lib', 'check_reqs.js'), path.join(projectPath, 'cordova', 'lib', 'check_reqs.js'));
shell.cp(path.join(ROOT, 'bin', 'android_sdk_version'), path.join(destScriptsDir, 'android_sdk_version'));
shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
}
/**
* Test whether a package name is acceptable for use as an android project.
* Returns a promise, fulfilled if the package name is acceptable; rejected
* otherwise.
*/
function validatePackageName(package_name) {
//Make the package conform to Java package types
//Enforce underscore limitation
if (!/^[a-zA-Z]+(\.[a-zA-Z0-9][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Q.reject('Package name must look like: com.company.Name');
}
//Class is a reserved word
if(/\b[Cc]lass\b/.test(package_name)) {
return Q.reject('class is a reserved word');
}
return Q.resolve();
}
/**
* Test whether a project name is acceptable for use as an android class.
* Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise.
*/
function validateProjectName(project_name) {
//Make sure there's something there
if (project_name === '') {
return Q.reject('Project name cannot be empty');
}
//Enforce stupid name error
if (project_name === 'CordovaActivity') {
return Q.reject('Project name cannot be CordovaActivity');
}
//Classes in Java don't begin with numbers
if (/^[0-9]/.test(project_name)) {
return Q.reject('Project name must not begin with a number');
}
return Q.resolve();
}
/**
* $ create [options]
@@ -36,215 +194,135 @@ var shell = require('shelljs'),
* - `package_name`{String} Package name, following reverse-domain style convention.
* - `project_name` {String} Project name.
* - 'project_template_dir' {String} Path to project template (override).
*
* Returns a promise.
*/
module.exports.run = function(project_path, package_name, project_name, project_template_dir) {
var VERSION = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8');
exports.createProject = function(project_path, package_name, project_name, project_template_dir, use_shared_project, use_cli_template) {
var VERSION = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
// Set default values for path, package and name
project_path = typeof project_path !== 'undefined' ? project_path : "CordovaExample";
project_path = path.relative(process.cwd(), project_path);
package_name = typeof package_name !== 'undefined' ? package_name : 'my.cordova.project';
project_name = typeof project_name !== 'undefined' ? project_name : 'CordovaExample';
project_template_dir = typeof project_template_dir !== 'undefined' ?
project_template_dir :
path.join(ROOT, 'bin', 'templates', 'project');
var safe_activity_name = project_name.replace(/\W/, '');
var package_as_path = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(project_path, 'src', package_as_path);
// safe_activity_name is being hardcoded to avoid issues with unicode app name (https://issues.apache.org/jira/browse/CB-6511)
// TODO: provide option to specify activity name via CLI (proposal: https://issues.apache.org/jira/browse/CB-7231)
var safe_activity_name = "CordovaApp";
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
var target_api = check_reqs.get_target();
var strings_path = path.join(project_path, 'res', 'values', 'strings.xml');
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
// Check if project already exists
if(fs.existsSync(project_path)) {
console.error('Project already exists! Delete and recreate');
process.exit(2);
return Q.reject('Project already exists! Delete and recreate');
}
//Make the package conform to Java package types
return validatePackageName(package_name)
.then(function() {
validateProjectName(project_name);
})
// Check that requirements are met and proper targets are installed
if(!check_reqs.run()) {
console.error('Please make sure you meeet the software requirements in order to build an android cordova project');
process.exit(2);
}
.then(function() {
return check_reqs.run();
}).then(function() {
// Log the given values for the project
console.log('Creating Cordova project for the Android platform:');
console.log('\tPath: ' + project_path);
console.log('\tPackage: ' + package_name);
console.log('\tName: ' + project_name);
console.log('\tAndroid target: ' + target_api);
// Log the given values for the project
console.log('Creating Cordova project for the Android platform :');
console.log('\tPath : ' + path.relative(process.cwd(), project_path));
console.log('\tPackage : ' + package_name);
console.log('\tName : ' + project_name);
console.log('\tAndroid target : ' + target_api);
console.log('Copying template files...');
// build from source. distro should have these files
if(!fs.existsSync(path.join(ROOT, 'cordova-' + VERSION + '.jar')) && fs.existsSync(path.join(ROOT, 'framework'))) {
console.log('Building jar and js files...');
// update the cordova-android framework for the desired target
exec('android update project --target ' + target_api + ' --path ' + path.join(ROOT, 'framework'));
setShellFatal(true, function() {
// copy project template
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// compile cordova.js and cordova.jar
var cwd = process.cwd();
process.chdir(path.join(ROOT, 'framework'));
exec('ant jar');
process.chdir(cwd);
}
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(project_path, 'libs'));
// create new android project
var create_cmd = 'android create project --target "'+target_api+'" --path "'+path.relative(process.cwd(), project_path)+'" --package "'+package_name+'" --activity "'+safe_activity_name+'"';
exec(create_cmd);
console.log('Copying template files...');
// copy project template
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
// copy cordova.js, cordova.jar and res/xml
if(fs.existsSync(path.join(ROOT, 'framework'))) {
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
shell.cp(path.join(ROOT, 'framework', 'assets', 'www', 'cordova.js'), path.join(project_path, 'assets', 'www', 'cordova.js'));
shell.cp(path.join(ROOT, 'framework', 'cordova-' + VERSION + '.jar'), path.join(project_path, 'libs', 'cordova-' + VERSION + '.jar'));
} else {
shell.cp('-r', path.join(ROOT, 'xml'), path.join(project_path, 'res'));
shell.cp(path.join(ROOT, 'cordova.js'), path.join(project_path, 'assets', 'www', 'cordova.js'));
shell.cp(path.join(ROOT, 'cordova-' + VERSION + '.jar'), path.join(project_path, 'libs', 'cordova-' + VERSION + '.jar'));
}
// interpolate the activity name and package
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
replaceInFile(activity_path, /__ACTIVITY__/, safe_activity_name);
replaceInFile(activity_path, /__ID__/, package_name);
// interpolate the app name into strings.xml
replaceInFile(strings_path, />Cordova</, '>' + project_name + '<');
shell.cp('-f', path.join(project_template_dir, 'AndroidManifest.xml'), manifest_path);
replaceInFile(manifest_path, /__ACTIVITY__/, safe_activity_name);
replaceInFile(manifest_path, /__PACKAGE__/, package_name);
replaceInFile(manifest_path, /__APILEVEL__/, target_api.split('-')[1]);
var cordova_path = path.join(ROOT, 'bin', 'templates', 'cordova');
// creating cordova folder and copying run/build/log/launch/check_reqs scripts
var lib_path = path.join(cordova_path, 'lib');
shell.mkdir(path.join(project_path, 'cordova'));
shell.mkdir(path.join(project_path, 'cordova', 'lib'));
shell.cp(path.join(cordova_path, 'build'), path.join(project_path, 'cordova', 'build'));
shell.chmod(755, path.join(project_path, 'cordova', 'build'));
shell.cp(path.join(cordova_path, 'clean'), path.join(project_path, 'cordova', 'clean'));
shell.chmod(755, path.join(project_path, 'cordova', 'clean'));
shell.cp(path.join(cordova_path, 'log'), path.join(project_path, 'cordova', 'log'));
shell.chmod(755, path.join(project_path, 'cordova', 'log'));
shell.cp(path.join(cordova_path, 'run'), path.join(project_path, 'cordova', 'run'));
shell.chmod(755, path.join(project_path, 'cordova', 'run'));
shell.cp(path.join(cordova_path, 'version'), path.join(project_path, 'cordova', 'version'));
shell.chmod(755, path.join(project_path, 'cordova', 'version'));
shell.cp(path.join(ROOT, 'bin', 'check_reqs'), path.join(project_path, 'cordova', 'check_reqs'));
shell.chmod(755, path.join(project_path, 'cordova', 'check_reqs'));
shell.cp(path.join(lib_path, 'appinfo.js'), path.join(project_path, 'cordova', 'lib', 'appinfo.js'));
shell.cp(path.join(lib_path, 'build.js'), path.join(project_path, 'cordova', 'lib', 'build.js'));
shell.cp(path.join(ROOT, 'bin', 'lib', 'check_reqs.js'), path.join(project_path, 'cordova', 'lib', 'check_reqs.js'));
shell.cp(path.join(lib_path, 'clean.js'), path.join(project_path, 'cordova', 'lib', 'clean.js'));
shell.cp(path.join(lib_path, 'device.js'), path.join(project_path, 'cordova', 'lib', 'device.js'));
shell.cp(path.join(lib_path, 'emulator.js'), path.join(project_path, 'cordova', 'lib', 'emulator.js'));
shell.cp(path.join(lib_path, 'log.js'), path.join(project_path, 'cordova', 'lib', 'log.js'));
shell.cp(path.join(lib_path, 'run.js'), path.join(project_path, 'cordova', 'lib', 'run.js'));
shell.cp(path.join(lib_path, 'install-device'), path.join(project_path, 'cordova', 'lib', 'install-device'));
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'install-device'));
shell.cp(path.join(lib_path, 'install-emulator'), path.join(project_path, 'cordova', 'lib', 'install-emulator'));
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'install-emulator'));
shell.cp(path.join(lib_path, 'list-devices'), path.join(project_path, 'cordova', 'lib', 'list-devices'));
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'list-devices'));
shell.cp(path.join(lib_path, 'list-emulator-images'), path.join(project_path, 'cordova', 'lib', 'list-emulator-images'));
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'list-emulator-images'));
shell.cp(path.join(lib_path, 'list-started-emulators'), path.join(project_path, 'cordova', 'lib', 'list-started-emulators'));
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'list-started-emulators'));
shell.cp(path.join(lib_path, 'start-emulator'), path.join(project_path, 'cordova', 'lib', 'start-emulator'));
shell.chmod(755, path.join(project_path, 'cordova', 'lib', 'start-emulator'));
// if on windows, copy .bat scripts
// TODO : make these not nessesary, they clutter the scripting folder.
if(process.platform == 'win32' || process.platform == 'win64') {
shell.cp(path.join(cordova_path, 'build.bat'), path.join(project_path, 'cordova', 'build.bat'));
shell.cp(path.join(cordova_path, 'clean.bat'), path.join(project_path, 'cordova', 'clean.bat'));
shell.cp(path.join(cordova_path, 'log.bat'), path.join(project_path, 'cordova', 'log.bat'));
shell.cp(path.join(cordova_path, 'run.bat'), path.join(project_path, 'cordova', 'run.bat'));
shell.cp(path.join(cordova_path, 'version.bat'), path.join(project_path, 'cordova', 'version.bat'));
shell.cp(path.join(ROOT, 'bin', 'check_reqs.bat'), path.join(project_path, 'cordova', 'check_reqs.bat'));
// lib scripts
shell.cp(path.join(lib_path, 'install-device.bat'), path.join(project_path, 'cordova', 'lib', 'install-device.bat'));
shell.cp(path.join(lib_path, 'install-emulator.bat'), path.join(project_path, 'cordova', 'lib', 'install-emulator.bat'));
shell.cp(path.join(lib_path, 'list-devices.bat'), path.join(project_path, 'cordova', 'lib', 'list-devices.bat'));
shell.cp(path.join(lib_path, 'list-emulator-images.bat'), path.join(project_path, 'cordova', 'lib', 'list-emulator-images.bat'));
shell.cp(path.join(lib_path, 'list-started-emulators.bat'), path.join(project_path, 'cordova', 'lib', 'list-started-emulators.bat'));
shell.cp(path.join(lib_path, 'start-emulator.bat'), path.join(project_path, 'cordova', 'lib', 'start-emulator.bat'));
}
// copy node related files
shell.cp(path.join(ROOT, 'bin', 'package.json'), path.join(project_path, 'cordova', 'package.json'));
shell.cp('-r', path.join(ROOT, 'bin', 'node_modules'), path.join(project_path, 'cordova'));
/*
* HELPER FUNCTIONS
*/
function exec(command) {
var result;
try {
result = shell.exec(command, {silent:false, async:false});
} catch(e) {
console.error('Command error on execuation : ' + command);
console.error(e);
process.exit(2);
}
if(result && result.code > 0) {
console.error('Command failed to execute : ' + command);
console.error(result.output);
process.exit(2);
} else {
return result;
}
}
function replaceInFile(filename, regex, replacement) {
write(filename, read(filename).replace(regex, replacement));
}
function read(filename) {
if(fs.existsSync(filename)) {
if(fs.lstatSync(filename).isFile()) {
return fs.readFileSync(filename, 'utf-8');
// Add in the proper eclipse project file.
if (use_cli_template) {
var note = 'To show `assets/www` or `res/xml/config.xml`, go to:\n' +
' Project -> Properties -> Resource -> Resource Filters\n' +
'And delete the exclusion filter.\n';
shell.cp(path.join(project_template_dir, 'eclipse-project-CLI'), path.join(project_path, '.project'));
fs.writeFileSync(path.join(project_path, 'assets', '_where-is-www.txt'), note);
} else {
console.error('Uanble to read directory : ' + filename);
process.exit(1);
shell.cp(path.join(project_template_dir, 'eclipse-project'), path.join(project_path, '.project'));
}
} else {
console.error('Uanble to read file, not found : ' + filename);
process.exit(1);
}
}
function write(filename, content) {
fs.writeFileSync(filename, content, 'utf-8');
}
// copy cordova.js, cordova.jar
copyJsAndLibrary(project_path, use_shared_project, safe_activity_name);
// interpolate the activity name and package
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml'));
shell.sed('-i', /__NAME__/, project_name, path.join(project_path, '.project'));
shell.sed('-i', /__ID__/, package_name, activity_path);
shell.cp('-f', path.join(project_template_dir, 'AndroidManifest.xml'), manifest_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, manifest_path);
shell.sed('-i', /__PACKAGE__/, package_name, manifest_path);
shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path);
copyScripts(project_path);
copyBuildRules(project_path);
});
// Link it to local android install.
writeProjectProperties(project_path, target_api);
}).then(function() {
console.log('Project successfully created.');
});
}
/**
* Usage information.
**/
module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'bin', 'create')) + ' <path_to_new_project> <package_name> <project_name>');
console.log('Make sure the Android SDK tools folder is in your PATH!');
console.log(' <path_to_new_project>: Path to your new Cordova Android project');
console.log(' <package_name>: Package name, following reverse-domain style convention');
console.log(' <project_name>: Project name');
process.exit(0);
// Attribute removed in Cordova 4.4 (CB-5447).
function removeDebuggableFromManifest(projectPath) {
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
shell.sed('-i', /\s*android:debuggable="true"/, '', manifestPath);
}
function extractProjectNameFromManifest(projectPath) {
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
var manifestData = fs.readFileSync(manifestPath, 'utf8');
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
if (!m) {
throw new Error('Could not find activity name in ' + manifestPath);
}
return m[1];
}
// Returns a promise.
exports.updateProject = function(projectPath, shared) {
var newVersion = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
// Check that requirements are met and proper targets are installed
return check_reqs.run()
.then(function() {
var projectName = extractProjectNameFromManifest(projectPath);
var target_api = check_reqs.get_target();
copyJsAndLibrary(projectPath, shared, projectName);
copyScripts(projectPath);
copyBuildRules(projectPath);
removeDebuggableFromManifest(projectPath);
writeProjectProperties(projectPath, target_api, shared);
console.log('Android project is now at version ' + newVersion);
console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
});
};
// For testing
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;

32
bin/lib/simpleargs.js Normal file
View File

@@ -0,0 +1,32 @@
/*
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.
*/
exports.getArgs = function(argv) {
var ret = {};
var posArgs = [];
for (var i = 2, arg; arg = argv[i] || i < argv.length; ++i) {
if (/^--/.exec(arg)) {
ret[arg] = true;
} else {
posArgs.push(arg);
}
}
ret._ = posArgs;
return ret;
};

40
bin/node_modules/q/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,40 @@
For pull requests:
- Be consistent with prevalent style and design decisions.
- Add a Jasmine spec to `specs/q-spec.js`.
- Use `npm test` to avoid regressions.
- Run tests in `q-spec/run.html` in as many supported browsers as you
can find the will to deal with.
- Do not build minified versions; we do this each release.
- If you would be so kind, add a note to `CHANGES.md` in an
appropriate section:
- `Next Major Version` if it introduces backward incompatibilities
to code in the wild using documented features.
- `Next Minor Version` if it adds a new feature.
- `Next Patch Version` if it fixes a bug.
For releases:
- Run `npm test`.
- Run tests in `q-spec/run.html` in a representative sample of every
browser under the sun.
- Run `npm run cover` and make sure you're happy with the results.
- Run `npm run minify` and be sure to commit the resulting `q.min.js`.
- Note the Gzipped size output by the previous command, and update
`README.md` if it has changed to 1 significant digit.
- Stash any local changes.
- Update `CHANGES.md` to reflect all changes in the differences
between `HEAD` and the previous tagged version. Give credit where
credit is due.
- Update `README.md` to address all new, non-experimental features.
- Update the API reference on the Wiki to reflect all non-experimental
features.
- Use `npm version major|minor|patch` to update `package.json`,
commit, and tag the new version.
- Use `npm publish` to send up a new release.
- Send an email to the q-continuum mailing list announcing the new
release and the notes from the change log. This helps folks
maintaining other package ecosystems.

19
bin/node_modules/q/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright 20092012 Kristopher Michael Kowal. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

813
bin/node_modules/q/README.md generated vendored Normal file
View File

@@ -0,0 +1,813 @@
[![Build Status](https://secure.travis-ci.org/kriskowal/q.png?branch=master)](http://travis-ci.org/kriskowal/q)
<a href="http://promises-aplus.github.com/promises-spec">
<img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png"
align="right" alt="Promises/A+ logo" />
</a>
If a function cannot return a value or throw an exception without
blocking, it can return a promise instead. A promise is an object
that represents the return value or the thrown exception that the
function may eventually provide. A promise can also be used as a
proxy for a [remote object][Q-Connection] to overcome latency.
[Q-Connection]: https://github.com/kriskowal/q-connection
On the first pass, promises can mitigate the “[Pyramid of
Doom][POD]”: the situation where code marches to the right faster
than it marches forward.
[POD]: http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/
```javascript
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
```
With a promise library, you can flatten the pyramid.
```javascript
Q.fcall(promisedStep1)
.then(promisedStep2)
.then(promisedStep3)
.then(promisedStep4)
.then(function (value4) {
// Do something with value4
})
.catch(function (error) {
// Handle any error from all above steps
})
.done();
```
With this approach, you also get implicit error propagation, just like `try`,
`catch`, and `finally`. An error in `promisedStep1` will flow all the way to
the `catch` function, where its caught and handled. (Here `promisedStepN` is
a version of `stepN` that returns a promise.)
The callback approach is called an “inversion of control”.
A function that accepts a callback instead of a return value
is saying, “Dont call me, Ill call you.”. Promises
[un-invert][IOC] the inversion, cleanly separating the input
arguments from control flow arguments. This simplifies the
use and creation of APIs, particularly variadic,
rest and spread arguments.
[IOC]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript
## Getting Started
The Q module can be loaded as:
- A ``<script>`` tag (creating a ``Q`` global variable): ~2.5 KB minified and
gzipped.
- A Node.js and CommonJS module, available in [npm](https://npmjs.org/) as
the [q](https://npmjs.org/package/q) package
- An AMD module
- A [component](https://github.com/component/component) as ``microjs/q``
- Using [bower](http://bower.io/) as ``q``
- Using [NuGet](http://nuget.org/) as [Q](https://nuget.org/packages/q)
Q can exchange promises with jQuery, Dojo, When.js, WinJS, and more.
## Resources
Our [wiki][] contains a number of useful resources, including:
- A method-by-method [Q API reference][reference].
- A growing [examples gallery][examples], showing how Q can be used to make
everything better. From XHR to database access to accessing the Flickr API,
Q is there for you.
- There are many libraries that produce and consume Q promises for everything
from file system/database access or RPC to templating. For a list of some of
the more popular ones, see [Libraries][].
- If you want materials that introduce the promise concept generally, and the
below tutorial isn't doing it for you, check out our collection of
[presentations, blog posts, and podcasts][resources].
- A guide for those [coming from jQuery's `$.Deferred`][jquery].
We'd also love to have you join the Q-Continuum [mailing list][].
[wiki]: https://github.com/kriskowal/q/wiki
[reference]: https://github.com/kriskowal/q/wiki/API-Reference
[examples]: https://github.com/kriskowal/q/wiki/Examples-Gallery
[Libraries]: https://github.com/kriskowal/q/wiki/Libraries
[resources]: https://github.com/kriskowal/q/wiki/General-Promise-Resources
[jquery]: https://github.com/kriskowal/q/wiki/Coming-from-jQuery
[mailing list]: https://groups.google.com/forum/#!forum/q-continuum
## Tutorial
Promises have a ``then`` method, which you can use to get the eventual
return value (fulfillment) or thrown exception (rejection).
```javascript
promiseMeSomething()
.then(function (value) {
}, function (reason) {
});
```
If ``promiseMeSomething`` returns a promise that gets fulfilled later
with a return value, the first function (the fulfillment handler) will be
called with the value. However, if the ``promiseMeSomething`` function
gets rejected later by a thrown exception, the second function (the
rejection handler) will be called with the exception.
Note that resolution of a promise is always asynchronous: that is, the
fulfillment or rejection handler will always be called in the next turn of the
event loop (i.e. `process.nextTick` in Node). This gives you a nice
guarantee when mentally tracing the flow of your code, namely that
``then`` will always return before either handler is executed.
In this tutorial, we begin with how to consume and work with promises. We'll
talk about how to create them, and thus create functions like
`promiseMeSomething` that return promises, [below](#the-beginning).
### Propagation
The ``then`` method returns a promise, which in this example, Im
assigning to ``outputPromise``.
```javascript
var outputPromise = getInputPromise()
.then(function (input) {
}, function (reason) {
});
```
The ``outputPromise`` variable becomes a new promise for the return
value of either handler. Since a function can only either return a
value or throw an exception, only one handler will ever be called and it
will be responsible for resolving ``outputPromise``.
- If you return a value in a handler, ``outputPromise`` will get
fulfilled.
- If you throw an exception in a handler, ``outputPromise`` will get
rejected.
- If you return a **promise** in a handler, ``outputPromise`` will
“become” that promise. Being able to become a new promise is useful
for managing delays, combining results, or recovering from errors.
If the ``getInputPromise()`` promise gets rejected and you omit the
rejection handler, the **error** will go to ``outputPromise``:
```javascript
var outputPromise = getInputPromise()
.then(function (value) {
});
```
If the input promise gets fulfilled and you omit the fulfillment handler, the
**value** will go to ``outputPromise``:
```javascript
var outputPromise = getInputPromise()
.then(null, function (error) {
});
```
Q promises provide a ``fail`` shorthand for ``then`` when you are only
interested in handling the error:
```javascript
var outputPromise = getInputPromise()
.fail(function (error) {
});
```
If you are writing JavaScript for modern engines only or using
CoffeeScript, you may use `catch` instead of `fail`.
Promises also have a ``fin`` function that is like a ``finally`` clause.
The final handler gets called, with no arguments, when the promise
returned by ``getInputPromise()`` either returns a value or throws an
error. The value returned or error thrown by ``getInputPromise()``
passes directly to ``outputPromise`` unless the final handler fails, and
may be delayed if the final handler returns a promise.
```javascript
var outputPromise = getInputPromise()
.fin(function () {
// close files, database connections, stop servers, conclude tests
});
```
- If the handler returns a value, the value is ignored
- If the handler throws an error, the error passes to ``outputPromise``
- If the handler returns a promise, ``outputPromise`` gets postponed. The
eventual value or error has the same effect as an immediate return
value or thrown error: a value would be ignored, an error would be
forwarded.
If you are writing JavaScript for modern engines only or using
CoffeeScript, you may use `finally` instead of `fin`.
### Chaining
There are two ways to chain promises. You can chain promises either
inside or outside handlers. The next two examples are equivalent.
```javascript
return getUsername()
.then(function (username) {
return getUser(username)
.then(function (user) {
// if we get here without an error,
// the value returned here
// or the exception thrown here
// resolves the promise returned
// by the first line
})
});
```
```javascript
return getUsername()
.then(function (username) {
return getUser(username);
})
.then(function (user) {
// if we get here without an error,
// the value returned here
// or the exception thrown here
// resolves the promise returned
// by the first line
});
```
The only difference is nesting. Its useful to nest handlers if you
need to capture multiple input values in your closure.
```javascript
function authenticate() {
return getUsername()
.then(function (username) {
return getUser(username);
})
// chained because we will not need the user name in the next event
.then(function (user) {
return getPassword()
// nested because we need both user and password next
.then(function (password) {
if (user.passwordHash !== hash(password)) {
throw new Error("Can't authenticate");
}
});
});
}
```
### Combination
You can turn an array of promises into a promise for the whole,
fulfilled array using ``all``.
```javascript
return Q.all([
eventualAdd(2, 2),
eventualAdd(10, 20)
]);
```
If you have a promise for an array, you can use ``spread`` as a
replacement for ``then``. The ``spread`` function “spreads” the
values over the arguments of the fulfillment handler. The rejection handler
will get called at the first sign of failure. That is, whichever of
the recived promises fails first gets handled by the rejection handler.
```javascript
function eventualAdd(a, b) {
return Q.spread([a, b], function (a, b) {
return a + b;
})
}
```
But ``spread`` calls ``all`` initially, so you can skip it in chains.
```javascript
return getUsername()
.then(function (username) {
return [username, getUser(username)];
})
.spread(function (username, user) {
});
```
The ``all`` function returns a promise for an array of values. When this
promise is fulfilled, the array contains the fulfillment values of the original
promises, in the same order as those promises. If one of the given promises
is rejected, the returned promise is immediately rejected, not waiting for the
rest of the batch. If you want to wait for all of the promises to either be
fulfilled or rejected, you can use ``allSettled``.
```javascript
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
```
### Sequences
If you have a number of promise-producing functions that need
to be run sequentially, you can of course do so manually:
```javascript
return foo(initialVal).then(bar).then(baz).then(qux);
```
However, if you want to run a dynamically constructed sequence of
functions, you'll want something like this:
```javascript
var funcs = [foo, bar, baz, qux];
var result = Q(initialVal);
funcs.forEach(function (f) {
result = result.then(f);
});
return result;
```
You can make this slightly more compact using `reduce`:
```javascript
return funcs.reduce(function (soFar, f) {
return soFar.then(f);
}, Q(initialVal));
```
Or, you could use th ultra-compact version:
```javascript
return funcs.reduce(Q.when, Q());
```
### Handling Errors
One sometimes-unintuive aspect of promises is that if you throw an
exception in the fulfillment handler, it will not be be caught by the error
handler.
```javascript
return foo()
.then(function (value) {
throw new Error("Can't bar.");
}, function (error) {
// We only get here if "foo" fails
});
```
To see why this is, consider the parallel between promises and
``try``/``catch``. We are ``try``-ing to execute ``foo()``: the error
handler represents a ``catch`` for ``foo()``, while the fulfillment handler
represents code that happens *after* the ``try``/``catch`` block.
That code then needs its own ``try``/``catch`` block.
In terms of promises, this means chaining your rejection handler:
```javascript
return foo()
.then(function (value) {
throw new Error("Can't bar.");
})
.fail(function (error) {
// We get here with either foo's error or bar's error
});
```
### Progress Notification
It's possible for promises to report their progress, e.g. for tasks that take a
long time like a file upload. Not all promises will implement progress
notifications, but for those that do, you can consume the progress values using
a third parameter to ``then``:
```javascript
return uploadFile()
.then(function () {
// Success uploading the file
}, function (err) {
// There was an error, and we get the reason for error
}, function (progress) {
// We get notified of the upload's progress as it is executed
});
```
Like `fail`, Q also provides a shorthand for progress callbacks
called `progress`:
```javascript
return uploadFile().progress(function (progress) {
// We get notified of the upload's progress
});
```
### The End
When you get to the end of a chain of promises, you should either
return the last promise or end the chain. Since handlers catch
errors, its an unfortunate pattern that the exceptions can go
unobserved.
So, either return it,
```javascript
return foo()
.then(function () {
return "bar";
});
```
Or, end it.
```javascript
foo()
.then(function () {
return "bar";
})
.done();
```
Ending a promise chain makes sure that, if an error doesnt get
handled before the end, it will get rethrown and reported.
This is a stopgap. We are exploring ways to make unhandled errors
visible without any explicit handling.
### The Beginning
Everything above assumes you get a promise from somewhere else. This
is the common case. Every once in a while, you will need to create a
promise from scratch.
#### Using ``Q.fcall``
You can create a promise from a value using ``Q.fcall``. This returns a
promise for 10.
```javascript
return Q.fcall(function () {
return 10;
});
```
You can also use ``fcall`` to get a promise for an exception.
```javascript
return Q.fcall(function () {
throw new Error("Can't do it");
});
```
As the name implies, ``fcall`` can call functions, or even promised
functions. This uses the ``eventualAdd`` function above to add two
numbers.
```javascript
return Q.fcall(eventualAdd, 2, 2);
```
#### Using Deferreds
If you have to interface with asynchronous functions that are callback-based
instead of promise-based, Q provides a few shortcuts (like ``Q.nfcall`` and
friends). But much of the time, the solution will be to use *deferreds*.
```javascript
var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (error, text) {
if (error) {
deferred.reject(new Error(error));
} else {
deferred.resolve(text);
}
});
return deferred.promise;
```
Note that a deferred can be resolved with a value or a promise. The
``reject`` function is a shorthand for resolving with a rejected
promise.
```javascript
// this:
deferred.reject(new Error("Can't do it"));
// is shorthand for:
var rejection = Q.fcall(function () {
throw new Error("Can't do it");
});
deferred.resolve(rejection);
```
This is a simplified implementation of ``Q.delay``.
```javascript
function delay(ms) {
var deferred = Q.defer();
setTimeout(deferred.resolve, ms);
return deferred.promise;
}
```
This is a simplified implementation of ``Q.timeout``
```javascript
function timeout(promise, ms) {
var deferred = Q.defer();
Q.when(promise, deferred.resolve);
delay(ms).then(function () {
deferred.reject(new Error("Timed out"));
});
return deferred.promise;
}
```
Finally, you can send a progress notification to the promise with
``deferred.notify``.
For illustration, this is a wrapper for XML HTTP requests in the browser. Note
that a more [thorough][XHR] implementation would be in order in practice.
[XHR]: https://github.com/montagejs/mr/blob/71e8df99bb4f0584985accd6f2801ef3015b9763/browser.js#L29-L73
```javascript
function requestOkText(url) {
var request = new XMLHttpRequest();
var deferred = Q.defer();
request.open("GET", url, true);
request.onload = onload;
request.onerror = onerror;
request.onprogress = onprogress;
request.send();
function onload() {
if (request.status === 200) {
deferred.resolve(request.responseText);
} else {
deferred.reject(new Error("Status code was " + request.status));
}
}
function onerror() {
deferred.reject(new Error("Can't XHR " + JSON.stringify(url)));
}
function onprogress(event) {
deferred.notify(event.loaded / event.total);
}
return deferred.promise;
}
```
Below is an example of how to use this ``requestOkText`` function:
```javascript
requestOkText("http://localhost:3000")
.then(function (responseText) {
// If the HTTP response returns 200 OK, log the response text.
console.log(responseText);
}, function (error) {
// If there's an error or a non-200 status code, log the error.
console.error(error);
}, function (progress) {
// Log the progress as it comes in.
console.log("Request progress: " + Math.round(progress * 100) + "%");
});
```
### The Middle
If you are using a function that may return a promise, but just might
return a value if it doesnt need to defer, you can use the “static”
methods of the Q library.
The ``when`` function is the static equivalent for ``then``.
```javascript
return Q.when(valueOrPromise, function (value) {
}, function (error) {
});
```
All of the other methods on a promise have static analogs with the
same name.
The following are equivalent:
```javascript
return Q.all([a, b]);
```
```javascript
return Q.fcall(function () {
return [a, b];
})
.all();
```
When working with promises provided by other libraries, you should
convert it to a Q promise. Not all promise libraries make the same
guarantees as Q and certainly dont provide all of the same methods.
Most libraries only provide a partially functional ``then`` method.
This thankfully is all we need to turn them into vibrant Q promises.
```javascript
return Q($.ajax(...))
.then(function () {
});
```
If there is any chance that the promise you receive is not a Q promise
as provided by your library, you should wrap it using a Q function.
You can even use ``Q.invoke`` as a shorthand.
```javascript
return Q.invoke($, 'ajax', ...)
.then(function () {
});
```
### Over the Wire
A promise can serve as a proxy for another object, even a remote
object. There are methods that allow you to optimistically manipulate
properties or call functions. All of these interactions return
promises, so they can be chained.
```
direct manipulation using a promise as a proxy
-------------------------- -------------------------------
value.foo promise.get("foo")
value.foo = value promise.put("foo", value)
delete value.foo promise.del("foo")
value.foo(...args) promise.post("foo", [args])
value.foo(...args) promise.invoke("foo", ...args)
value(...args) promise.fapply([args])
value(...args) promise.fcall(...args)
```
If the promise is a proxy for a remote object, you can shave
round-trips by using these functions instead of ``then``. To take
advantage of promises for remote objects, check out [Q-Connection][].
[Q-Connection]: https://github.com/kriskowal/q-connection
Even in the case of non-remote objects, these methods can be used as
shorthand for particularly-simple fulfillment handlers. For example, you
can replace
```javascript
return Q.fcall(function () {
return [{ foo: "bar" }, { foo: "baz" }];
})
.then(function (value) {
return value[0].foo;
});
```
with
```javascript
return Q.fcall(function () {
return [{ foo: "bar" }, { foo: "baz" }];
})
.get(0)
.get("foo");
```
### Adapting Node
If you're working with functions that make use of the Node.js callback pattern,
where callbacks are in the form of `function(err, result)`, Q provides a few
useful utility functions for converting between them. The most straightforward
are probably `Q.nfcall` and `Q.nfapply` ("Node function call/apply") for calling
Node.js-style functions and getting back a promise:
```javascript
return Q.nfcall(FS.readFile, "foo.txt", "utf-8");
return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]);
```
If you are working with methods, instead of simple functions, you can easily
run in to the usual problems where passing a method to another function—like
`Q.nfcall`—"un-binds" the method from its owner. To avoid this, you can either
use `Function.prototype.bind` or some nice shortcut methods we provide:
```javascript
return Q.ninvoke(redisClient, "get", "user:1:id");
return Q.npost(redisClient, "get", ["user:1:id"]);
```
You can also create reusable wrappers with `Q.denodeify` or `Q.nbind`:
```javascript
var readFile = Q.denodeify(FS.readFile);
return readFile("foo.txt", "utf-8");
var redisClientGet = Q.nbind(redisClient.get, redisClient);
return redisClientGet("user:1:id");
```
Finally, if you're working with raw deferred objects, there is a
`makeNodeResolver` method on deferreds that can be handy:
```javascript
var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver());
return deferred.promise;
```
### Long Stack Traces
Q comes with optional support for “long stack traces,” wherein the `stack`
property of `Error` rejection reasons is rewritten to be traced along
asynchronous jumps instead of stopping at the most recent one. As an example:
```js
function theDepthsOfMyProgram() {
Q.delay(100).done(function explode() {
throw new Error("boo!");
});
}
theDepthsOfMyProgram();
```
usually would give a rather unhelpful stack trace looking something like
```
Error: boo!
at explode (/path/to/test.js:3:11)
at _fulfilled (/path/to/test.js:q:54)
at resolvedValue.promiseDispatch.done (/path/to/q.js:823:30)
at makePromise.promise.promiseDispatch (/path/to/q.js:496:13)
at pending (/path/to/q.js:397:39)
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
```
But, if you turn this feature on by setting
```js
Q.longStackSupport = true;
```
then the above code gives a nice stack trace to the tune of
```
Error: boo!
at explode (/path/to/test.js:3:11)
From previous event:
at theDepthsOfMyProgram (/path/to/test.js:2:16)
at Object.<anonymous> (/path/to/test.js:7:1)
```
Note how you can see the the function that triggered the async operation in the
stack trace! This is very helpful for debugging, as otherwise you end up getting
only the first line, plus a bunch of Q internals, with no sign of where the
operation started.
This feature does come with somewhat-serious performance and memory overhead,
however. If you're working with lots of promises, or trying to scale a server
to many users, you should probably keep it off. But in development, go for it!
## Tests
You can view the results of the Q test suite [in your browser][tests]!
[tests]: https://rawgithub.com/kriskowal/q/master/spec/q-spec.html
## License
Copyright 20092013 Kristopher Michael Kowal
MIT License (enclosed)

71
bin/node_modules/q/benchmark/compare-with-callbacks.js generated vendored Normal file
View File

@@ -0,0 +1,71 @@
"use strict";
var Q = require("../q");
var fs = require("fs");
suite("A single simple async operation", function () {
bench("with an immediately-fulfilled promise", function (done) {
Q().then(done);
});
bench("with direct setImmediate usage", function (done) {
setImmediate(done);
});
bench("with direct setTimeout(…, 0)", function (done) {
setTimeout(done, 0);
});
});
suite("A fs.readFile", function () {
var denodeified = Q.denodeify(fs.readFile);
set("iterations", 1000);
set("delay", 1000);
bench("directly, with callbacks", function (done) {
fs.readFile(__filename, done);
});
bench("with Q.nfcall", function (done) {
Q.nfcall(fs.readFile, __filename).then(done);
});
bench("with a Q.denodeify'ed version", function (done) {
denodeified(__filename).then(done);
});
bench("with manual usage of deferred.makeNodeResolver", function (done) {
var deferred = Q.defer();
fs.readFile(__filename, deferred.makeNodeResolver());
deferred.promise.then(done);
});
});
suite("1000 operations in parallel", function () {
function makeCounter(desiredCount, ultimateCallback) {
var soFar = 0;
return function () {
if (++soFar === desiredCount) {
ultimateCallback();
}
};
}
var numberOfOps = 1000;
bench("with immediately-fulfilled promises", function (done) {
var counter = makeCounter(numberOfOps, done);
for (var i = 0; i < numberOfOps; ++i) {
Q().then(counter);
}
});
bench("with direct setImmediate usage", function (done) {
var counter = makeCounter(numberOfOps, done);
for (var i = 0; i < numberOfOps; ++i) {
setImmediate(counter);
}
});
});

36
bin/node_modules/q/benchmark/scenarios.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
"use strict";
var Q = require("../q");
suite("Chaining", function () {
var numberToChain = 1000;
bench("Chaining many already-fulfilled promises together", function (done) {
var currentPromise = Q();
for (var i = 0; i < numberToChain; ++i) {
currentPromise = currentPromise.then(function () {
return Q();
});
}
currentPromise.then(done);
});
bench("Chaining and then fulfilling the end of the chain", function (done) {
var deferred = Q.defer();
var currentPromise = deferred.promise;
for (var i = 0; i < numberToChain; ++i) {
(function () {
var promiseToReturn = currentPromise;
currentPromise = Q().then(function () {
return promiseToReturn;
});
}());
}
currentPromise.then(done);
deferred.resolve();
});
});

93
bin/node_modules/q/package.json generated vendored Normal file

File diff suppressed because one or more lines are too long

1937
bin/node_modules/q/q.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

35
bin/node_modules/q/queue.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
var Q = require("./q");
module.exports = Queue;
function Queue() {
var ends = Q.defer();
var closed = Q.defer();
return {
put: function (value) {
var next = Q.defer();
ends.resolve({
head: value,
tail: next.promise
});
ends.resolve = next.resolve;
},
get: function () {
var result = ends.promise.get("head");
ends.promise = ends.promise.get("tail");
return result.fail(function (error) {
closed.resolve(error);
throw error;
});
},
closed: closed.promise,
close: function (error) {
error = error || new Error("Can't get value from closed queue");
var end = {head: Q.reject(error)};
end.tail = end;
ends.resolve(end);
return closed.promise;
}
};
}

7
bin/node_modules/shelljs/.jshintrc generated vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"loopfunc": true,
"sub": true,
"undef": true,
"unused": true,
"node": true
}

View File

@@ -1 +1,2 @@
node_modules/
test/
tmp/

View File

@@ -1,5 +1,5 @@
language: node_js
node_js:
- 0.6
- 0.8
- "0.8"
- "0.10"
- "0.11"

91
bin/node_modules/shelljs/README.md generated vendored
View File

@@ -150,9 +150,11 @@ All commands run synchronously, unless otherwise stated.
### cd('dir')
Changes to directory `dir` for the duration of the script
### pwd()
Returns the current directory.
### ls([options ,] path [,path ...])
### ls([options ,] path_array)
Available options:
@@ -170,6 +172,7 @@ ls('-R', ['/users/me', '/tmp']); // same as above
Returns array of files in the given path, or in current directory if no path provided.
### find(path [,path ...])
### find(path_array)
Examples:
@@ -185,6 +188,7 @@ Returns array of all files (however deep) in the given paths.
The main difference from `ls('-R', path)` is that the resulting file names
include the base directories, e.g. `lib/resources/file1` instead of just `file1`.
### cp([options ,] source [,source ...], dest)
### cp([options ,] source_array, dest)
Available options:
@@ -202,6 +206,7 @@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above
Copies files. The wildcard `*` is accepted.
### rm([options ,] file [, file ...])
### rm([options ,] file_array)
Available options:
@@ -219,6 +224,7 @@ rm(['some_file.txt', 'another_file.txt']); // same as above
Removes files. The wildcard `*` is accepted.
### mv(source [, source ...], dest')
### mv(source_array, dest')
Available options:
@@ -235,6 +241,7 @@ mv(['file1', 'file2'], 'dir/'); // same as above
Moves files. The wildcard `*` is accepted.
### mkdir([options ,] dir [, dir ...])
### mkdir([options ,] dir_array)
Available options:
@@ -250,6 +257,7 @@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above
Creates directories.
### test(expression)
Available expression primaries:
@@ -271,6 +279,7 @@ if (!test('-f', path)) continue; // skip if it's a regular file
Evaluates expression using the available primaries and returns corresponding value.
### cat(file [, file ...])
### cat(file_array)
@@ -286,6 +295,7 @@ Returns a string containing the given file, or a concatenated string
containing the files if more than one file is given (a new line character is
introduced between each file). Wildcard `*` accepted.
### 'string'.to(file)
Examples:
@@ -297,6 +307,19 @@ cat('input.txt').to('output.txt');
Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as
those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_
### 'string'.toEnd(file)
Examples:
```javascript
cat('input.txt').toEnd('output.txt');
```
Analogous to the redirect-and-append operator `>>` in Unix, but works with JavaScript strings (such as
those returned by `cat`, `grep`, etc).
### sed([options ,] search_regex, replace_str, file)
Available options:
@@ -312,6 +335,7 @@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js');
Reads an input string from `file` and performs a JavaScript `replace()` on the input
using the given search regex and replacement string. Returns the new string after replacement.
### grep([options ,] regex_filter, file [, file ...])
### grep([options ,] regex_filter, file_array)
Available options:
@@ -328,6 +352,7 @@ grep('GLOBAL_VARIABLE', '*.js');
Reads input string from given files and returns a string containing all lines of the
file that match the given `regex_filter`. Wildcard `*` accepted.
### which(command)
Examples:
@@ -339,6 +364,7 @@ var nodeExec = which('node');
Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions.
Returns string containing the absolute path to the command.
### echo(string [,string ...])
Examples:
@@ -351,20 +377,6 @@ var str = echo('hello world');
Prints string to stdout, and returns string with additional utility methods
like `.to()`.
### dirs([options | '+N' | '-N'])
Available options:
+ `-c`: Clears the directory stack by deleting all of the elements.
Arguments:
+ `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero.
+ `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero.
Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified.
See also: pushd, popd
### pushd([options,] [dir | '-N' | '+N'])
@@ -411,6 +423,22 @@ echo(process.cwd()); // '/usr'
When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack.
### dirs([options | '+N' | '-N'])
Available options:
+ `-c`: Clears the directory stack by deleting all of the elements.
Arguments:
+ `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero.
+ `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero.
Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified.
See also: pushd, popd
### exit(code)
Exits the current process with the given exit code.
@@ -448,6 +476,7 @@ the `callback` gets the arguments `(code, output)`.
the current synchronous implementation uses a lot of CPU. This should be getting
fixed soon.
### chmod(octal_mode || octal_string, file)
### chmod(symbolic_mode, file)
@@ -461,7 +490,7 @@ Examples:
```javascript
chmod(755, '/Users/brandon');
chmod('755', '/Users/brandon'); // same as above
chmod('755', '/Users/brandon'); // same as above
chmod('u+x', '/Users/brandon');
```
@@ -474,6 +503,27 @@ Notable exceptions:
given to the umask.
+ There is no "quiet" option since default behavior is to run silent.
## Non-Unix commands
### tempdir()
Examples:
```javascript
var tmp = tempdir(); // "/tmp" for most *nix platforms
```
Searches and returns string containing a writeable, platform-dependent temporary directory.
Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir).
### error()
Tests if error occurred in the last command. Returns `null` if no error occurred,
otherwise returns string explaining the error
## Configuration
@@ -500,14 +550,3 @@ cp('this_file_does_not_exist', '/dev/null'); // dies here
```
If `true` the script will die on errors. Default is `false`.
## Non-Unix commands
### tempdir()
Searches and returns string containing a writeable, platform-dependent temporary directory.
Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir).
### error()
Tests if error occurred in the last command. Returns `null` if no error occurred,
otherwise returns string explaining the error

View File

@@ -1,4 +0,0 @@
{
"loopfunc": true,
"sub": true
}

19
bin/node_modules/shelljs/make.js generated vendored
View File

@@ -1,6 +1,6 @@
require('./global');
config.fatal = true;
global.config.fatal = true;
global.target = {};
// This ensures we only execute the script targets after the entire script has
@@ -11,38 +11,37 @@ setTimeout(function() {
if (args.length === 1 && args[0] === '--help') {
console.log('Available targets:');
for (t in target)
for (t in global.target)
console.log(' ' + t);
return;
}
// Wrap targets to prevent duplicate execution
for (t in target) {
for (t in global.target) {
(function(t, oldTarget){
// Wrap it
target[t] = function(force) {
global.target[t] = function(force) {
if (oldTarget.done && !force)
return;
oldTarget.done = true;
return oldTarget.apply(oldTarget, arguments);
};
})(t, target[t]);
})(t, global.target[t]);
}
// Execute desired targets
if (args.length > 0) {
args.forEach(function(arg) {
if (arg in target)
target[arg]();
if (arg in global.target)
global.target[arg]();
else {
console.log('no such target: ' + arg);
exit(1);
}
});
} else if ('all' in target) {
target.all();
} else if ('all' in global.target) {
global.target.all();
}
}, 0);

File diff suppressed because one or more lines are too long

View File

@@ -7,6 +7,12 @@ cd(__dirname + '/..');
// Extract docs from shell.js
var docs = grep('//@', 'shell.js');
docs = docs.replace(/\/\/\@include (.+)/g, function(match, path) {
var file = path.match('.js$') ? path : path+'.js';
return grep('//@', file);
});
// Remove '//@'
docs = docs.replace(/\/\/\@ ?/g, '');
// Append docs to README

View File

@@ -16,7 +16,7 @@ if (!test('-f', JSHINT_BIN)) {
exit(1);
}
if (exec(JSHINT_BIN + ' --config jshint.json *.js test/*.js').code !== 0) {
if (exec(JSHINT_BIN + ' *.js test/*.js').code !== 0) {
failed = true;
echo('*** JSHINT FAILED! (return code != 0)');
echo();

1920
bin/node_modules/shelljs/shell.js generated vendored

File diff suppressed because it is too large Load Diff

43
bin/node_modules/shelljs/src/cat.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
var common = require('./common');
var fs = require('fs');
//@
//@ ### cat(file [, file ...])
//@ ### cat(file_array)
//@
//@ Examples:
//@
//@ ```javascript
//@ var str = cat('file*.txt');
//@ var str = cat('file1', 'file2');
//@ var str = cat(['file1', 'file2']); // same as above
//@ ```
//@
//@ Returns a string containing the given file, or a concatenated string
//@ containing the files if more than one file is given (a new line character is
//@ introduced between each file). Wildcard `*` accepted.
function _cat(options, files) {
var cat = '';
if (!files)
common.error('no paths given');
if (typeof files === 'string')
files = [].slice.call(arguments, 1);
// if it's array leave it as it is
files = common.expand(files);
files.forEach(function(file) {
if (!fs.existsSync(file))
common.error('no such file or directory: ' + file);
cat += fs.readFileSync(file, 'utf8') + '\n';
});
if (cat[cat.length-1] === '\n')
cat = cat.substring(0, cat.length-1);
return common.ShellString(cat);
}
module.exports = _cat;

19
bin/node_modules/shelljs/src/cd.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
var fs = require('fs');
var common = require('./common');
//@
//@ ### cd('dir')
//@ Changes to directory `dir` for the duration of the script
function _cd(options, dir) {
if (!dir)
common.error('directory not specified');
if (!fs.existsSync(dir))
common.error('no such file or directory: ' + dir);
if (!fs.statSync(dir).isDirectory())
common.error('not a directory: ' + dir);
process.chdir(dir);
}
module.exports = _cd;

208
bin/node_modules/shelljs/src/chmod.js generated vendored Normal file
View File

@@ -0,0 +1,208 @@
var common = require('./common');
var fs = require('fs');
var path = require('path');
var PERMS = (function (base) {
return {
OTHER_EXEC : base.EXEC,
OTHER_WRITE : base.WRITE,
OTHER_READ : base.READ,
GROUP_EXEC : base.EXEC << 3,
GROUP_WRITE : base.WRITE << 3,
GROUP_READ : base.READ << 3,
OWNER_EXEC : base.EXEC << 6,
OWNER_WRITE : base.WRITE << 6,
OWNER_READ : base.READ << 6,
// Literal octal numbers are apparently not allowed in "strict" javascript. Using parseInt is
// the preferred way, else a jshint warning is thrown.
STICKY : parseInt('01000', 8),
SETGID : parseInt('02000', 8),
SETUID : parseInt('04000', 8),
TYPE_MASK : parseInt('0770000', 8)
};
})({
EXEC : 1,
WRITE : 2,
READ : 4
});
//@
//@ ### chmod(octal_mode || octal_string, file)
//@ ### chmod(symbolic_mode, file)
//@
//@ Available options:
//@
//@ + `-v`: output a diagnostic for every file processed//@
//@ + `-c`: like verbose but report only when a change is made//@
//@ + `-R`: change files and directories recursively//@
//@
//@ Examples:
//@
//@ ```javascript
//@ chmod(755, '/Users/brandon');
//@ chmod('755', '/Users/brandon'); // same as above
//@ chmod('u+x', '/Users/brandon');
//@ ```
//@
//@ Alters the permissions of a file or directory by either specifying the
//@ absolute permissions in octal form or expressing the changes in symbols.
//@ This command tries to mimic the POSIX behavior as much as possible.
//@ Notable exceptions:
//@
//@ + In symbolic modes, 'a-r' and '-r' are identical. No consideration is
//@ given to the umask.
//@ + There is no "quiet" option since default behavior is to run silent.
function _chmod(options, mode, filePattern) {
if (!filePattern) {
if (options.length > 0 && options.charAt(0) === '-') {
// Special case where the specified file permissions started with - to subtract perms, which
// get picked up by the option parser as command flags.
// If we are down by one argument and options starts with -, shift everything over.
filePattern = mode;
mode = options;
options = '';
}
else {
common.error('You must specify a file.');
}
}
options = common.parseOptions(options, {
'R': 'recursive',
'c': 'changes',
'v': 'verbose'
});
if (typeof filePattern === 'string') {
filePattern = [ filePattern ];
}
var files;
if (options.recursive) {
files = [];
common.expand(filePattern).forEach(function addFile(expandedFile) {
var stat = fs.lstatSync(expandedFile);
if (!stat.isSymbolicLink()) {
files.push(expandedFile);
if (stat.isDirectory()) { // intentionally does not follow symlinks.
fs.readdirSync(expandedFile).forEach(function (child) {
addFile(expandedFile + '/' + child);
});
}
}
});
}
else {
files = common.expand(filePattern);
}
files.forEach(function innerChmod(file) {
file = path.resolve(file);
if (!fs.existsSync(file)) {
common.error('File not found: ' + file);
}
// When recursing, don't follow symlinks.
if (options.recursive && fs.lstatSync(file).isSymbolicLink()) {
return;
}
var perms = fs.statSync(file).mode;
var type = perms & PERMS.TYPE_MASK;
var newPerms = perms;
if (isNaN(parseInt(mode, 8))) {
// parse options
mode.split(',').forEach(function (symbolicMode) {
/*jshint regexdash:true */
var pattern = /([ugoa]*)([=\+-])([rwxXst]*)/i;
var matches = pattern.exec(symbolicMode);
if (matches) {
var applyTo = matches[1];
var operator = matches[2];
var change = matches[3];
var changeOwner = applyTo.indexOf('u') != -1 || applyTo === 'a' || applyTo === '';
var changeGroup = applyTo.indexOf('g') != -1 || applyTo === 'a' || applyTo === '';
var changeOther = applyTo.indexOf('o') != -1 || applyTo === 'a' || applyTo === '';
var changeRead = change.indexOf('r') != -1;
var changeWrite = change.indexOf('w') != -1;
var changeExec = change.indexOf('x') != -1;
var changeSticky = change.indexOf('t') != -1;
var changeSetuid = change.indexOf('s') != -1;
var mask = 0;
if (changeOwner) {
mask |= (changeRead ? PERMS.OWNER_READ : 0) + (changeWrite ? PERMS.OWNER_WRITE : 0) + (changeExec ? PERMS.OWNER_EXEC : 0) + (changeSetuid ? PERMS.SETUID : 0);
}
if (changeGroup) {
mask |= (changeRead ? PERMS.GROUP_READ : 0) + (changeWrite ? PERMS.GROUP_WRITE : 0) + (changeExec ? PERMS.GROUP_EXEC : 0) + (changeSetuid ? PERMS.SETGID : 0);
}
if (changeOther) {
mask |= (changeRead ? PERMS.OTHER_READ : 0) + (changeWrite ? PERMS.OTHER_WRITE : 0) + (changeExec ? PERMS.OTHER_EXEC : 0);
}
// Sticky bit is special - it's not tied to user, group or other.
if (changeSticky) {
mask |= PERMS.STICKY;
}
switch (operator) {
case '+':
newPerms |= mask;
break;
case '-':
newPerms &= ~mask;
break;
case '=':
newPerms = type + mask;
// According to POSIX, when using = to explicitly set the permissions, setuid and setgid can never be cleared.
if (fs.statSync(file).isDirectory()) {
newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms;
}
break;
}
if (options.verbose) {
log(file + ' -> ' + newPerms.toString(8));
}
if (perms != newPerms) {
if (!options.verbose && options.changes) {
log(file + ' -> ' + newPerms.toString(8));
}
fs.chmodSync(file, newPerms);
}
}
else {
common.error('Invalid symbolic mode change: ' + symbolicMode);
}
});
}
else {
// they gave us a full number
newPerms = type + parseInt(mode, 8);
// POSIX rules are that setuid and setgid can only be added using numeric form, but not cleared.
if (fs.statSync(file).isDirectory()) {
newPerms |= (PERMS.SETUID + PERMS.SETGID) & perms;
}
fs.chmodSync(file, newPerms);
}
});
}
module.exports = _chmod;

189
bin/node_modules/shelljs/src/common.js generated vendored Normal file
View File

@@ -0,0 +1,189 @@
var os = require('os');
var fs = require('fs');
var _ls = require('./ls');
// Module globals
var config = {
silent: false,
fatal: false
};
exports.config = config;
var state = {
error: null,
currentCmd: 'shell.js',
tempDir: null
};
exports.state = state;
var platform = os.type().match(/^Win/) ? 'win' : 'unix';
exports.platform = platform;
function log() {
if (!config.silent)
console.log.apply(this, arguments);
}
exports.log = log;
// Shows error message. Throws unless _continue or config.fatal are true
function error(msg, _continue) {
if (state.error === null)
state.error = '';
state.error += state.currentCmd + ': ' + msg + '\n';
if (msg.length > 0)
log(state.error);
if (config.fatal)
process.exit(1);
if (!_continue)
throw '';
}
exports.error = error;
// In the future, when Proxies are default, we can add methods like `.to()` to primitive strings.
// For now, this is a dummy function to bookmark places we need such strings
function ShellString(str) {
return str;
}
exports.ShellString = ShellString;
// Returns {'alice': true, 'bob': false} when passed a dictionary, e.g.:
// parseOptions('-a', {'a':'alice', 'b':'bob'});
function parseOptions(str, map) {
if (!map)
error('parseOptions() internal error: no map given');
// All options are false by default
var options = {};
for (var letter in map)
options[map[letter]] = false;
if (!str)
return options; // defaults
if (typeof str !== 'string')
error('parseOptions() internal error: wrong str');
// e.g. match[1] = 'Rf' for str = '-Rf'
var match = str.match(/^\-(.+)/);
if (!match)
return options;
// e.g. chars = ['R', 'f']
var chars = match[1].split('');
chars.forEach(function(c) {
if (c in map)
options[map[c]] = true;
else
error('option not recognized: '+c);
});
return options;
}
exports.parseOptions = parseOptions;
// Expands wildcards with matching (ie. existing) file names.
// For example:
// expand(['file*.js']) = ['file1.js', 'file2.js', ...]
// (if the files 'file1.js', 'file2.js', etc, exist in the current dir)
function expand(list) {
var expanded = [];
list.forEach(function(listEl) {
// Wildcard present?
if (listEl.search(/\*/) > -1) {
_ls('', listEl).forEach(function(file) {
expanded.push(file);
});
} else {
expanded.push(listEl);
}
});
return expanded;
}
exports.expand = expand;
// Normalizes _unlinkSync() across platforms to match Unix behavior, i.e.
// file can be unlinked even if it's read-only, see https://github.com/joyent/node/issues/3006
function unlinkSync(file) {
try {
fs.unlinkSync(file);
} catch(e) {
// Try to override file permission
if (e.code === 'EPERM') {
fs.chmodSync(file, '0666');
fs.unlinkSync(file);
} else {
throw e;
}
}
}
exports.unlinkSync = unlinkSync;
// e.g. 'shelljs_a5f185d0443ca...'
function randomFileName() {
function randomHash(count) {
if (count === 1)
return parseInt(16*Math.random(), 10).toString(16);
else {
var hash = '';
for (var i=0; i<count; i++)
hash += randomHash(1);
return hash;
}
}
return 'shelljs_'+randomHash(20);
}
exports.randomFileName = randomFileName;
// extend(target_obj, source_obj1 [, source_obj2 ...])
// Shallow extend, e.g.:
// extend({A:1}, {b:2}, {c:3}) returns {A:1, b:2, c:3}
function extend(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function(source) {
for (var key in source)
target[key] = source[key];
});
return target;
}
exports.extend = extend;
// Common wrapper for all Unix-like commands
function wrap(cmd, fn, options) {
return function() {
var retValue = null;
state.currentCmd = cmd;
state.error = null;
try {
var args = [].slice.call(arguments, 0);
if (options && options.notUnix) {
retValue = fn.apply(this, args);
} else {
if (args.length === 0 || typeof args[0] !== 'string' || args[0][0] !== '-')
args.unshift(''); // only add dummy option if '-option' not already present
retValue = fn.apply(this, args);
}
} catch (e) {
if (!state.error) {
// If state.error hasn't been set it's an error thrown by Node, not us - probably a bug...
console.log('shell.js: internal error');
console.log(e.stack || e);
process.exit(1);
}
if (config.fatal)
throw e;
}
state.currentCmd = 'shell.js';
return retValue;
};
} // wrap
exports.wrap = wrap;

200
bin/node_modules/shelljs/src/cp.js generated vendored Normal file
View File

@@ -0,0 +1,200 @@
var fs = require('fs');
var path = require('path');
var common = require('./common');
// Buffered file copy, synchronous
// (Using readFileSync() + writeFileSync() could easily cause a memory overflow
// with large files)
function copyFileSync(srcFile, destFile) {
if (!fs.existsSync(srcFile))
common.error('copyFileSync: no such file or directory: ' + srcFile);
var BUF_LENGTH = 64*1024,
buf = new Buffer(BUF_LENGTH),
bytesRead = BUF_LENGTH,
pos = 0,
fdr = null,
fdw = null;
try {
fdr = fs.openSync(srcFile, 'r');
} catch(e) {
common.error('copyFileSync: could not read src file ('+srcFile+')');
}
try {
fdw = fs.openSync(destFile, 'w');
} catch(e) {
common.error('copyFileSync: could not write to dest file (code='+e.code+'):'+destFile);
}
while (bytesRead === BUF_LENGTH) {
bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos);
fs.writeSync(fdw, buf, 0, bytesRead);
pos += bytesRead;
}
fs.closeSync(fdr);
fs.closeSync(fdw);
fs.chmodSync(destFile, fs.statSync(srcFile).mode);
}
// Recursively copies 'sourceDir' into 'destDir'
// Adapted from https://github.com/ryanmcgrath/wrench-js
//
// Copyright (c) 2010 Ryan McGrath
// Copyright (c) 2012 Artur Adib
//
// Licensed under the MIT License
// http://www.opensource.org/licenses/mit-license.php
function cpdirSyncRecursive(sourceDir, destDir, opts) {
if (!opts) opts = {};
/* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */
var checkDir = fs.statSync(sourceDir);
try {
fs.mkdirSync(destDir, checkDir.mode);
} catch (e) {
//if the directory already exists, that's okay
if (e.code !== 'EEXIST') throw e;
}
var files = fs.readdirSync(sourceDir);
for (var i = 0; i < files.length; i++) {
var srcFile = sourceDir + "/" + files[i];
var destFile = destDir + "/" + files[i];
var srcFileStat = fs.lstatSync(srcFile);
if (srcFileStat.isDirectory()) {
/* recursion this thing right on back. */
cpdirSyncRecursive(srcFile, destFile, opts);
} else if (srcFileStat.isSymbolicLink()) {
var symlinkFull = fs.readlinkSync(srcFile);
fs.symlinkSync(symlinkFull, destFile);
} else {
/* At this point, we've hit a file actually worth copying... so copy it on over. */
if (fs.existsSync(destFile) && !opts.force) {
common.log('skipping existing file: ' + files[i]);
} else {
copyFileSync(srcFile, destFile);
}
}
} // for files
} // cpdirSyncRecursive
//@
//@ ### cp([options ,] source [,source ...], dest)
//@ ### cp([options ,] source_array, dest)
//@ Available options:
//@
//@ + `-f`: force
//@ + `-r, -R`: recursive
//@
//@ Examples:
//@
//@ ```javascript
//@ cp('file1', 'dir1');
//@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp');
//@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above
//@ ```
//@
//@ Copies files. The wildcard `*` is accepted.
function _cp(options, sources, dest) {
options = common.parseOptions(options, {
'f': 'force',
'R': 'recursive',
'r': 'recursive'
});
// Get sources, dest
if (arguments.length < 3) {
common.error('missing <source> and/or <dest>');
} else if (arguments.length > 3) {
sources = [].slice.call(arguments, 1, arguments.length - 1);
dest = arguments[arguments.length - 1];
} else if (typeof sources === 'string') {
sources = [sources];
} else if ('length' in sources) {
sources = sources; // no-op for array
} else {
common.error('invalid arguments');
}
var exists = fs.existsSync(dest),
stats = exists && fs.statSync(dest);
// Dest is not existing dir, but multiple sources given
if ((!exists || !stats.isDirectory()) && sources.length > 1)
common.error('dest is not a directory (too many sources)');
// Dest is an existing file, but no -f given
if (exists && stats.isFile() && !options.force)
common.error('dest file already exists: ' + dest);
if (options.recursive) {
// Recursive allows the shortcut syntax "sourcedir/" for "sourcedir/*"
// (see Github issue #15)
sources.forEach(function(src, i) {
if (src[src.length - 1] === '/')
sources[i] += '*';
});
// Create dest
try {
fs.mkdirSync(dest, parseInt('0777', 8));
} catch (e) {
// like Unix's cp, keep going even if we can't create dest dir
}
}
sources = common.expand(sources);
sources.forEach(function(src) {
if (!fs.existsSync(src)) {
common.error('no such file or directory: '+src, true);
return; // skip file
}
// If here, src exists
if (fs.statSync(src).isDirectory()) {
if (!options.recursive) {
// Non-Recursive
common.log(src + ' is a directory (not copied)');
} else {
// Recursive
// 'cp /a/source dest' should create 'source' in 'dest'
var newDest = path.join(dest, path.basename(src)),
checkDir = fs.statSync(src);
try {
fs.mkdirSync(newDest, checkDir.mode);
} catch (e) {
//if the directory already exists, that's okay
if (e.code !== 'EEXIST') throw e;
}
cpdirSyncRecursive(src, newDest, {force: options.force});
}
return; // done with dir
}
// If here, src is a file
// When copying to '/path/dir':
// thisDest = '/path/dir/file1'
var thisDest = dest;
if (fs.existsSync(dest) && fs.statSync(dest).isDirectory())
thisDest = path.normalize(dest + '/' + path.basename(src));
if (fs.existsSync(thisDest) && !options.force) {
common.error('dest file already exists: ' + thisDest, true);
return; // skip file
}
copyFileSync(src, thisDest);
}); // forEach(src)
}
module.exports = _cp;

191
bin/node_modules/shelljs/src/dirs.js generated vendored Normal file
View File

@@ -0,0 +1,191 @@
var common = require('./common');
var _cd = require('./cd');
var path = require('path');
// Pushd/popd/dirs internals
var _dirStack = [];
function _isStackIndex(index) {
return (/^[\-+]\d+$/).test(index);
}
function _parseStackIndex(index) {
if (_isStackIndex(index)) {
if (Math.abs(index) < _dirStack.length + 1) { // +1 for pwd
return (/^-/).test(index) ? Number(index) - 1 : Number(index);
} else {
common.error(index + ': directory stack index out of range');
}
} else {
common.error(index + ': invalid number');
}
}
function _actualDirStack() {
return [process.cwd()].concat(_dirStack);
}
//@
//@ ### pushd([options,] [dir | '-N' | '+N'])
//@
//@ Available options:
//@
//@ + `-n`: Suppresses the normal change of directory when adding directories to the stack, so that only the stack is manipulated.
//@
//@ Arguments:
//@
//@ + `dir`: Makes the current working directory be the top of the stack, and then executes the equivalent of `cd dir`.
//@ + `+N`: Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero) to the top of the list by rotating the stack.
//@ + `-N`: Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero) to the top of the list by rotating the stack.
//@
//@ Examples:
//@
//@ ```javascript
//@ // process.cwd() === '/usr'
//@ pushd('/etc'); // Returns /etc /usr
//@ pushd('+1'); // Returns /usr /etc
//@ ```
//@
//@ Save the current directory on the top of the directory stack and then cd to `dir`. With no arguments, pushd exchanges the top two directories. Returns an array of paths in the stack.
function _pushd(options, dir) {
if (_isStackIndex(options)) {
dir = options;
options = '';
}
options = common.parseOptions(options, {
'n' : 'no-cd'
});
var dirs = _actualDirStack();
if (dir === '+0') {
return dirs; // +0 is a noop
} else if (!dir) {
if (dirs.length > 1) {
dirs = dirs.splice(1, 1).concat(dirs);
} else {
return common.error('no other directory');
}
} else if (_isStackIndex(dir)) {
var n = _parseStackIndex(dir);
dirs = dirs.slice(n).concat(dirs.slice(0, n));
} else {
if (options['no-cd']) {
dirs.splice(1, 0, dir);
} else {
dirs.unshift(dir);
}
}
if (options['no-cd']) {
dirs = dirs.slice(1);
} else {
dir = path.resolve(dirs.shift());
_cd('', dir);
}
_dirStack = dirs;
return _dirs('');
}
exports.pushd = _pushd;
//@
//@ ### popd([options,] ['-N' | '+N'])
//@
//@ Available options:
//@
//@ + `-n`: Suppresses the normal change of directory when removing directories from the stack, so that only the stack is manipulated.
//@
//@ Arguments:
//@
//@ + `+N`: Removes the Nth directory (counting from the left of the list printed by dirs), starting with zero.
//@ + `-N`: Removes the Nth directory (counting from the right of the list printed by dirs), starting with zero.
//@
//@ Examples:
//@
//@ ```javascript
//@ echo(process.cwd()); // '/usr'
//@ pushd('/etc'); // '/etc /usr'
//@ echo(process.cwd()); // '/etc'
//@ popd(); // '/usr'
//@ echo(process.cwd()); // '/usr'
//@ ```
//@
//@ When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory. The elements are numbered from 0 starting at the first directory listed with dirs; i.e., popd is equivalent to popd +0. Returns an array of paths in the stack.
function _popd(options, index) {
if (_isStackIndex(options)) {
index = options;
options = '';
}
options = common.parseOptions(options, {
'n' : 'no-cd'
});
if (!_dirStack.length) {
return common.error('directory stack empty');
}
index = _parseStackIndex(index || '+0');
if (options['no-cd'] || index > 0 || _dirStack.length + index === 0) {
index = index > 0 ? index - 1 : index;
_dirStack.splice(index, 1);
} else {
var dir = path.resolve(_dirStack.shift());
_cd('', dir);
}
return _dirs('');
}
exports.popd = _popd;
//@
//@ ### dirs([options | '+N' | '-N'])
//@
//@ Available options:
//@
//@ + `-c`: Clears the directory stack by deleting all of the elements.
//@
//@ Arguments:
//@
//@ + `+N`: Displays the Nth directory (counting from the left of the list printed by dirs when invoked without options), starting with zero.
//@ + `-N`: Displays the Nth directory (counting from the right of the list printed by dirs when invoked without options), starting with zero.
//@
//@ Display the list of currently remembered directories. Returns an array of paths in the stack, or a single path if +N or -N was specified.
//@
//@ See also: pushd, popd
function _dirs(options, index) {
if (_isStackIndex(options)) {
index = options;
options = '';
}
options = common.parseOptions(options, {
'c' : 'clear'
});
if (options['clear']) {
_dirStack = [];
return _dirStack;
}
var stack = _actualDirStack();
if (index) {
index = _parseStackIndex(index);
if (index < 0) {
index = stack.length + index;
}
common.log(stack[index]);
return stack[index];
}
common.log(stack.join(' '));
return stack;
}
exports.dirs = _dirs;

20
bin/node_modules/shelljs/src/echo.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
var common = require('./common');
//@
//@ ### echo(string [,string ...])
//@
//@ Examples:
//@
//@ ```javascript
//@ echo('hello world');
//@ var str = echo('hello world');
//@ ```
//@
//@ Prints string to stdout, and returns string with additional utility methods
//@ like `.to()`.
function _echo() {
var messages = [].slice.call(arguments, 0);
console.log.apply(this, messages);
return common.ShellString(messages.join(' '));
}
module.exports = _echo;

10
bin/node_modules/shelljs/src/error.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
var common = require('./common');
//@
//@ ### error()
//@ Tests if error occurred in the last command. Returns `null` if no error occurred,
//@ otherwise returns string explaining the error
function error() {
return common.state.error;
};
module.exports = error;

181
bin/node_modules/shelljs/src/exec.js generated vendored Normal file
View File

@@ -0,0 +1,181 @@
var common = require('./common');
var _tempDir = require('./tempdir');
var _pwd = require('./pwd');
var path = require('path');
var fs = require('fs');
var child = require('child_process');
// Hack to run child_process.exec() synchronously (sync avoids callback hell)
// Uses a custom wait loop that checks for a flag file, created when the child process is done.
// (Can't do a wait loop that checks for internal Node variables/messages as
// Node is single-threaded; callbacks and other internal state changes are done in the
// event loop).
function execSync(cmd, opts) {
var tempDir = _tempDir();
var stdoutFile = path.resolve(tempDir+'/'+common.randomFileName()),
codeFile = path.resolve(tempDir+'/'+common.randomFileName()),
scriptFile = path.resolve(tempDir+'/'+common.randomFileName()),
sleepFile = path.resolve(tempDir+'/'+common.randomFileName());
var options = common.extend({
silent: common.config.silent
}, opts);
var previousStdoutContent = '';
// Echoes stdout changes from running process, if not silent
function updateStdout() {
if (options.silent || !fs.existsSync(stdoutFile))
return;
var stdoutContent = fs.readFileSync(stdoutFile, 'utf8');
// No changes since last time?
if (stdoutContent.length <= previousStdoutContent.length)
return;
process.stdout.write(stdoutContent.substr(previousStdoutContent.length));
previousStdoutContent = stdoutContent;
}
function escape(str) {
return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0");
}
cmd += ' > '+stdoutFile+' 2>&1'; // works on both win/unix
var script =
"var child = require('child_process')," +
" fs = require('fs');" +
"child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {" +
" fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');" +
"});";
if (fs.existsSync(scriptFile)) common.unlinkSync(scriptFile);
if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile);
if (fs.existsSync(codeFile)) common.unlinkSync(codeFile);
fs.writeFileSync(scriptFile, script);
child.exec('"'+process.execPath+'" '+scriptFile, {
env: process.env,
cwd: _pwd(),
maxBuffer: 20*1024*1024
});
// The wait loop
// sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage
// (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing
// CPU usage, though apparently not so much on Windows)
while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }
while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }
// At this point codeFile exists, but it's not necessarily flushed yet.
// Keep reading it until it is.
var code = parseInt('', 10);
while (isNaN(code)) {
code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10);
}
var stdout = fs.readFileSync(stdoutFile, 'utf8');
// No biggie if we can't erase the files now -- they're in a temp dir anyway
try { common.unlinkSync(scriptFile); } catch(e) {}
try { common.unlinkSync(stdoutFile); } catch(e) {}
try { common.unlinkSync(codeFile); } catch(e) {}
try { common.unlinkSync(sleepFile); } catch(e) {}
// some shell return codes are defined as errors, per http://tldp.org/LDP/abs/html/exitcodes.html
if (code === 1 || code === 2 || code >= 126) {
common.error('', true); // unix/shell doesn't really give an error message after non-zero exit codes
}
// True if successful, false if not
var obj = {
code: code,
output: stdout
};
return obj;
} // execSync()
// Wrapper around exec() to enable echoing output to console in real time
function execAsync(cmd, opts, callback) {
var output = '';
var options = common.extend({
silent: common.config.silent
}, opts);
var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
if (callback)
callback(err ? err.code : 0, output);
});
c.stdout.on('data', function(data) {
output += data;
if (!options.silent)
process.stdout.write(data);
});
c.stderr.on('data', function(data) {
output += data;
if (!options.silent)
process.stdout.write(data);
});
return c;
}
//@
//@ ### exec(command [, options] [, callback])
//@ Available options (all `false` by default):
//@
//@ + `async`: Asynchronous execution. Defaults to true if a callback is provided.
//@ + `silent`: Do not echo program output to console.
//@
//@ Examples:
//@
//@ ```javascript
//@ var version = exec('node --version', {silent:true}).output;
//@
//@ var child = exec('some_long_running_process', {async:true});
//@ child.stdout.on('data', function(data) {
//@ /* ... do something with data ... */
//@ });
//@
//@ exec('some_long_running_process', function(code, output) {
//@ console.log('Exit code:', code);
//@ console.log('Program output:', output);
//@ });
//@ ```
//@
//@ Executes the given `command` _synchronously_, unless otherwise specified.
//@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's
//@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and
//@ the `callback` gets the arguments `(code, output)`.
//@
//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as
//@ the current synchronous implementation uses a lot of CPU. This should be getting
//@ fixed soon.
function _exec(command, options, callback) {
if (!command)
common.error('must specify command');
// Callback is defined instead of options.
if (typeof options === 'function') {
callback = options;
options = { async: true };
}
// Callback is defined with options.
if (typeof options === 'object' && typeof callback === 'function') {
options.async = true;
}
options = common.extend({
silent: common.config.silent,
async: false
}, options);
if (options.async)
return execAsync(command, options, callback);
else
return execSync(command, options);
}
module.exports = _exec;

51
bin/node_modules/shelljs/src/find.js generated vendored Normal file
View File

@@ -0,0 +1,51 @@
var fs = require('fs');
var common = require('./common');
var _ls = require('./ls');
//@
//@ ### find(path [,path ...])
//@ ### find(path_array)
//@ Examples:
//@
//@ ```javascript
//@ find('src', 'lib');
//@ find(['src', 'lib']); // same as above
//@ find('.').filter(function(file) { return file.match(/\.js$/); });
//@ ```
//@
//@ Returns array of all files (however deep) in the given paths.
//@
//@ The main difference from `ls('-R', path)` is that the resulting file names
//@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`.
function _find(options, paths) {
if (!paths)
common.error('no path specified');
else if (typeof paths === 'object')
paths = paths; // assume array
else if (typeof paths === 'string')
paths = [].slice.call(arguments, 1);
var list = [];
function pushFile(file) {
if (common.platform === 'win')
file = file.replace(/\\/g, '/');
list.push(file);
}
// why not simply do ls('-R', paths)? because the output wouldn't give the base dirs
// to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory
paths.forEach(function(file) {
pushFile(file);
if (fs.statSync(file).isDirectory()) {
_ls('-RA', file+'/*').forEach(function(subfile) {
pushFile(subfile);
});
}
});
return list;
}
module.exports = _find;

52
bin/node_modules/shelljs/src/grep.js generated vendored Normal file
View File

@@ -0,0 +1,52 @@
var common = require('./common');
var fs = require('fs');
//@
//@ ### grep([options ,] regex_filter, file [, file ...])
//@ ### grep([options ,] regex_filter, file_array)
//@ Available options:
//@
//@ + `-v`: Inverse the sense of the regex and print the lines not matching the criteria.
//@
//@ Examples:
//@
//@ ```javascript
//@ grep('-v', 'GLOBAL_VARIABLE', '*.js');
//@ grep('GLOBAL_VARIABLE', '*.js');
//@ ```
//@
//@ Reads input string from given files and returns a string containing all lines of the
//@ file that match the given `regex_filter`. Wildcard `*` accepted.
function _grep(options, regex, files) {
options = common.parseOptions(options, {
'v': 'inverse'
});
if (!files)
common.error('no paths given');
if (typeof files === 'string')
files = [].slice.call(arguments, 2);
// if it's array leave it as it is
files = common.expand(files);
var grep = '';
files.forEach(function(file) {
if (!fs.existsSync(file)) {
common.error('no such file or directory: ' + file, true);
return;
}
var contents = fs.readFileSync(file, 'utf8'),
lines = contents.split(/\r*\n/);
lines.forEach(function(line) {
var matched = line.match(regex);
if ((options.inverse && !matched) || (!options.inverse && matched))
grep += line + '\n';
});
});
return common.ShellString(grep);
}
module.exports = _grep;

126
bin/node_modules/shelljs/src/ls.js generated vendored Normal file
View File

@@ -0,0 +1,126 @@
var path = require('path');
var fs = require('fs');
var common = require('./common');
var _cd = require('./cd');
var _pwd = require('./pwd');
//@
//@ ### ls([options ,] path [,path ...])
//@ ### ls([options ,] path_array)
//@ Available options:
//@
//@ + `-R`: recursive
//@ + `-A`: all files (include files beginning with `.`, except for `.` and `..`)
//@
//@ Examples:
//@
//@ ```javascript
//@ ls('projs/*.js');
//@ ls('-R', '/users/me', '/tmp');
//@ ls('-R', ['/users/me', '/tmp']); // same as above
//@ ```
//@
//@ Returns array of files in the given path, or in current directory if no path provided.
function _ls(options, paths) {
options = common.parseOptions(options, {
'R': 'recursive',
'A': 'all',
'a': 'all_deprecated'
});
if (options.all_deprecated) {
// We won't support the -a option as it's hard to image why it's useful
// (it includes '.' and '..' in addition to '.*' files)
// For backwards compatibility we'll dump a deprecated message and proceed as before
common.log('ls: Option -a is deprecated. Use -A instead');
options.all = true;
}
if (!paths)
paths = ['.'];
else if (typeof paths === 'object')
paths = paths; // assume array
else if (typeof paths === 'string')
paths = [].slice.call(arguments, 1);
var list = [];
// Conditionally pushes file to list - returns true if pushed, false otherwise
// (e.g. prevents hidden files to be included unless explicitly told so)
function pushFile(file, query) {
// hidden file?
if (path.basename(file)[0] === '.') {
// not explicitly asking for hidden files?
if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1))
return false;
}
if (common.platform === 'win')
file = file.replace(/\\/g, '/');
list.push(file);
return true;
}
paths.forEach(function(p) {
if (fs.existsSync(p)) {
var stats = fs.statSync(p);
// Simple file?
if (stats.isFile()) {
pushFile(p, p);
return; // continue
}
// Simple dir?
if (stats.isDirectory()) {
// Iterate over p contents
fs.readdirSync(p).forEach(function(file) {
if (!pushFile(file, p))
return;
// Recursive?
if (options.recursive) {
var oldDir = _pwd();
_cd('', p);
if (fs.statSync(file).isDirectory())
list = list.concat(_ls('-R'+(options.all?'A':''), file+'/*'));
_cd('', oldDir);
}
});
return; // continue
}
}
// p does not exist - possible wildcard present
var basename = path.basename(p);
var dirname = path.dirname(p);
// Wildcard present on an existing dir? (e.g. '/tmp/*.js')
if (basename.search(/\*/) > -1 && fs.existsSync(dirname) && fs.statSync(dirname).isDirectory) {
// Escape special regular expression chars
var regexp = basename.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\.|\+|\?)/g, '\\$1');
// Translates wildcard into regex
regexp = '^' + regexp.replace(/\*/g, '.*') + '$';
// Iterate over directory contents
fs.readdirSync(dirname).forEach(function(file) {
if (file.match(new RegExp(regexp))) {
if (!pushFile(path.normalize(dirname+'/'+file), basename))
return;
// Recursive?
if (options.recursive) {
var pp = dirname + '/' + file;
if (fs.lstatSync(pp).isDirectory())
list = list.concat(_ls('-R'+(options.all?'A':''), pp+'/*'));
} // recursive
} // if file matches
}); // forEach
return;
}
common.error('no such file or directory: ' + p, true);
});
return list;
}
module.exports = _ls;

68
bin/node_modules/shelljs/src/mkdir.js generated vendored Normal file
View File

@@ -0,0 +1,68 @@
var common = require('./common');
var fs = require('fs');
var path = require('path');
// Recursively creates 'dir'
function mkdirSyncRecursive(dir) {
var baseDir = path.dirname(dir);
// Base dir exists, no recursion necessary
if (fs.existsSync(baseDir)) {
fs.mkdirSync(dir, parseInt('0777', 8));
return;
}
// Base dir does not exist, go recursive
mkdirSyncRecursive(baseDir);
// Base dir created, can create dir
fs.mkdirSync(dir, parseInt('0777', 8));
}
//@
//@ ### mkdir([options ,] dir [, dir ...])
//@ ### mkdir([options ,] dir_array)
//@ Available options:
//@
//@ + `p`: full path (will create intermediate dirs if necessary)
//@
//@ Examples:
//@
//@ ```javascript
//@ mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g');
//@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above
//@ ```
//@
//@ Creates directories.
function _mkdir(options, dirs) {
options = common.parseOptions(options, {
'p': 'fullpath'
});
if (!dirs)
common.error('no paths given');
if (typeof dirs === 'string')
dirs = [].slice.call(arguments, 1);
// if it's array leave it as it is
dirs.forEach(function(dir) {
if (fs.existsSync(dir)) {
if (!options.fullpath)
common.error('path already exists: ' + dir, true);
return; // skip dir
}
// Base dir does not exist, and no -p option given
var baseDir = path.dirname(dir);
if (!fs.existsSync(baseDir) && !options.fullpath) {
common.error('no such file or directory: ' + baseDir, true);
return; // skip dir
}
if (options.fullpath)
mkdirSyncRecursive(dir);
else
fs.mkdirSync(dir, parseInt('0777', 8));
});
} // mkdir
module.exports = _mkdir;

80
bin/node_modules/shelljs/src/mv.js generated vendored Normal file
View File

@@ -0,0 +1,80 @@
var fs = require('fs');
var path = require('path');
var common = require('./common');
//@
//@ ### mv(source [, source ...], dest')
//@ ### mv(source_array, dest')
//@ Available options:
//@
//@ + `f`: force
//@
//@ Examples:
//@
//@ ```javascript
//@ mv('-f', 'file', 'dir/');
//@ mv('file1', 'file2', 'dir/');
//@ mv(['file1', 'file2'], 'dir/'); // same as above
//@ ```
//@
//@ Moves files. The wildcard `*` is accepted.
function _mv(options, sources, dest) {
options = common.parseOptions(options, {
'f': 'force'
});
// Get sources, dest
if (arguments.length < 3) {
common.error('missing <source> and/or <dest>');
} else if (arguments.length > 3) {
sources = [].slice.call(arguments, 1, arguments.length - 1);
dest = arguments[arguments.length - 1];
} else if (typeof sources === 'string') {
sources = [sources];
} else if ('length' in sources) {
sources = sources; // no-op for array
} else {
common.error('invalid arguments');
}
sources = common.expand(sources);
var exists = fs.existsSync(dest),
stats = exists && fs.statSync(dest);
// Dest is not existing dir, but multiple sources given
if ((!exists || !stats.isDirectory()) && sources.length > 1)
common.error('dest is not a directory (too many sources)');
// Dest is an existing file, but no -f given
if (exists && stats.isFile() && !options.force)
common.error('dest file already exists: ' + dest);
sources.forEach(function(src) {
if (!fs.existsSync(src)) {
common.error('no such file or directory: '+src, true);
return; // skip file
}
// If here, src exists
// When copying to '/path/dir':
// thisDest = '/path/dir/file1'
var thisDest = dest;
if (fs.existsSync(dest) && fs.statSync(dest).isDirectory())
thisDest = path.normalize(dest + '/' + path.basename(src));
if (fs.existsSync(thisDest) && !options.force) {
common.error('dest file already exists: ' + thisDest, true);
return; // skip file
}
if (path.resolve(src) === path.dirname(path.resolve(thisDest))) {
common.error('cannot move to self: '+src, true);
return; // skip file
}
fs.renameSync(src, thisDest);
}); // forEach(src)
} // mv
module.exports = _mv;

1
bin/node_modules/shelljs/src/popd.js generated vendored Normal file
View File

@@ -0,0 +1 @@
// see dirs.js

1
bin/node_modules/shelljs/src/pushd.js generated vendored Normal file
View File

@@ -0,0 +1 @@
// see dirs.js

11
bin/node_modules/shelljs/src/pwd.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
var path = require('path');
var common = require('./common');
//@
//@ ### pwd()
//@ Returns the current directory.
function _pwd(options) {
var pwd = path.resolve(process.cwd());
return common.ShellString(pwd);
}
module.exports = _pwd;

145
bin/node_modules/shelljs/src/rm.js generated vendored Normal file
View File

@@ -0,0 +1,145 @@
var common = require('./common');
var fs = require('fs');
// Recursively removes 'dir'
// Adapted from https://github.com/ryanmcgrath/wrench-js
//
// Copyright (c) 2010 Ryan McGrath
// Copyright (c) 2012 Artur Adib
//
// Licensed under the MIT License
// http://www.opensource.org/licenses/mit-license.php
function rmdirSyncRecursive(dir, force) {
var files;
files = fs.readdirSync(dir);
// Loop through and delete everything in the sub-tree after checking it
for(var i = 0; i < files.length; i++) {
var file = dir + "/" + files[i],
currFile = fs.lstatSync(file);
if(currFile.isDirectory()) { // Recursive function back to the beginning
rmdirSyncRecursive(file, force);
}
else if(currFile.isSymbolicLink()) { // Unlink symlinks
if (force || isWriteable(file)) {
try {
common.unlinkSync(file);
} catch (e) {
common.error('could not remove file (code '+e.code+'): ' + file, true);
}
}
}
else // Assume it's a file - perhaps a try/catch belongs here?
if (force || isWriteable(file)) {
try {
common.unlinkSync(file);
} catch (e) {
common.error('could not remove file (code '+e.code+'): ' + file, true);
}
}
}
// Now that we know everything in the sub-tree has been deleted, we can delete the main directory.
// Huzzah for the shopkeep.
var result;
try {
result = fs.rmdirSync(dir);
} catch(e) {
common.error('could not remove directory (code '+e.code+'): ' + dir, true);
}
return result;
} // rmdirSyncRecursive
// Hack to determine if file has write permissions for current user
// Avoids having to check user, group, etc, but it's probably slow
function isWriteable(file) {
var writePermission = true;
try {
var __fd = fs.openSync(file, 'a');
fs.closeSync(__fd);
} catch(e) {
writePermission = false;
}
return writePermission;
}
//@
//@ ### rm([options ,] file [, file ...])
//@ ### rm([options ,] file_array)
//@ Available options:
//@
//@ + `-f`: force
//@ + `-r, -R`: recursive
//@
//@ Examples:
//@
//@ ```javascript
//@ rm('-rf', '/tmp/*');
//@ rm('some_file.txt', 'another_file.txt');
//@ rm(['some_file.txt', 'another_file.txt']); // same as above
//@ ```
//@
//@ Removes files. The wildcard `*` is accepted.
function _rm(options, files) {
options = common.parseOptions(options, {
'f': 'force',
'r': 'recursive',
'R': 'recursive'
});
if (!files)
common.error('no paths given');
if (typeof files === 'string')
files = [].slice.call(arguments, 1);
// if it's array leave it as it is
files = common.expand(files);
files.forEach(function(file) {
if (!fs.existsSync(file)) {
// Path does not exist, no force flag given
if (!options.force)
common.error('no such file or directory: '+file, true);
return; // skip file
}
// If here, path exists
var stats = fs.lstatSync(file);
if (stats.isFile() || stats.isSymbolicLink()) {
// Do not check for file writing permissions
if (options.force) {
common.unlinkSync(file);
return;
}
if (isWriteable(file))
common.unlinkSync(file);
else
common.error('permission denied: '+file, true);
return;
} // simple file
// Path is an existing directory, but no -r flag given
if (stats.isDirectory() && !options.recursive) {
common.error('path is a directory', true);
return; // skip path
}
// Recursively remove existing directory
if (stats.isDirectory() && options.recursive) {
rmdirSyncRecursive(file, options.force);
}
}); // forEach(file)
} // rm
module.exports = _rm;

43
bin/node_modules/shelljs/src/sed.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
var common = require('./common');
var fs = require('fs');
//@
//@ ### sed([options ,] search_regex, replace_str, file)
//@ Available options:
//@
//@ + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_
//@
//@ Examples:
//@
//@ ```javascript
//@ sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js');
//@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js');
//@ ```
//@
//@ Reads an input string from `file` and performs a JavaScript `replace()` on the input
//@ using the given search regex and replacement string. Returns the new string after replacement.
function _sed(options, regex, replacement, file) {
options = common.parseOptions(options, {
'i': 'inplace'
});
if (typeof replacement === 'string')
replacement = replacement; // no-op
else if (typeof replacement === 'number')
replacement = replacement.toString(); // fallback
else
common.error('invalid replacement string');
if (!file)
common.error('no file given');
if (!fs.existsSync(file))
common.error('no such file or directory: ' + file);
var result = fs.readFileSync(file, 'utf8').replace(regex, replacement);
if (options.inplace)
fs.writeFileSync(file, result, 'utf8');
return common.ShellString(result);
}
module.exports = _sed;

56
bin/node_modules/shelljs/src/tempdir.js generated vendored Normal file
View File

@@ -0,0 +1,56 @@
var common = require('./common');
var os = require('os');
var fs = require('fs');
// Returns false if 'dir' is not a writeable directory, 'dir' otherwise
function writeableDir(dir) {
if (!dir || !fs.existsSync(dir))
return false;
if (!fs.statSync(dir).isDirectory())
return false;
var testFile = dir+'/'+common.randomFileName();
try {
fs.writeFileSync(testFile, ' ');
common.unlinkSync(testFile);
return dir;
} catch (e) {
return false;
}
}
//@
//@ ### tempdir()
//@
//@ Examples:
//@
//@ ```javascript
//@ var tmp = tempdir(); // "/tmp" for most *nix platforms
//@ ```
//@
//@ Searches and returns string containing a writeable, platform-dependent temporary directory.
//@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir).
function _tempDir() {
var state = common.state;
if (state.tempDir)
return state.tempDir; // from cache
state.tempDir = writeableDir(os.tempDir && os.tempDir()) || // node 0.8+
writeableDir(process.env['TMPDIR']) ||
writeableDir(process.env['TEMP']) ||
writeableDir(process.env['TMP']) ||
writeableDir(process.env['Wimp$ScrapDir']) || // RiscOS
writeableDir('C:\\TEMP') || // Windows
writeableDir('C:\\TMP') || // Windows
writeableDir('\\TEMP') || // Windows
writeableDir('\\TMP') || // Windows
writeableDir('/tmp') ||
writeableDir('/var/tmp') ||
writeableDir('/usr/tmp') ||
writeableDir('.'); // last resort
return state.tempDir;
}
module.exports = _tempDir;

85
bin/node_modules/shelljs/src/test.js generated vendored Normal file
View File

@@ -0,0 +1,85 @@
var common = require('./common');
var fs = require('fs');
//@
//@ ### test(expression)
//@ Available expression primaries:
//@
//@ + `'-b', 'path'`: true if path is a block device
//@ + `'-c', 'path'`: true if path is a character device
//@ + `'-d', 'path'`: true if path is a directory
//@ + `'-e', 'path'`: true if path exists
//@ + `'-f', 'path'`: true if path is a regular file
//@ + `'-L', 'path'`: true if path is a symboilc link
//@ + `'-p', 'path'`: true if path is a pipe (FIFO)
//@ + `'-S', 'path'`: true if path is a socket
//@
//@ Examples:
//@
//@ ```javascript
//@ if (test('-d', path)) { /* do something with dir */ };
//@ if (!test('-f', path)) continue; // skip if it's a regular file
//@ ```
//@
//@ Evaluates expression using the available primaries and returns corresponding value.
function _test(options, path) {
if (!path)
common.error('no path given');
// hack - only works with unary primaries
options = common.parseOptions(options, {
'b': 'block',
'c': 'character',
'd': 'directory',
'e': 'exists',
'f': 'file',
'L': 'link',
'p': 'pipe',
'S': 'socket'
});
var canInterpret = false;
for (var key in options)
if (options[key] === true) {
canInterpret = true;
break;
}
if (!canInterpret)
common.error('could not interpret expression');
if (options.link) {
try {
return fs.lstatSync(path).isSymbolicLink();
} catch(e) {
return false;
}
}
if (!fs.existsSync(path))
return false;
if (options.exists)
return true;
var stats = fs.statSync(path);
if (options.block)
return stats.isBlockDevice();
if (options.character)
return stats.isCharacterDevice();
if (options.directory)
return stats.isDirectory();
if (options.file)
return stats.isFile();
if (options.pipe)
return stats.isFIFO();
if (options.socket)
return stats.isSocket();
} // test
module.exports = _test;

29
bin/node_modules/shelljs/src/to.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
var common = require('./common');
var fs = require('fs');
var path = require('path');
//@
//@ ### 'string'.to(file)
//@
//@ Examples:
//@
//@ ```javascript
//@ cat('input.txt').to('output.txt');
//@ ```
//@
//@ Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as
//@ those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_
function _to(options, file) {
if (!file)
common.error('wrong arguments');
if (!fs.existsSync( path.dirname(file) ))
common.error('no such file or directory: ' + path.dirname(file));
try {
fs.writeFileSync(file, this.toString(), 'utf8');
} catch(e) {
common.error('could not write to file (code '+e.code+'): '+file, true);
}
}
module.exports = _to;

29
bin/node_modules/shelljs/src/toEnd.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
var common = require('./common');
var fs = require('fs');
var path = require('path');
//@
//@ ### 'string'.toEnd(file)
//@
//@ Examples:
//@
//@ ```javascript
//@ cat('input.txt').toEnd('output.txt');
//@ ```
//@
//@ Analogous to the redirect-and-append operator `>>` in Unix, but works with JavaScript strings (such as
//@ those returned by `cat`, `grep`, etc).
function _toEnd(options, file) {
if (!file)
common.error('wrong arguments');
if (!fs.existsSync( path.dirname(file) ))
common.error('no such file or directory: ' + path.dirname(file));
try {
fs.appendFileSync(file, this.toString(), 'utf8');
} catch(e) {
common.error('could not append to file (code '+e.code+'): '+file, true);
}
}
module.exports = _toEnd;

79
bin/node_modules/shelljs/src/which.js generated vendored Normal file
View File

@@ -0,0 +1,79 @@
var common = require('./common');
var fs = require('fs');
var path = require('path');
// Cross-platform method for splitting environment PATH variables
function splitPath(p) {
for (i=1;i<2;i++) {}
if (!p)
return [];
if (common.platform === 'win')
return p.split(';');
else
return p.split(':');
}
//@
//@ ### which(command)
//@
//@ Examples:
//@
//@ ```javascript
//@ var nodeExec = which('node');
//@ ```
//@
//@ Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions.
//@ Returns string containing the absolute path to the command.
function _which(options, cmd) {
if (!cmd)
common.error('must specify command');
var pathEnv = process.env.path || process.env.Path || process.env.PATH,
pathArray = splitPath(pathEnv),
where = null;
// No relative/absolute paths provided?
if (cmd.search(/\//) === -1) {
// Search for command in PATH
pathArray.forEach(function(dir) {
if (where)
return; // already found it
var attempt = path.resolve(dir + '/' + cmd);
if (fs.existsSync(attempt)) {
where = attempt;
return;
}
if (common.platform === 'win') {
var baseAttempt = attempt;
attempt = baseAttempt + '.exe';
if (fs.existsSync(attempt)) {
where = attempt;
return;
}
attempt = baseAttempt + '.cmd';
if (fs.existsSync(attempt)) {
where = attempt;
return;
}
attempt = baseAttempt + '.bat';
if (fs.existsSync(attempt)) {
where = attempt;
return;
}
} // if 'win'
});
}
// Command not found anywhere?
if (!fs.existsSync(cmd) && !where)
return null;
where = where || path.resolve(cmd);
return common.ShellString(where);
}
module.exports = _which;

23
bin/node_modules/which/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,23 @@
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

5
bin/node_modules/which/README.md generated vendored Normal file
View File

@@ -0,0 +1,5 @@
The "which" util from npm's guts.
Finds the first instance of a specified executable in the PATH
environment variable. Does not cache the results, so `hash -r` is not
needed when the PATH changes.

14
bin/node_modules/which/bin/which generated vendored Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env node
var which = require("../")
if (process.argv.length < 3) {
console.error("Usage: which <thing>")
process.exit(1)
}
which(process.argv[2], function (er, thing) {
if (er) {
console.error(er.message)
process.exit(er.errno || 127)
}
console.log(thing)
})

31
bin/node_modules/which/package.json generated vendored Normal file
View File

@@ -0,0 +1,31 @@
{
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me"
},
"name": "which",
"description": "Like which(1) unix command. Find the first instance of an executable in the PATH.",
"version": "1.0.5",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-which.git"
},
"main": "which.js",
"bin": {
"which": "./bin/which"
},
"engines": {
"node": "*"
},
"dependencies": {},
"devDependencies": {},
"readme": "The \"which\" util from npm's guts.\n\nFinds the first instance of a specified executable in the PATH\nenvironment variable. Does not cache the results, so `hash -r` is not\nneeded when the PATH changes.\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/isaacs/node-which/issues"
},
"homepage": "https://github.com/isaacs/node-which",
"_id": "which@1.0.5",
"_from": "which@"
}

104
bin/node_modules/which/which.js generated vendored Normal file
View File

@@ -0,0 +1,104 @@
module.exports = which
which.sync = whichSync
var path = require("path")
, fs
, COLON = process.platform === "win32" ? ";" : ":"
, isExe
try {
fs = require("graceful-fs")
} catch (ex) {
fs = require("fs")
}
if (process.platform == "win32") {
// On windows, there is no good way to check that a file is executable
isExe = function isExe () { return true }
} else {
isExe = function isExe (mod, uid, gid) {
//console.error(mod, uid, gid);
//console.error("isExe?", (mod & 0111).toString(8))
var ret = (mod & 0001)
|| (mod & 0010) && process.getgid && gid === process.getgid()
|| (mod & 0100) && process.getuid && uid === process.getuid()
//console.error("isExe?", ret)
return ret
}
}
function which (cmd, cb) {
if (isAbsolute(cmd)) return cb(null, cmd)
var pathEnv = (process.env.PATH || "").split(COLON)
, pathExt = [""]
if (process.platform === "win32") {
pathEnv.push(process.cwd())
pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
if (cmd.indexOf(".") !== -1) pathExt.unshift("")
}
//console.error("pathEnv", pathEnv)
;(function F (i, l) {
if (i === l) return cb(new Error("not found: "+cmd))
var p = path.resolve(pathEnv[i], cmd)
;(function E (ii, ll) {
if (ii === ll) return F(i + 1, l)
var ext = pathExt[ii]
//console.error(p + ext)
fs.stat(p + ext, function (er, stat) {
if (!er &&
stat &&
stat.isFile() &&
isExe(stat.mode, stat.uid, stat.gid)) {
//console.error("yes, exe!", p + ext)
return cb(null, p + ext)
}
return E(ii + 1, ll)
})
})(0, pathExt.length)
})(0, pathEnv.length)
}
function whichSync (cmd) {
if (isAbsolute(cmd)) return cmd
var pathEnv = (process.env.PATH || "").split(COLON)
, pathExt = [""]
if (process.platform === "win32") {
pathEnv.push(process.cwd())
pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
if (cmd.indexOf(".") !== -1) pathExt.unshift("")
}
for (var i = 0, l = pathEnv.length; i < l; i ++) {
var p = path.join(pathEnv[i], cmd)
for (var j = 0, ll = pathExt.length; j < ll; j ++) {
var cur = p + pathExt[j]
var stat
try { stat = fs.statSync(cur) } catch (ex) {}
if (stat &&
stat.isFile() &&
isExe(stat.mode, stat.uid, stat.gid)) return cur
}
}
throw new Error("not found: "+cmd)
}
var isAbsolute = process.platform === "win32" ? absWin : absUnix
function absWin (p) {
if (absUnix(p)) return true
// pull off the device/UNC bit from a windows path.
// from node's lib/path.js
var splitDeviceRe =
/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/
, result = splitDeviceRe.exec(p)
, device = result[1] || ''
, isUnc = device && device.charAt(1) !== ':'
, isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
return isAbsolute
}
function absUnix (p) {
return p.charAt(0) === "/" || p === ""
}

View File

@@ -1,32 +0,0 @@
{
"name": "cordova-android",
"description": "Cordova tooling for the android platform.",
"version": "0.0.0",
"homepage": "http://github.com/apache/cordova-android",
"repository": {
"type": "git",
"url": "https://git-wip-us.apache.org/repos/asf/cordova-andorid.git"
},
"keywords": [
"cli",
"cordova",
"tooling"
],
"engineStrict": "true",
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"shelljs" : "0.1.4"
},
"devDependencies": {
},
"optionalDependencies": {
},
"author": {
"name": "Benn Mapes",
"email": "bennmapes@gmail.com"
},
"contributors": [
]
}

View File

@@ -24,12 +24,18 @@ var build = require('./lib/build'),
args = process.argv;
// Support basic help commands
if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
if(args[2] == '--help' ||
args[2] == '/?' ||
args[2] == '-h' ||
args[2] == 'help' ||
args[2] == '-help' ||
args[2] == '/help') {
build.help();
} else if(reqs.run()) {
build.run(args[2]);
process.exit(0);
} else {
process.exit(2);
}
reqs.run().done(function() {
return build.run(args.slice(2));
}, function(err) {
console.error(err);
process.exit(2);
});
}

View File

@@ -5,9 +5,9 @@
:: 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0build"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'build' script in 'cordova' folder, aborting...>&2

View File

@@ -19,16 +19,26 @@
under the License.
*/
var clean = require('./lib/clean'),
var build = require('./lib/build'),
reqs = require('./lib/check_reqs'),
args = process.argv;
var path = require('path');
// Usage support for when args are given
if(args.length > 2) {
clean.help();
} else if(reqs.run()) {
clean.run();
// Support basic help commands
if(args[2] == '--help' ||
args[2] == '/?' ||
args[2] == '-h' ||
args[2] == 'help' ||
args[2] == '-help' ||
args[2] == '/help') {
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
console.log('Cleans the project directory.');
process.exit(0);
} else {
process.exit(2);
}
reqs.run().done(function() {
return build.runClean(args.slice(2));
}, function(err) {
console.error(err);
process.exit(2);
});
}

View File

@@ -5,9 +5,9 @@
:: 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0clean"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'clean' script in 'cordova' folder, aborting...>&2

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
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.
-->
<widget xmlns = "http://www.w3.org/ns/widgets"
id = "io.cordova.helloCordova"
version = "2.0.0">
<!-- Preferences for Android -->
<preference name="loglevel" value="DEBUG" />
</widget>

View File

@@ -20,70 +20,366 @@
*/
var shell = require('shelljs'),
clean = require('./clean'),
exec = require('./exec'),
spawn = require('./spawn'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
ROOT = path.join(__dirname, '..', '..');
var check_reqs = require('./check_reqs');
/*
* Builds the project with ant.
*/
module.exports.run = function(build_type) {
//default build type
build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
var cmd;
switch(build_type) {
case '--debug' :
clean.run();
cmd = 'ant debug -f ' + path.join(ROOT, 'build.xml');
break;
case '--release' :
clean.run();
cmd = 'ant release -f ' + path.join(ROOT, 'build.xml');
break;
case '--nobuild' :
console.log('Skipping build...');
break;
default :
console.error('Build option \'' + build_type + '\' not recognized.');
process.exit(2);
break;
}
if(cmd) {
var result = shell.exec(cmd, {silent:false, async:false});
if(result.code > 0) {
console.error('ERROR: Failed to build android project.');
console.error(result.output);
process.exit(2);
}
}
}
var LOCAL_PROPERTIES_TEMPLATE =
'# This file is automatically generated.\n' +
'# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';
/*
* Gets the path to the apk file, if not such file exists then
* the script will error out. (should we error or just return undefined?)
*/
module.exports.get_apk = function() {
if(fs.existsSync(path.join(ROOT, 'bin'))) {
var bin_files = fs.readdirSync(path.join(ROOT, 'bin'));
for (file in bin_files) {
if(path.extname(bin_files[file]) == '.apk') {
return path.join(ROOT, 'bin', bin_files[file]);
function findApks(directory) {
var ret = [];
if (fs.existsSync(directory)) {
fs.readdirSync(directory).forEach(function(p) {
if (path.extname(p) == '.apk') {
ret.push(path.join(directory, p));
}
}
console.error('ERROR : No .apk found in \'bin\' folder');
process.exit(2);
} else {
console.error('ERROR : unable to find project bin folder, could not locate .apk');
process.exit(2);
});
}
return ret;
}
function sortFilesByDate(files) {
return files.map(function(p) {
return { p: p, t: fs.statSync(p).mtime };
}).sort(function(a, b) {
var timeDiff = b.t - a.t;
return timeDiff === 0 ? a.p.length - b.p.length : timeDiff;
}).map(function(p) { return p.p; });
}
function findOutputApksHelper(dir, build_type) {
var ret = findApks(dir).filter(function(candidate) {
// Need to choose between release and debug .apk.
if (build_type === 'debug') {
return /-debug/.exec(candidate) && !/-unaligned|-unsigned/.exec(candidate);
}
if (build_type === 'release') {
return /-release/.exec(candidate) && !/-unaligned/.exec(candidate);
}
return true;
});
ret = sortFilesByDate(ret);
if (ret.length === 0) {
return ret;
}
var archSpecific = !!/-x86|-arm/.exec(ret[0]);
return ret.filter(function(p) {
return !!/-x86|-arm/.exec(p) == archSpecific;
});
}
function hasCustomRules() {
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
}
function extractProjectNameFromManifest(projectPath) {
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
var manifestData = fs.readFileSync(manifestPath, 'utf8');
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
if (!m) {
throw new Error('Could not find activity name in ' + manifestPath);
}
return m[1];
}
function extractSubProjectPaths() {
var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
var m;
while (m = r.exec(data)) {
ret[m[1]] = 1;
}
return Object.keys(ret);
}
var builders = {
ant: {
getArgs: function(cmd) {
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
// custom_rules.xml is required for incremental builds.
if (hasCustomRules()) {
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
}
return args;
},
prepEnv: function() {
return check_reqs.check_ant()
.then(function() {
// Copy in build.xml on each build so that:
// A) we don't require the Android SDK at project creation time, and
// B) we always use the SDK's latest version of it.
var sdkDir = process.env['ANDROID_HOME'];
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
function writeBuildXml(projectPath) {
var newData = buildTemplate.replace('PROJECT_NAME', extractProjectNameFromManifest(ROOT));
fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
fs.writeFileSync(path.join(projectPath, 'local.properties'), LOCAL_PROPERTIES_TEMPLATE);
}
}
var subProjects = extractSubProjectPaths();
writeBuildXml(ROOT);
for (var i = 0; i < subProjects.length; ++i) {
writeBuildXml(path.join(ROOT, subProjects[i]));
}
});
},
/*
* Builds the project with ant.
* Returns a promise.
*/
build: function(build_type) {
// Without our custom_rules.xml, we need to clean before building.
var ret = Q();
if (!hasCustomRules()) {
// clean will call check_ant() for us.
ret = this.clean();
}
var builder = this;
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
return check_reqs.check_ant()
.then(function() {
return spawn('ant', args);
});
},
clean: function() {
var args = this.getArgs('clean');
return check_reqs.check_ant()
.then(function() {
return spawn('ant', args);
});
},
findOutputApks: function(build_type) {
var binDir = path.join(ROOT, hasCustomRules() ? 'ant-build' : 'bin');
return findOutputApksHelper(binDir, build_type);
}
},
gradle: {
getArgs: function(cmd) {
var lintSteps;
if (process.env['BUILD_MULTIPLE_APKS']) {
lintSteps = [
'lint',
'lintVitalX86Release',
'lintVitalArmv7Release',
'compileLint',
'copyReleaseLint',
'copyDebugLint'
];
} else {
lintSteps = [
'lint',
'lintVitalRelease',
'compileLint',
'copyReleaseLint',
'copyDebugLint'
];
}
var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
// 10 seconds -> 6 seconds
args.push('-Dorg.gradle.daemon=true');
// Excluding lint: 6s-> 1.6s
for (var i = 0; i < lintSteps.length; ++i) {
args.push('-x', lintSteps[i]);
}
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// args.push('-Dorg.gradle.parallel=true');
return args;
},
prepEnv: function() {
return check_reqs.check_gradle()
.then(function() {
// Copy the gradle wrapper on each build so that:
// A) we don't require the Android SDK at project creation time, and
// B) we always use the SDK's latest version of it.
var projectPath = ROOT;
// check_reqs ensures that this is set.
var sdkDir = process.env['ANDROID_HOME'];
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
if (process.platform == 'win32') {
shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
} else {
shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
}
shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
shell.mkdir('-p', path.join(projectPath, 'gradle'));
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
});
},
/*
* Builds the project with gradle.
* Returns a promise.
*/
build: function(build_type) {
var builder = this;
var wrapper = path.join(ROOT, 'gradlew');
var args = builder.getArgs('build');
return Q().then(function() {
return spawn(wrapper, args);
});
},
clean: function() {
var builder = this;
var wrapper = path.join(ROOT, 'gradlew');
var args = builder.getArgs('clean');
return Q().then(function() {
return spawn(wrapper, args);
});
},
findOutputApks: function(build_type) {
var binDir = path.join(ROOT, 'build', 'apk');
return findOutputApksHelper(binDir, build_type);
}
},
none: {
prepEnv: function() {
return Q();
},
build: function() {
console.log('Skipping build...');
return Q(null);
},
clean: function() {
return Q();
},
findOutputApks: function(build_type) {
return sortFilesByDate(builders.ant.findOutputApks(build_type).concat(builders.gradle.findOutputApks(build_type)));
}
}
};
function parseOpts(options) {
// Backwards-compatibility: Allow a single string argument
if (typeof options == "string") options = [options];
var ret = {
buildType: 'debug',
buildMethod: process.env['ANDROID_BUILD'] || 'ant'
};
// Iterate through command line options
for (var i=0; options && (i < options.length); ++i) {
if (/^--/.exec(options[i])) {
var option = options[i].substring(2);
switch(option) {
case 'debug':
case 'release':
ret.buildType = option;
break;
case 'ant':
case 'gradle':
ret.buildMethod = option;
break;
case 'nobuild' :
ret.buildMethod = 'none';
break;
default :
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
}
} else {
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
}
}
return ret;
}
/*
* Builds the project with the specifed options
* Returns a promise.
*/
module.exports.runClean = function(options) {
var opts = parseOpts(options);
var builder = builders[opts.buildMethod];
return builder.prepEnv()
.then(function() {
return builder.clean();
}).then(function() {
shell.rm('-rf', path.join(ROOT, 'out'));
});
};
/*
* Builds the project with the specifed options
* Returns a promise.
*/
module.exports.run = function(options) {
var opts = parseOpts(options);
var builder = builders[opts.buildMethod];
return builder.prepEnv()
.then(function() {
return builder.build(opts.buildType);
}).then(function() {
var apkPaths = builder.findOutputApks(opts.buildType);
console.log('Built the following apk(s):');
console.log(' ' + apkPaths.join('\n '));
return {
apkPaths: apkPaths,
buildType: opts.buildType,
buildMethod: opts.buildMethod
};
});
};
/*
* Detects the architecture of a device/emulator
* Returns "arm" or "x86".
*/
module.exports.detectArchitecture = function(target) {
return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
.then(function(output) {
if (/intel/i.exec(output)) {
return 'x86';
}
return 'arm';
});
};
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
var paths = buildResults.apkPaths.filter(function(p) {
if (buildResults.buildType == 'debug') {
return /-debug/.exec(p);
}
return !/-debug/.exec(p);
});
var archPattern = new RegExp('-' + arch);
var hasArchPattern = /-x86|-arm/;
for (var i = 0; i < paths.length; ++i) {
if (hasArchPattern.exec(paths[i])) {
if (archPattern.exec(paths[i])) {
return paths[i];
}
} else {
return paths[i];
}
}
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
};
module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'corodva', 'build')) + ' [build_type]');
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
console.log('Build Types : ');
console.log(' \'--debug\': Default build, will build project in using ant debug');
console.log(' \'--release\': will build project using ant release');
console.log(' \'--debug\': Default build, will build project in debug mode');
console.log(' \'--release\': will build project for release');
console.log(' \'--ant\': Default build, will build project with ant');
console.log(' \'--gradle\': will build project with gradle');
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
process.exit(0);
}
};

View File

@@ -19,24 +19,20 @@
under the License.
*/
var shell = require('shelljs'),
var exec = require('./exec'),
Q = require('q'),
path = require('path'),
build = require('./build'),
appinfo = require('./appinfo'),
exec = require('child_process').exec,
ROOT = path.join(__dirname, '..', '..');
/**
* Returns a list of the device ID's found
* Returns a promise for the list of the device ID's found
*/
module.exports.list = function() {
var cmd = 'adb devices';
var result = shell.exec(cmd, {silent:true, async:false});
if (result.code > 0) {
console.error('Failed to execute android command \'' + cmd + '\'.');
process.exit(2);
} else {
var response = result.output.split('\n');
return exec('adb devices')
.then(function(output) {
var response = output.split('\n');
var device_list = [];
for (var i = 1; i < response.length; i++) {
if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
@@ -44,52 +40,51 @@ module.exports.list = function() {
}
}
return device_list;
}
});
}
/*
* Installs a previously built application on the device
* and launches it.
* Returns a promise.
*/
module.exports.install = function(target) {
var device_list = this.list();
if (device_list.length > 0) {
module.exports.install = function(target, buildResults) {
var launchName;
return this.list()
.then(function(device_list) {
if (!device_list || !device_list.length)
return Q.reject('ERROR: Failed to deploy to device, no devices found.');
// default device
target = typeof target !== 'undefined' ? target : device_list[0];
if (device_list.indexOf(target) > -1) {
var apk_path = build.get_apk();
var launchName = appinfo.getActivityName();
target = target || device_list[0];
if (device_list.indexOf(target) < 0)
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
return build.detectArchitecture(target)
.then(function(arch) {
var apk_path = build.findBestApkForArchitecture(buildResults, arch);
launchName = appinfo.getActivityName();
console.log('Using apk: ' + apk_path);
console.log('Installing app on device...');
cmd = 'adb -s ' + target + ' install -r ' + apk_path;
var install = shell.exec(cmd, {silent:false, async:false});
if (install.error || install.output.match(/Failure/)) {
console.error('ERROR : Failed to install apk to device : ');
console.error(install.output);
process.exit(2);
}
var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"';
return exec(cmd);
});
}).then(function(output) {
if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output);
//unlock screen
cmd = 'adb -s ' + target + ' shell input keyevent 82';
shell.exec(cmd, {silent:true, async:false});
// launch the application
console.log('Launching application...');
cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
var launch = shell.exec(cmd, {silent:true, async:false});
if(launch.code > 0) {
console.error('ERROR : Failed to launch application on emulator : ' + launch.error);
console.error(launch.output);
process.exit(2);
} else {
console.log('LANCH SUCCESS');
}
} else {
console.error('ERROR : Unable to find target \'' + target + '\'.');
console.error('Failed to deploy to device.');
process.exit(2);
}
} else {
console.error('ERROR : Failed to deploy to device, no devices found.');
process.exit(2);
}
//unlock screen
var cmd = 'adb -s ' + target + ' shell input keyevent 82';
return exec(cmd);
}, function(err) { return Q.reject('ERROR: Failed to install apk to device: ' + err); })
.then(function() {
// launch the application
console.log('Launching application...');
var cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
return exec(cmd);
}).then(function() {
console.log('LAUNCH SUCCESS');
}, function(err) {
return Q.reject('ERROR: Failed to launch application on device: ' + err);
});
}

View File

@@ -20,14 +20,17 @@
*/
var shell = require('shelljs'),
exec = require('./exec'),
Q = require('q'),
path = require('path'),
appinfo = require('./appinfo'),
build = require('./build'),
ROOT = path.join(__dirname, '..', '..'),
child_process = require('child_process'),
new_emulator = 'cordova_emulator';
/**
* Returns a list of emulator images in the form of objects
* Returns a Promise for a list of emulator images in the form of objects
* {
name : <emulator_name>,
path : <path_to_emulator_image>,
@@ -37,13 +40,9 @@ var shell = require('shelljs'),
}
*/
module.exports.list_images = function() {
var cmd = 'android list avds';
var result = shell.exec(cmd, {silent:true, async:false});
if (result.code > 0) {
console.error('Failed to execute android command \'' + cmd + '\'.');
process.exit(2);
} else {
var response = result.output.split('\n');
return exec('android list avds')
.then(function(output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
@@ -76,41 +75,41 @@ module.exports.list_images = function() {
}
return emulator_list;
}
});
}
/**
* Will return the closest avd to the projects target
* or undefined if no avds exist.
* Returns a promise.
*/
module.exports.best_image = function() {
var project_target = this.get_target().replace('android-', '');
var images = this.list_images();
var closest = 9999;
var best = images[0];
for (i in images) {
var target = images[i].target;
if(target) {
var num = target.split('(API level ')[1].replace(')', '');
if (num == project_target) {
return images[i];
} else if (project_target - num < closest && project_target > num) {
var closest = project_target - num;
best = images[i];
return this.list_images()
.then(function(images) {
var closest = 9999;
var best = images[0];
for (i in images) {
var target = images[i].target;
if(target) {
var num = target.split('(API level ')[1].replace(')', '');
if (num == project_target) {
return images[i];
} else if (project_target - num < closest && project_target > num) {
var closest = project_target - num;
best = images[i];
}
}
}
}
return best;
return best;
});
}
// Returns a promise.
module.exports.list_started = function() {
var cmd = 'adb devices';
var result = shell.exec(cmd, {silent:true, async:false});
if (result.code > 0) {
console.error('Failed to execute android command \'' + cmd + '\'.');
process.exit(2);
} else {
var response = result.output.split('\n');
return exec('adb devices')
.then(function(output) {
var response = output.split('\n');
var started_emulator_list = [];
for (var i = 1; i < response.length; i++) {
if (response[i].match(/device/) && response[i].match(/emulator/)) {
@@ -118,7 +117,7 @@ module.exports.list_started = function() {
}
}
return started_emulator_list;
}
});
}
module.exports.get_target = function() {
@@ -126,163 +125,155 @@ module.exports.get_target = function() {
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
}
// Returns a promise.
module.exports.list_targets = function() {
var target_out = shell.exec('android list targets', {silent:true, async:false}).output.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
if(target_out[i].match(/id:/)) {
targets.push(targets[i].split(' ')[1]);
return exec('android list targets')
.then(function(output) {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
if(target_out[i].match(/id:/)) {
targets.push(targets[i].split(' ')[1]);
}
}
}
return targets;
return targets;
});
}
/*
* Starts an emulator with the given ID,
* and returns the started ID of that emulator.
* If no ID is given it will used the first image availible,
* if no image is availible it will error out (maybe create one?).
* If no ID is given it will used the first image available,
* if no image is available it will error out (maybe create one?).
*
* Returns a promise.
*/
module.exports.start = function(emulator_ID) {
var started_emulators = this.list_started();
var num_started = started_emulators.length;
if (typeof emulator_ID === 'undefined') {
var emulator_list = this.list_images();
if (emulator_list.length > 0) {
emulator_ID = this.best_image().name;
console.log('WARNING : no emulator specified, defaulting to ' + emulator_ID);
var self = this;
var emulator_id, num_started, started_emulators;
return self.list_started()
.then(function(list) {
started_emulators = list;
num_started = started_emulators.length;
if (typeof emulator_ID === 'undefined') {
return self.list_images()
.then(function(emulator_list) {
if (emulator_list.length > 0) {
return self.best_image()
.then(function(best) {
emulator_ID = best.name;
console.log('WARNING : no emulator specified, defaulting to ' + emulator_ID);
return emulator_ID;
});
} else {
return Q.reject('ERROR : No emulator images (avds) found, if you would like to create an\n' +
' avd follow the instructions provided here:\n' +
' http://developer.android.com/tools/devices/index.html\n' +
' Or run \'android create avd --name <name> --target <targetID>\'\n' +
' in on the command line.');
}
});
} else {
console.error('ERROR : No emulator images (avds) found, if you would like to create an');
console.error(' avd follow the instructions provided here : ');
console.error(' http://developer.android.com/tools/devices/index.html')
console.error(' Or run \'android create avd --name <name> --target <targetID>\' ');
console.error(' in on the command line.');
process.exit(2);
/*console.log('WARNING : no emulators availible, creating \'' + new_emulator + '\'.');
this.create_image(new_emulator, this.get_target());
emulator_ID = new_emulator;*/
return Q(emulator_ID);
}
}
var pipe_null = (process.platform == 'win32' || process.platform == 'win64'? '> NUL' : '> /dev/null');
var cmd = 'emulator -avd ' + emulator_ID + ' ' + pipe_null + ' &';
if(process.platform == 'win32' || process.platform == 'win64') {
cmd = '%comspec% /c start cmd /c ' + cmd;
}
var result = shell.exec(cmd, {silent:true, async:false}, function(code, output) {
if (code > 0) {
console.error('Failed to execute android command \'' + cmd + '\'.');
console.error(output);
process.exit(2);
}
});
// wait for emulator to start
console.log('Waiting for emulator...');
var new_started = this.wait_for_emulator(num_started);
var emulator_id;
if (new_started.length > 1) {
for (i in new_started) {
console.log(new_started[i]);
console.log(started_emulators.indexOf(new_started[i]));
if (started_emulators.indexOf(new_started[i]) < 0) {
emulator_id = new_started[i];
}).then(function() {
var cmd = 'emulator';
var args = ['-avd', emulator_ID];
var proc = child_process.spawn(cmd, args, { stdio: 'inherit', detached: true });
proc.unref(); // Don't wait for it to finish, since the emulator will probably keep running for a long time.
}).then(function() {
// wait for emulator to start
console.log('Waiting for emulator...');
return self.wait_for_emulator(num_started);
}).then(function(new_started) {
if (new_started.length > 1) {
for (i in new_started) {
if (started_emulators.indexOf(new_started[i]) < 0) {
emulator_id = new_started[i];
}
}
} else {
emulator_id = new_started[0];
}
} else {
emulator_id = new_started[0];
}
if (!emulator_id) {
console.error('ERROR : Failed to start emulator, could not find new emulator');
process.exit(2);
}
if (!emulator_id) return Q.reject('ERROR : Failed to start emulator, could not find new emulator');
//wait for emulator to boot up
process.stdout.write('Booting up emulator (this may take a while)...');
this.wait_for_boot(emulator_id);
console.log('BOOT COMPLETE');
//wait for emulator to boot up
process.stdout.write('Booting up emulator (this may take a while)...');
return self.wait_for_boot(emulator_id);
}).then(function() {
console.log('BOOT COMPLETE');
//unlock screen
cmd = 'adb -s ' + emulator_id + ' shell input keyevent 82';
shell.exec(cmd, {silent:false, async:false});
//return the new emulator id for the started emulators
return emulator_id;
//unlock screen
return exec('adb -s ' + emulator_id + ' shell input keyevent 82');
}).then(function() {
//return the new emulator id for the started emulators
return emulator_id;
});
}
/*
* Waits for the new emulator to apear on the started-emulator list.
* Returns a promise with a list of newly started emulators' IDs.
*/
module.exports.wait_for_emulator = function(num_running) {
var new_started = this.list_started();
if (new_started.length > num_running) {
return new_started;
} else {
this.sleep(1);
return this.wait_for_emulator(num_running);
}
var self = this;
return self.list_started()
.then(function(new_started) {
if (new_started.length > num_running) {
return new_started;
} else {
return Q.delay(1000).then(function() {
return self.wait_for_emulator(num_running);
});
}
});
}
/*
* Waits for the boot animation property of the emulator to switch to 'stopped'
*/
module.exports.wait_for_boot = function(emulator_id) {
var cmd;
// ShellJS opens a lot of file handles, and the default on OS X is too small.
// TODO : This is not working, need to find a better way to increese the ulimit.
if(process.platform == 'win32' || process.platform == 'win64') {
cmd = 'adb -s ' + emulator_id + ' shell getprop init.svc.bootanim';
} else {
cmd = 'ulimit -S -n 4096; adb -s ' + emulator_id + ' shell getprop init.svc.bootanim';
}
var boot_anim = shell.exec(cmd, {silent:true, async:false});
if (boot_anim.output.match(/stopped/)) {
return;
} else {
process.stdout.write('.');
this.sleep(3);
return this.wait_for_boot(emulator_id);
}
}
/*
* TODO : find a better way to wait for the emulator (maybe using async methods?)
*/
module.exports.sleep = function(time_sec) {
if (process.platform == 'win32' || process.platform == 'win64') {
shell.exec('ping 127.0.0.1 -n ' + time_sec, {silent:true, async:false});
} else {
shell.exec('sleep ' + time_sec, {silent:true, async:false});
}
var self = this;
return exec('adb -s ' + emulator_id + ' shell getprop init.svc.bootanim')
.then(function(output) {
if (output.match(/stopped/)) {
return;
} else {
process.stdout.write('.');
return Q.delay(3000).then(function() {
return self.wait_for_boot(emulator_id);
});
}
});
}
/*
* Create avd
* TODO : Enter the stdin input required to complete the creation of an avd.
* Returns a promise.
*/
module.exports.create_image = function(name, target) {
console.log('Creating avd named ' + name);
if (target) {
var cmd = 'android create avd --name ' + name + ' --target ' + target;
var create = shell.exec(cmd, {sient:false, async:false});
if (create.error) {
return exec('android create avd --name ' + name + ' --target ' + target)
.then(null, function(error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?');
console.error(create.output);
process.exit(2);
}
});
} else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
var cmd = 'android create avd --name ' + name + ' --target ' + this.list_targets()[0];
var create = shell.exec(cmd, {sient:false, async:false});
if (create.error) {
return exec('android create avd --name ' + name + ' --target ' + this.list_targets()[0])
.then(function() {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Please insure you have targets available by running the "android" command');
return Q.reject();
}, function(error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(create.output);
process.exit(2);
}
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Please insure you have targets availible by runing the "android" command').
process.exit(2);
console.error(error);
});
}
}
@@ -290,48 +281,48 @@ module.exports.create_image = function(name, target) {
* Installs a previously built application on the emulator and launches it.
* If no target is specified, then it picks one.
* If no started emulators are found, error out.
* Returns a promise.
*/
module.exports.install = function(target) {
var emulator_list = this.list_started();
if (emulator_list.length < 1) {
console.error('ERROR : No started emulators found, please start an emultor before deploying your project.');
process.exit(2);
/*console.log('WARNING : No started emulators found, attemting to start an avd...');
this.start(this.best_image().name);*/
}
// default emulator
target = typeof target !== 'undefined' ? target : emulator_list[0];
if (emulator_list.indexOf(target) > -1) {
console.log('Installing app on emulator...');
var apk_path = build.get_apk();
var cmd = 'adb -s ' + target + ' install -r ' + apk_path;
var install = shell.exec(cmd, {sient:false, async:false});
if (install.error || install.output.match(/Failure/)) {
console.error('ERROR : Failed to install apk to emulator : ');
console.error(install.output);
process.exit(2);
module.exports.install = function(target, buildResults) {
var self = this;
return this.list_started()
.then(function(emulator_list) {
if (emulator_list.length < 1) {
return Q.reject('No started emulators found, please start an emultor before deploying your project.');
}
//unlock screen
cmd = 'adb -s ' + target + ' shell input keyevent 82';
shell.exec(cmd, {silent:true, async:false});
// default emulator
target = target || emulator_list[0];
if (emulator_list.indexOf(target) < 0) {
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
}
return build.detectArchitecture(target)
.then(function(arch) {
var apk_path = build.findBestApkForArchitecture(buildResults, arch);
console.log('Installing app on emulator...');
console.log('Using apk: ' + apk_path);
return exec('adb -s ' + target + ' install -r "' + apk_path + '"');
});
}).then(function(output) {
if (output.match(/Failure/)) {
return Q.reject('Failed to install apk to emulator: ' + output);
}
return Q();
}, function(err) {
return Q.reject('Failed to install apk to emulator: ' + err);
}).then(function() {
//unlock screen
return exec('adb -s ' + target + ' shell input keyevent 82');
}).then(function() {
// launch the application
console.log('Launching application...');
var launchName = appinfo.getActivityName();
cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
console.log(cmd);
var launch = shell.exec(cmd, {silent:false, async:false});
if(launch.code > 0) {
console.error('ERROR : Failed to launch application on emulator : ' + launch.error);
console.error(launch.output);
process.exit(2);
} else {
console.log('LANCH SUCCESS');
}
} else {
console.error('ERROR : Unable to find target \'' + target + '\'.');
console.error('Failed to deploy to emulator.');
process.exit(2);
}
return exec(cmd);
}).then(function(output) {
console.log('LAUNCH SUCCESS');
}, function(err) {
return Q.reject('Failed to launch app on emulator: ' + err);
});
}

41
bin/templates/cordova/lib/exec.js vendored Normal file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env node
/*
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 child_process = require('child_process'),
Q = require('q');
// Takes a command and optional current working directory.
// Returns a promise that either resolves with the stdout, or
// rejects with an error message and the stderr.
module.exports = function(cmd, opt_cwd) {
var d = Q.defer();
try {
child_process.exec(cmd, {cwd: opt_cwd, maxBuffer: 1024000}, function(err, stdout, stderr) {
if (err) d.reject('Error executing "' + cmd + '": ' + stderr);
else d.resolve(stdout);
});
} catch(e) {
console.error('error caught: ' + e);
d.reject(e);
}
return d.promise;
}

View File

@@ -26,13 +26,17 @@ if(args.length > 2) {
var install_target;
if (args[2].substring(0, 9) == '--target=') {
install_target = args[2].substring(9, args[2].length);
device.install(install_target);
process.exit(0);
device.install(install_target).done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});
} else {
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
process.exit(2);
}
} else {
device.install();
process.exit(0);
}
device.install().done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});
}

View File

@@ -22,17 +22,17 @@
var emulator = require('./emulator'),
args = process.argv;
var install_target;
if(args.length > 2) {
var install_target;
if (args[2].substring(0, 9) == '--target=') {
install_target = args[2].substring(9, args[2].length);
emulator.install(install_target);
process.exit(0);
} else {
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
process.exit(2);
}
} else {
emulator.install();
process.exit(0);
}
}
emulator.install(install_target).done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -22,7 +22,12 @@
var devices = require('./device');
// Usage support for when args are given
var device_list = devices.list();
for(device in device_list) {
console.log(device_list[device]);
}
devices.list().done(function(device_list) {
device_list && device_list.forEach(function(dev) {
console.log(dev);
});
}, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -22,8 +22,11 @@
var emulators = require('./emulator');
// Usage support for when args are given
var emulator_list = emulators.list_images();
for(emulator in emulator_list) {
console.log(emulator_list[emulator].name);
process.exit(0);
}
emulators.list_images().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
console.log(emu.name);
});
}, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -22,8 +22,11 @@
var emulators = require('./emulator');
// Usage support for when args are given
var emulator_list = emulators.list_started();
for(emulator in emulator_list) {
console.log(emulator_list[emulator]);
process.exit(0);
}
emulators.list_started().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
console.log(emu);
});
}, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -21,23 +21,37 @@
var shell = require('shelljs'),
path = require('path'),
ROOT = path.join(__dirname, '..', '..');
Q = require('q'),
child_process = require('child_process'),
ROOT = path.join(__dirname, '..', '..');
/*
* Starts running logcat in the shell.
* Returns a promise.
*/
module.exports.run = function() {
var cmd = 'adb logcat | grep -v nativeGetEnabledTags';
var result = shell.exec(cmd, {silent:false, async:false});
if (result.code > 0) {
console.error('ERROR: Failed to run logcat command.');
console.error(result.output);
process.exit(2);
}
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat']);
adb.stdout.on('data', function(data) {
var lines = data ? data.toString().split('\n') : [];
var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; });
console.log(out.join('\n'));
});
adb.stderr.on('data', console.error);
adb.on('close', function(code) {
if (code > 0) {
d.reject('Failed to run logcat command.');
} else d.resolve();
});
return d.promise;
}
module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'corodva', 'log')));
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
console.log('Gives the logcat output on the command line.');
process.exit(0);
}
}

View File

@@ -22,25 +22,27 @@
var path = require('path'),
build = require('./build'),
emulator = require('./emulator'),
device = require('./device');
device = require('./device'),
Q = require('q');
/*
* Runs the application on a device if availible.
* Runs the application on a device if available.
* If not device is found, it will use a started emulator.
* If no started emulators are found it will attempt to start an avd.
* If no avds are found it will error out.
* Returns a promise.
*/
module.exports.run = function(args) {
var build_type;
var buildFlags = [];
var install_target;
for (var i=2; i<args.length; i++) {
if (args[i] == '--debug') {
build_type = '--debug';
buildFlags.push('--debug');
} else if (args[i] == '--release') {
build_type = '--release';
buildFlags.push('--release');
} else if (args[i] == '--nobuild') {
build_type = '--nobuild';
buildFlags.push('--nobuild');
} else if (args[i] == '--device') {
install_target = '--device';
} else if (args[i] == '--emulator') {
@@ -52,65 +54,79 @@ var path = require('path'),
process.exit(2);
}
}
build.run(build_type);
if (install_target == '--device') {
device.install();
} else if (install_target == '--emulator') {
if (emulator.list_started() == 0) {
emulator.start();
}
emulator.install();
} else if (install_target) {
var devices = device.list();
var started_emulators = emulator.list_started();
var avds = emulator.list_images();
if (devices.indexOf(install_target) > -1) {
device.install(install_target);
} else if (started_emulators.indexOf(install_target) > -1) {
emulator.install(install_target);
} else {
// if target emulator isn't started, then start it.
var emulator_ID;
for(avd in avds) {
if(avds[avd].name == install_target) {
emulator_ID = emulator.start(install_target);
emulator.install(emulator_ID);
break;
}
}
if(!emulator_ID) {
console.error('ERROR : Target \'' + install_target + '\' not found, unalbe to run project');
process.exit(2);
}
}
} else {
// no target given, deploy to device if availible, otherwise use the emulator.
var device_list = device.list();
if (device_list.length > 0) {
console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.');
device.install(device_list[0])
} else {
var emulator_list = emulator.list_started();
if (emulator_list.length > 0) {
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.');
emulator.install(emulator_list[0]);
} else {
console.log('WARNING : No started emulators found, starting an emulator.');
var best_avd = emulator.best_image();
if(best_avd) {
var emulator_ID = emulator.start(best_avd.name);
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.');
emulator.install(emulator_ID);
return build.run(buildFlags).then(function(buildResults) {
if (install_target == '--device') {
return device.install(null, buildResults);
} else if (install_target == '--emulator') {
return emulator.list_started().then(function(started) {
var p = started && started.length > 0 ? Q() : emulator.start();
return p.then(function() { return emulator.install(null, buildResults); });
});
} else if (install_target) {
var devices, started_emulators, avds;
return device.list()
.then(function(res) {
devices = res;
return emulator.list_started();
}).then(function(res) {
started_emulators = res;
return emulator.list_images();
}).then(function(res) {
avds = res;
if (devices.indexOf(install_target) > -1) {
return device.install(install_target, buildResults);
} else if (started_emulators.indexOf(install_target) > -1) {
return emulator.install(install_target, buildResults);
} else {
emulator.start();
// if target emulator isn't started, then start it.
var emulator_ID;
for(avd in avds) {
if(avds[avd].name == install_target) {
return emulator.start(install_target)
.then(function() { emulator.install(emulator_ID, buildResults); });
}
}
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
}
}
});
} else {
// no target given, deploy to device if available, otherwise use the emulator.
return device.list()
.then(function(device_list) {
if (device_list.length > 0) {
console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.');
return device.install(device_list[0], buildResults);
} else {
return emulator.list_started()
.then(function(emulator_list) {
if (emulator_list.length > 0) {
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.');
return emulator.install(emulator_list[0], buildResults);
} else {
console.log('WARNING : No started emulators found, starting an emulator.');
return emulator.best_image()
.then(function(best_avd) {
if(best_avd) {
return emulator.start(best_avd.name)
.then(function(emulator_ID) {
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.');
return emulator.install(emulator_ID, buildResults);
});
} else {
return emulator.start();
}
});
}
});
}
});
}
}
});
}
module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), args[0]) + ' [options]');
module.exports.help = function(args) {
console.log('Usage: ' + path.relative(process.cwd(), args[1]) + ' [options]');
console.log('Build options :');
console.log(' --debug : Builds project in debug mode');
console.log(' --release : Builds project in release mode');
@@ -120,4 +136,4 @@ module.exports.help = function() {
console.log(' --emulator : Will deploy the built project to an emulator if one exists');
console.log(' --target=<target_id> : Installs to the target with the specified id.');
process.exit(0);
}
}

49
bin/templates/cordova/lib/spawn.js vendored Normal file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env node
/*
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 child_process = require('child_process'),
Q = require('q');
var isWindows = process.platform.slice(0, 3) == 'win';
// Takes a command and optional current working directory.
module.exports = function(cmd, args, opt_cwd) {
var d = Q.defer();
try {
// Work around spawn not being able to find .bat files.
if (isWindows) {
args.unshift('/s', '/c', cmd);
cmd = 'cmd';
}
var child = child_process.spawn(cmd, args, {cwd: opt_cwd, stdio: 'inherit'});
child.on('exit', function(code) {
if (code) {
d.reject('Error code ' + code + ' for command: ' + cmd + ' with args: ' + args);
} else {
d.resolve();
}
});
} catch(e) {
console.error('error caught: ' + e);
d.reject(e);
}
return d.promise;
}

View File

@@ -22,17 +22,18 @@
var emulator = require('./emulator'),
args = process.argv;
var install_target;
if(args.length > 2) {
var install_target;
if (args[2].substring(0, 9) == '--target=') {
install_target = args[2].substring(9, args[2].length);
emulator.start(install_target);
process.exit(0);
} else {
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
process.exit(2);
}
} else {
emulator.start();
process.exit(0);
}
}
emulator.start(install_target).done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -26,8 +26,11 @@ var log = require('./lib/log'),
// Usage support for when args are given
if(args.length > 2) {
log.help();
} else if(reqs.run()) {
log.run();
} else {
process.exit(2);
}
reqs.run().done(function() {
return log.run();
}, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});
}

View File

@@ -5,9 +5,9 @@
:: 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0log"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'log' script in 'cordova' folder, aborting...>&2

View File

@@ -26,10 +26,12 @@ var run = require('./lib/run'),
// Support basic help commands
if (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
run.help();
} else if(reqs.run()) {
run.run(args);
process.exit(0);
run.help(args);
} else {
process.exit(2);
}
reqs.run().done(function() {
return run.run(args);
}, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});
}

View File

@@ -5,9 +5,9 @@
:: 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0run"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'run' script in 'cordova' folder, aborting...>&2

View File

@@ -20,6 +20,6 @@
*/
// Coho updates this line:
var VERSION = '3.1.0-dev';
var VERSION = "3.6.4";
console.log(VERSION);

View File

@@ -5,9 +5,9 @@
:: 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0version"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'version' script in 'cordova' folder, aborting...>&2

View File

@@ -22,15 +22,14 @@ package __ID__;
import android.os.Bundle;
import org.apache.cordova.*;
public class __ACTIVITY__ extends DroidGap
public class __ACTIVITY__ extends CordovaActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.init();
// Set by <content src="index.html" /> in config.xml
super.loadUrl(Config.getStartUrl());
//super.loadUrl("file:///android_asset/www/index.html")
loadUrl(launchUrl);
}
}

View File

@@ -17,8 +17,8 @@
specific language governing permissions and limitations
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
@@ -31,12 +31,14 @@
<uses-permission android:name="android.permission.INTERNET" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:hardwareAccelerated="true"
android:debuggable="true">
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
android:hardwareAccelerated="true">
<activity android:name="__ACTIVITY__"
android:label="@string/activity_name"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
<intent-filter>
<intent-filter android:label="@string/launcher_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
@@ -44,4 +46,4 @@
</application>
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="__APILEVEL__"/>
</manifest>
</manifest>

View File

@@ -31,7 +31,7 @@ var app = {
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicity call 'app.receivedEvent(...);'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,68 +0,0 @@
<!DOCTYPE html>
<!--
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.
-->
<html>
<head>
<title>Jasmine Spec Runner</title>
<!-- jasmine source -->
<link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
<script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
<script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="js/index.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/helper.js"></script>
<script type="text/javascript" src="spec/index.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>
<body>
<div id="stage" style="display:none;"></div>
</body>
</html>

View File

@@ -1,33 +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.
*/
afterEach(function() {
document.getElementById('stage').innerHTML = '';
});
var helper = {
trigger: function(obj, name) {
var e = document.createEvent('Event');
e.initEvent(name, true, true);
obj.dispatchEvent(e);
},
getComputedStyle: function(querySelector, property) {
var element = document.querySelector(querySelector);
return window.getComputedStyle(element).getPropertyValue(property);
}
};

View File

@@ -1,67 +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.
*/
describe('app', function() {
describe('initialize', function() {
it('should bind deviceready', function() {
runs(function() {
spyOn(app, 'onDeviceReady');
app.initialize();
helper.trigger(window.document, 'deviceready');
});
waitsFor(function() {
return (app.onDeviceReady.calls.length > 0);
}, 'onDeviceReady should be called once', 500);
runs(function() {
expect(app.onDeviceReady).toHaveBeenCalled();
});
});
});
describe('onDeviceReady', function() {
it('should report that it fired', function() {
spyOn(app, 'receivedEvent');
app.onDeviceReady();
expect(app.receivedEvent).toHaveBeenCalledWith('deviceready');
});
});
describe('receivedEvent', function() {
beforeEach(function() {
var el = document.getElementById('stage');
el.innerHTML = ['<div id="deviceready">',
' <p class="event listening">Listening</p>',
' <p class="event received">Received</p>',
'</div>'].join('\n');
});
it('should hide the listening element', function() {
app.receivedEvent('deviceready');
var displayStyle = helper.getComputedStyle('#deviceready .listening', 'display');
expect(displayStyle).toEqual('none');
});
it('should show the received element', function() {
app.receivedEvent('deviceready');
var displayStyle = helper.getComputedStyle('#deviceready .received', 'display');
expect(displayStyle).toEqual('block');
});
});
});

View File

@@ -1,20 +0,0 @@
Copyright (c) 2008-2011 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,616 +0,0 @@
jasmine.HtmlReporterHelpers = {};
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
var results = child.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
return status;
};
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
var parentDiv = this.dom.summary;
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
var parent = child[parentSuite];
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
}
parentDiv = this.views.suites[parent.id].element;
}
parentDiv.appendChild(childElement);
};
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}
};
jasmine.HtmlReporter = function(_doc) {
var self = this;
var doc = _doc || window.document;
var reporterView;
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
if (specs.length == 0) {
return;
}
createReporterDom(runner.env.versionString());
doc.body.appendChild(dom.reporter);
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView.addSpecs(specs, self.specFilter);
};
self.reportRunnerResults = function(runner) {
reporterView && reporterView.complete();
};
self.reportSuiteResults = function(suite) {
reporterView.suiteComplete(suite);
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
};
self.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
self.specFilter = function(spec) {
if (!focusedSpecName()) {
return true;
}
return spec.getFullName().indexOf(focusedSpecName()) === 0;
};
return self;
function focusedSpecName() {
var specName;
(function memoizeFocusedSpec() {
if (specName) {
return;
}
var paramMap = [];
var params = doc.location.search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
specName = paramMap.spec;
})();
return specName;
}
function createReporterDom(version) {
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
dom.banner = self.createDom('div', { className: 'banner' },
self.createDom('span', { className: 'title' }, "Jasmine "),
self.createDom('span', { className: 'version' }, version)),
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
dom.alert = self.createDom('div', {className: 'alert'}),
dom.results = self.createDom('div', {className: 'results'},
dom.summary = self.createDom('div', { className: 'summary' }),
dom.details = self.createDom('div', { id: 'details' }))
);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
' | ',
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
this.summaryMenuItem.onclick = function() {
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
};
this.detailsMenuItem.onclick = function() {
showDetails();
};
};
this.addSpecs = function(specs, specFilter) {
this.totalSpecCount = specs.length;
this.views = {
specs: {},
suites: {}
};
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
var specView = this.views.specs[spec.id];
switch (specView.status()) {
case 'passed':
this.passedCount++;
break;
case 'failed':
this.failedCount++;
break;
case 'skipped':
this.skippedCount++;
break;
}
specView.refresh();
this.refresh();
};
this.suiteComplete = function(suite) {
var suiteView = this.views.suites[suite.id];
if (isUndefined(suiteView)) {
return;
}
suiteView.refresh();
};
this.refresh = function() {
if (isUndefined(this.resultsMenu)) {
this.createResultsMenu();
}
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.skippedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.skippedAlert);
}
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
// failing specs UI
if (isUndefined(this.failedAlert)) {
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
}
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
if (this.failedCount === 1 && isDefined(dom.alert)) {
dom.alert.appendChild(this.failedAlert);
dom.alert.appendChild(this.resultsMenu);
}
// summary info
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
};
this.complete = function() {
dom.alert.removeChild(this.runningAlert);
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
if (this.failedCount === 0) {
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
} else {
showDetails();
}
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
};
return this;
function showDetails() {
if (dom.reporter.className.search(/showDetails/) === -1) {
dom.reporter.className += " showDetails";
}
}
function isUndefined(obj) {
return typeof obj === 'undefined';
}
function isDefined(obj) {
return !isUndefined(obj);
}
function specPluralizedFor(count) {
var str = count + " spec";
if (count > 1) {
str += "s"
}
return str;
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.description)
);
this.detail = this.createDom('div', { className: 'specDetail' },
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
title: this.spec.getFullName()
}, this.spec.getFullName())
);
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
case 'failed':
this.appendSummaryToSuiteDiv();
this.appendFailureDetail();
break;
}
};
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
this.summary.className += ' ' + this.status();
this.appendToSummary(this.spec, this.summary);
};
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
this.detail.appendChild(messagesDiv);
this.dom.details.appendChild(this.detail);
}
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);
};
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
return this.getSpecStatus(this.suite);
};
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
this.element.className += " " + this.status();
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
/* @deprecated Use jasmine.HtmlReporter instead
*/
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
this.createDom('span', { className: 'title' }, "Jasmine"),
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onclick = function(evt) {
if (showPassed.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onclick = function(evt) {
if (showSkipped.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount === 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
} else {
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
}
}
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap.spec) {
return true;
}
return spec.getFullName().indexOf(paramMap.spec) === 0;
};

Some files were not shown because too many files have changed in this diff Show More