Commit 97e6ab60 by Sebastián Katzer

Upgraded to cordova-ios@4

parent 59e85018
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
}, },
"cordova-plugin-registerusernotificationsettings": { "cordova-plugin-app-event": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
}, },
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
......
...@@ -56,52 +56,74 @@ exports.setDefaults = function (newDefaults) { ...@@ -56,52 +56,74 @@ exports.setDefaults = function (newDefaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} msgs
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (msgs, callback, scope, args) {
this.registerPermission(function(granted) { var fn = function(granted) {
if (!granted) if (!granted) return;
return;
var notifications = Array.isArray(opts) ? opts : [opts]; var notifications = Array.isArray(msgs) ? msgs : [msgs];
for (var i = 0; i < notifications.length; i++) { for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i]; var notification = notifications[i];
this.mergeWithDefaults(properties); this.mergeWithDefaults(notification);
this.convertProperties(properties); this.convertProperties(notification);
} }
this.exec('schedule', notifications, callback, scope); this.exec('schedule', notifications, callback, scope);
}, this); };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (msgs, callback, scope, args) {
var notifications = Array.isArray(opts) ? opts : [opts]; var fn = function(granted) {
for (var i = 0; i < notifications.length; i++) { if (!granted) return;
var properties = notifications[i];
this.convertProperties(properties); var notifications = Array.isArray(msgs) ? msgs : [msgs];
}
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
this.convertProperties(notification);
}
this.exec('update', notifications, callback, scope);
};
this.exec('update', notifications, callback, scope); if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
...@@ -415,6 +437,13 @@ exports.hasPermission = function (callback, scope) { ...@@ -415,6 +437,13 @@ exports.hasPermission = function (callback, scope) {
* The callback function's scope * The callback function's scope
*/ */
exports.registerPermission = function (callback, scope) { exports.registerPermission = function (callback, scope) {
if (this._registered) {
return this.hasPermission(callback, scope);
} else {
this._registered = true;
}
var fn = this.createCallbackFn(callback, scope); var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS') { if (device.platform != 'iOS') {
......
...@@ -45,6 +45,9 @@ exports._defaults = { ...@@ -45,6 +45,9 @@ exports._defaults = {
// listener // listener
exports._listener = {}; exports._listener = {};
// Registered permission flag
exports._registered = false;
/******** /********
* UTIL * * UTIL *
......
...@@ -48,29 +48,35 @@ exports.setDefaults = function (defaults) { ...@@ -48,29 +48,35 @@ exports.setDefaults = function (defaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} notifications
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (notifications, callback, scope, args) {
this.core.schedule(opts, callback, scope); this.core.schedule(notifications, callback, scope, args);
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (notifications, callback, scope, args) {
this.core.update(opts, callback, scope); this.core.update(notifications, callback, scope, args);
}; };
/** /**
......
...@@ -56,52 +56,74 @@ exports.setDefaults = function (newDefaults) { ...@@ -56,52 +56,74 @@ exports.setDefaults = function (newDefaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} msgs
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (msgs, callback, scope, args) {
this.registerPermission(function(granted) { var fn = function(granted) {
if (!granted) if (!granted) return;
return;
var notifications = Array.isArray(opts) ? opts : [opts]; var notifications = Array.isArray(msgs) ? msgs : [msgs];
for (var i = 0; i < notifications.length; i++) { for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i]; var notification = notifications[i];
this.mergeWithDefaults(properties); this.mergeWithDefaults(notification);
this.convertProperties(properties); this.convertProperties(notification);
} }
this.exec('schedule', notifications, callback, scope); this.exec('schedule', notifications, callback, scope);
}, this); };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (msgs, callback, scope, args) {
var notifications = Array.isArray(opts) ? opts : [opts]; var fn = function(granted) {
for (var i = 0; i < notifications.length; i++) { if (!granted) return;
var properties = notifications[i];
this.convertProperties(properties); var notifications = Array.isArray(msgs) ? msgs : [msgs];
}
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
this.convertProperties(notification);
}
this.exec('update', notifications, callback, scope);
};
this.exec('update', notifications, callback, scope); if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
...@@ -415,6 +437,13 @@ exports.hasPermission = function (callback, scope) { ...@@ -415,6 +437,13 @@ exports.hasPermission = function (callback, scope) {
* The callback function's scope * The callback function's scope
*/ */
exports.registerPermission = function (callback, scope) { exports.registerPermission = function (callback, scope) {
if (this._registered) {
return this.hasPermission(callback, scope);
} else {
this._registered = true;
}
var fn = this.createCallbackFn(callback, scope); var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS') { if (device.platform != 'iOS') {
......
...@@ -45,6 +45,9 @@ exports._defaults = { ...@@ -45,6 +45,9 @@ exports._defaults = {
// listener // listener
exports._listener = {}; exports._listener = {};
// Registered permission flag
exports._registered = false;
/******** /********
* UTIL * * UTIL *
......
...@@ -48,29 +48,35 @@ exports.setDefaults = function (defaults) { ...@@ -48,29 +48,35 @@ exports.setDefaults = function (defaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} notifications
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (notifications, callback, scope, args) {
this.core.schedule(opts, callback, scope); this.core.schedule(notifications, callback, scope, args);
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (notifications, callback, scope, args) {
this.core.update(opts, callback, scope); this.core.update(notifications, callback, scope, args);
}; };
/** /**
......
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailability.h"
#import "CDVPlugin.h"
#import "CDVViewController.h"
#import "CDVCommandDelegate.h"
#import "CDVURLProtocol.h"
#import "CDVInvokedUrlCommand.h"
#import "CDVDebug.h"
#import "CDVPluginResult.h"
#import "CDVWhitelist.h"
#import "CDVLocalStorage.h"
#import "CDVScreenOrientationDelegate.h"
#import "CDVTimer.h"
#import "NSArray+Comparisons.h"
#import "NSData+Base64.h"
#import "NSDictionary+Extensions.h"
#import "NSMutableArray+QueueAdditions.h"
#import "UIDevice+Extensions.h"
#import "CDVJSON.h"
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailabilityDeprecated.h"
#define __CORDOVA_IOS__
#define __CORDOVA_0_9_6 906
#define __CORDOVA_1_0_0 10000
#define __CORDOVA_1_1_0 10100
#define __CORDOVA_1_2_0 10200
#define __CORDOVA_1_3_0 10300
#define __CORDOVA_1_4_0 10400
#define __CORDOVA_1_4_1 10401
#define __CORDOVA_1_5_0 10500
#define __CORDOVA_1_6_0 10600
#define __CORDOVA_1_6_1 10601
#define __CORDOVA_1_7_0 10700
#define __CORDOVA_1_8_0 10800
#define __CORDOVA_1_8_1 10801
#define __CORDOVA_1_9_0 10900
#define __CORDOVA_2_0_0 20000
#define __CORDOVA_2_1_0 20100
#define __CORDOVA_2_2_0 20200
#define __CORDOVA_2_3_0 20300
#define __CORDOVA_2_4_0 20400
#define __CORDOVA_2_5_0 20500
#define __CORDOVA_2_6_0 20600
#define __CORDOVA_2_7_0 20700
#define __CORDOVA_2_8_0 20800
#define __CORDOVA_2_9_0 20900
#define __CORDOVA_3_0_0 30000
#define __CORDOVA_3_1_0 30100
#define __CORDOVA_3_2_0 30200
#define __CORDOVA_3_3_0 30300
#define __CORDOVA_3_4_0 30400
#define __CORDOVA_3_4_1 30401
#define __CORDOVA_3_5_0 30500
#define __CORDOVA_3_6_0 30600
#define __CORDOVA_3_7_0 30700
#define __CORDOVA_3_8_0 30800
#define __CORDOVA_3_9_0 30900
#define __CORDOVA_3_9_1 30901
#define __CORDOVA_3_9_2 30902
#define __CORDOVA_NA 99999 /* not available */
/*
#if CORDOVA_VERSION_MIN_REQUIRED >= __CORDOVA_1_7_0
// do something when its at least 1.7.0
#else
// do something else (non 1.7.0)
#endif
*/
#ifndef CORDOVA_VERSION_MIN_REQUIRED
#define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_3_9_2
#endif
/*
Returns YES if it is at least version specified as NSString(X)
Usage:
if (IsAtLeastiOSVersion(@"5.1")) {
// do something for iOS 5.1 or greater
}
*/
#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending)
/* Return the string version of the decimal version */
#define CDV_VERSION [NSString stringWithFormat:@"%d.%d.%d", \
(CORDOVA_VERSION_MIN_REQUIRED / 10000), \
(CORDOVA_VERSION_MIN_REQUIRED % 10000) / 100, \
(CORDOVA_VERSION_MIN_REQUIRED % 10000) % 100]
// Enable this to log all exec() calls.
#define CDV_ENABLE_EXEC_LOGGING 0
#if CDV_ENABLE_EXEC_LOGGING
#define CDV_EXEC_LOG NSLog
#else
#define CDV_EXEC_LOG(...) do {} while (NO)
#endif
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#ifdef __clang__
#define CDV_DEPRECATED(version, msg) __attribute__((deprecated("Deprecated in Cordova " #version ". " msg)))
#else
#define CDV_DEPRECATED(version, msg) __attribute__((deprecated()))
#endif
static inline BOOL CDV_IsIPad(void) CDV_DEPRECATED(3.7.0, "This will be removed in 4.0.0");
static inline BOOL CDV_IsIPhone5(void) CDV_DEPRECATED(3.7.0, "This will be removed in 4.0.0");
static inline BOOL CDV_IsIPad(void) {
return [[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
}
static inline BOOL CDV_IsIPhone5(void) {
return ([[UIScreen mainScreen] bounds].size.width == 568 && [[UIScreen mainScreen] bounds].size.height == 320) || ([[UIScreen mainScreen] bounds].size.height == 568 && [[UIScreen mainScreen] bounds].size.width == 320);
}
\ No newline at end of file
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailability.h"
#import "CDVInvokedUrlCommand.h"
@class CDVPlugin;
@class CDVPluginResult;
@class CDVWhitelist;
@protocol CDVCommandDelegate <NSObject>
@property (nonatomic, readonly) NSDictionary* settings;
- (NSString*)pathForResource:(NSString*)resourcepath;
- (id)getCommandInstance:(NSString*)pluginName;
// Sends a plugin result to the JS. This is thread-safe.
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId;
// Evaluates the given JS. This is thread-safe.
- (void)evalJs:(NSString*)js;
// Can be used to evaluate JS right away instead of scheduling it on the run-loop.
// This is required for dispatch resign and pause events, but should not be used
// without reason. Without the run-loop delay, alerts used in JS callbacks may result
// in dead-lock. This method must be called from the UI thread.
- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop;
// Runs the given block on a background thread using a shared thread-pool.
- (void)runInBackground:(void (^)())block;
// Returns the User-Agent of the associated UIWebView.
- (NSString*)userAgent;
// Returns whether the given URL passes the white-list.
- (BOOL)URLIsWhitelisted:(NSURL*)url;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import "CDVCommandDelegate.h"
@class CDVViewController;
@class CDVCommandQueue;
@interface CDVCommandDelegateImpl : NSObject <CDVCommandDelegate>{
@private
__weak CDVViewController* _viewController;
NSRegularExpression* _callbackIdPattern;
@protected
__weak CDVCommandQueue* _commandQueue;
BOOL _delayResponses;
}
- (id)initWithViewController:(CDVViewController*)viewController;
- (void)flushCommandQueueWithDelayedJs;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVCommandDelegateImpl.h"
#import "CDVJSON_private.h"
#import "CDVCommandQueue.h"
#import "CDVPluginResult.h"
#import "CDVViewController.h"
@implementation CDVCommandDelegateImpl
- (id)initWithViewController:(CDVViewController*)viewController
{
self = [super init];
if (self != nil) {
_viewController = viewController;
_commandQueue = _viewController.commandQueue;
NSError* err = nil;
_callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"[^A-Za-z0-9._-]" options:0 error:&err];
if (err != nil) {
// Couldn't initialize Regex
NSLog(@"Error: Couldn't initialize regex");
_callbackIdPattern = nil;
}
}
return self;
}
- (NSString*)pathForResource:(NSString*)resourcepath
{
NSBundle* mainBundle = [NSBundle mainBundle];
NSMutableArray* directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]];
NSString* filename = [directoryParts lastObject];
[directoryParts removeLastObject];
NSString* directoryPartsJoined = [directoryParts componentsJoinedByString:@"/"];
NSString* directoryStr = _viewController.wwwFolderName;
if ([directoryPartsJoined length] > 0) {
directoryStr = [NSString stringWithFormat:@"%@/%@", _viewController.wwwFolderName, [directoryParts componentsJoinedByString:@"/"]];
}
return [mainBundle pathForResource:filename ofType:@"" inDirectory:directoryStr];
}
- (void)flushCommandQueueWithDelayedJs
{
_delayResponses = YES;
[_commandQueue executePending];
_delayResponses = NO;
}
- (void)evalJsHelper2:(NSString*)js
{
CDV_EXEC_LOG(@"Exec: evalling: %@", [js substringToIndex:MIN([js length], 160)]);
NSString* commandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:js];
if ([commandsJSON length] > 0) {
CDV_EXEC_LOG(@"Exec: Retrieved new exec messages by chaining.");
}
[_commandQueue enqueueCommandBatch:commandsJSON];
[_commandQueue executePending];
}
- (void)evalJsHelper:(NSString*)js
{
// Cycle the run-loop before executing the JS.
// For _delayResponses -
// This ensures that we don't eval JS during the middle of an existing JS
// function (possible since UIWebViewDelegate callbacks can be synchronous).
// For !isMainThread -
// It's a hard error to eval on the non-UI thread.
// For !_commandQueue.currentlyExecuting -
// This works around a bug where sometimes alerts() within callbacks can cause
// dead-lock.
// If the commandQueue is currently executing, then we know that it is safe to
// execute the callback immediately.
// Using (dispatch_get_main_queue()) does *not* fix deadlocks for some reason,
// but performSelectorOnMainThread: does.
if (_delayResponses || ![NSThread isMainThread] || !_commandQueue.currentlyExecuting) {
[self performSelectorOnMainThread:@selector(evalJsHelper2:) withObject:js waitUntilDone:NO];
} else {
[self evalJsHelper2:js];
}
}
- (BOOL)isValidCallbackId:(NSString*)callbackId
{
if ((callbackId == nil) || (_callbackIdPattern == nil)) {
return NO;
}
// Disallow if too long or if any invalid characters were found.
if (([callbackId length] > 100) || [_callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
return NO;
}
return YES;
}
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId
{
CDV_EXEC_LOG(@"Exec(%@): Sending result. Status=%@", callbackId, result.status);
// This occurs when there is are no win/fail callbacks for the call.
if ([@"INVALID" isEqualToString : callbackId]) {
return;
}
// This occurs when the callback id is malformed.
if (![self isValidCallbackId:callbackId]) {
NSLog(@"Invalid callback id received by sendPluginResult");
return;
}
int status = [result.status intValue];
BOOL keepCallback = [result.keepCallback boolValue];
NSString* argumentsAsJSON = [result argumentsAsJSON];
NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)", callbackId, status, argumentsAsJSON, keepCallback];
[self evalJsHelper:js];
}
- (void)evalJs:(NSString*)js
{
[self evalJs:js scheduledOnRunLoop:YES];
}
- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop
{
js = [NSString stringWithFormat:@"try{cordova.require('cordova/exec').nativeEvalAndFetch(function(){%@})}catch(e){console.log('exeption nativeEvalAndFetch : '+e);};", js];
if (scheduledOnRunLoop) {
[self evalJsHelper:js];
} else {
[self evalJsHelper2:js];
}
}
- (id)getCommandInstance:(NSString*)pluginName
{
return [_viewController getCommandInstance:pluginName];
}
- (void)runInBackground:(void (^)())block
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
- (NSString*)userAgent
{
return [_viewController userAgent];
}
- (BOOL)URLIsWhitelisted:(NSURL*)url
{
return ![_viewController.whitelist schemeIsAllowed:[url scheme]] ||
[_viewController.whitelist URLIsAllowed:url logFailure:NO];
}
- (NSDictionary*)settings
{
return _viewController.settings;
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@class CDVInvokedUrlCommand;
@class CDVViewController;
@interface CDVCommandQueue : NSObject
@property (nonatomic, readonly) BOOL currentlyExecuting;
- (id)initWithViewController:(CDVViewController*)viewController;
- (void)dispose;
- (void)resetRequestId;
- (void)enqueueCommandBatch:(NSString*)batchJSON;
- (void)processXhrExecBridgePoke:(NSNumber*)requestId;
- (void)fetchCommandsFromJs;
- (void)executePending;
- (BOOL)execute:(CDVInvokedUrlCommand*)command;
@end
/*
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.
*/
#include <objc/message.h>
#import "CDV.h"
#import "CDVCommandQueue.h"
#import "CDVViewController.h"
#import "CDVCommandDelegateImpl.h"
#import "CDVJSON_private.h"
// Parse JS on the main thread if it's shorter than this.
static const NSInteger JSON_SIZE_FOR_MAIN_THREAD = 4 * 1024; // Chosen arbitrarily.
// Execute multiple commands in one go until this many seconds have passed.
static const double MAX_EXECUTION_TIME = .008; // Half of a 60fps frame.
@interface CDVCommandQueue () {
NSInteger _lastCommandQueueFlushRequestId;
__weak CDVViewController* _viewController;
NSMutableArray* _queue;
NSTimeInterval _startExecutionTime;
}
@end
@implementation CDVCommandQueue
- (BOOL)currentlyExecuting
{
return _startExecutionTime > 0;
}
- (id)initWithViewController:(CDVViewController*)viewController
{
self = [super init];
if (self != nil) {
_viewController = viewController;
_queue = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dispose
{
// TODO(agrieve): Make this a zeroing weak ref once we drop support for 4.3.
_viewController = nil;
}
- (void)resetRequestId
{
_lastCommandQueueFlushRequestId = 0;
}
- (void)enqueueCommandBatch:(NSString*)batchJSON
{
if ([batchJSON length] > 0) {
NSMutableArray* commandBatchHolder = [[NSMutableArray alloc] init];
[_queue addObject:commandBatchHolder];
if ([batchJSON length] < JSON_SIZE_FOR_MAIN_THREAD) {
[commandBatchHolder addObject:[batchJSON cdv_JSONObject]];
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
NSMutableArray* result = [batchJSON cdv_JSONObject];
@synchronized(commandBatchHolder) {
[commandBatchHolder addObject:result];
}
[self performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
});
}
}
}
- (void)processXhrExecBridgePoke:(NSNumber*)requestId
{
NSInteger rid = [requestId integerValue];
// An ID of 1 is a special case because that signifies the first request of
// the page. Since resetRequestId is called from webViewDidStartLoad, and the
// JS context at the time of webViewDidStartLoad is still that of the previous
// page, it's possible for requests from the previous page to come in after this
// point. We ignore these by enforcing that ID=1 be the first ID.
if ((_lastCommandQueueFlushRequestId == 0) && (rid != 1)) {
CDV_EXEC_LOG(@"Exec: Ignoring exec request from previous page.");
return;
}
// Use the request ID to determine if we've already flushed for this request.
// This is required only because the NSURLProtocol enqueues the same request
// multiple times.
if (rid > _lastCommandQueueFlushRequestId) {
_lastCommandQueueFlushRequestId = [requestId integerValue];
[self fetchCommandsFromJs];
[self executePending];
}
}
- (void)fetchCommandsFromJs
{
// Grab all the queued commands from the JS side.
NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:
@"cordova.require('cordova/exec').nativeFetchMessages()"];
CDV_EXEC_LOG(@"Exec: Flushed JS->native queue (hadCommands=%d).", [queuedCommandsJSON length] > 0);
[self enqueueCommandBatch:queuedCommandsJSON];
}
- (void)executePending
{
// Make us re-entrant-safe.
if (_startExecutionTime > 0) {
return;
}
@try {
_startExecutionTime = [NSDate timeIntervalSinceReferenceDate];
while ([_queue count] > 0) {
NSMutableArray* commandBatchHolder = _queue[0];
NSMutableArray* commandBatch = nil;
@synchronized(commandBatchHolder) {
// If the next-up command is still being decoded, wait for it.
if ([commandBatchHolder count] == 0) {
break;
}
commandBatch = commandBatchHolder[0];
}
while ([commandBatch count] > 0) {
@autoreleasepool {
// Execute the commands one-at-a-time.
NSArray* jsonEntry = [commandBatch dequeue];
if ([commandBatch count] == 0) {
[_queue removeObjectAtIndex:0];
}
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(@"Exec(%@): Calling %@.%@", command.callbackId, command.className, command.methodName);
if (![self execute:command]) {
#ifdef DEBUG
NSString* commandJson = [jsonEntry cdv_JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat:@"%@[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;
DLog(@"FAILED pluginJSON = %@", commandString);
#endif
}
}
// Yield if we're taking too long.
if (([_queue count] > 0) && ([NSDate timeIntervalSinceReferenceDate] - _startExecutionTime > MAX_EXECUTION_TIME)) {
[self performSelector:@selector(executePending) withObject:nil afterDelay:0];
return;
}
}
}
} @finally
{
_startExecutionTime = 0;
}
}
- (BOOL)execute:(CDVInvokedUrlCommand*)command
{
if ((command.className == nil) || (command.methodName == nil)) {
NSLog(@"ERROR: Classname and/or methodName not found for command.");
return NO;
}
// Fetch an instance of this class
CDVPlugin* obj = [_viewController.commandDelegate getCommandInstance:command.className];
if (!([obj isKindOfClass:[CDVPlugin class]])) {
NSLog(@"ERROR: Plugin '%@' not found, or is not a CDVPlugin. Check your plugin mapping in config.xml.", command.className);
return NO;
}
BOOL retVal = YES;
double started = [[NSDate date] timeIntervalSince1970] * 1000.0;
// Find the proper selector to call.
NSString* methodName = [NSString stringWithFormat:@"%@:", command.methodName];
SEL normalSelector = NSSelectorFromString(methodName);
if ([obj respondsToSelector:normalSelector]) {
// [obj performSelector:normalSelector withObject:command];
((void (*)(id, SEL, id))objc_msgSend)(obj, normalSelector, command);
} else {
// There's no method to call, so throw an error.
NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", methodName, command.className);
retVal = NO;
}
double elapsed = [[NSDate date] timeIntervalSince1970] * 1000.0 - started;
if (elapsed > 10) {
NSLog(@"THREAD WARNING: ['%@'] took '%f' ms. Plugin should use a background thread.", command.className, elapsed);
}
return retVal;
}
@end
/*
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.
*/
@interface CDVConfigParser : NSObject <NSXMLParserDelegate>
{
NSString* featureName;
}
@property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSMutableArray* whitelistHosts;
@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readonly, strong) NSString* startPage;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVConfigParser.h"
@interface CDVConfigParser ()
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) NSMutableArray* whitelistHosts;
@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSString* startPage;
@end
@implementation CDVConfigParser
@synthesize pluginsDict, settings, whitelistHosts, startPage, startupPluginNames;
- (id)init
{
self = [super init];
if (self != nil) {
self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30];
self.settings = [[NSMutableDictionary alloc] initWithCapacity:30];
self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:30];
[self.whitelistHosts addObject:@"file:///*"];
[self.whitelistHosts addObject:@"content:///*"];
[self.whitelistHosts addObject:@"data:///*"];
self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8];
featureName = nil;
}
return self;
}
- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict
{
if ([elementName isEqualToString:@"preference"]) {
settings[[attributeDict[@"name"] lowercaseString]] = attributeDict[@"value"];
} else if ([elementName isEqualToString:@"feature"]) { // store feature name to use with correct parameter set
featureName = [attributeDict[@"name"] lowercaseString];
} else if ((featureName != nil) && [elementName isEqualToString:@"param"]) {
NSString* paramName = [attributeDict[@"name"] lowercaseString];
id value = attributeDict[@"value"];
if ([paramName isEqualToString:@"ios-package"]) {
pluginsDict[featureName] = value;
}
BOOL paramIsOnload = ([paramName isEqualToString:@"onload"] && [@"true" isEqualToString : value]);
BOOL attribIsOnload = [@"true" isEqualToString :[attributeDict[@"onload"] lowercaseString]];
if (paramIsOnload || attribIsOnload) {
[self.startupPluginNames addObject:featureName];
}
} else if ([elementName isEqualToString:@"access"]) {
[whitelistHosts addObject:attributeDict[@"origin"]];
} else if ([elementName isEqualToString:@"content"]) {
self.startPage = attributeDict[@"src"];
}
}
- (void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName
{
if ([elementName isEqualToString:@"feature"]) { // no longer handling a feature so release
featureName = nil;
}
}
- (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError
{
NSAssert(NO, @"config.xml parse error line %ld col %ld", (long)[parser lineNumber], (long)[parser columnNumber]);
}
@end
/*
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.
*/
#ifdef DEBUG
#define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...)
#endif
#define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPlugin.h"
@interface CDVHandleOpenURL : CDVPlugin
@property (nonatomic, strong) NSURL* url;
@property (nonatomic, assign) BOOL pageLoaded;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVHandleOpenURL.h"
#import "CDV.h"
@implementation CDVHandleOpenURL
- (void)pluginInitialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationLaunchedWithUrl:) name:CDVPluginHandleOpenURLNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationPageDidLoad:) name:CDVPageDidLoadNotification object:nil];
}
- (void)applicationLaunchedWithUrl:(NSNotification*)notification
{
NSURL *url = [notification object];
self.url = url;
// warm-start handler
if (self.pageLoaded) {
[self processOpenUrl:self.url pageLoaded:YES];
self.url = nil;
}
}
- (void)applicationPageDidLoad:(NSNotification*)notification
{
// cold-start handler
self.pageLoaded = YES;
if (self.url) {
[self processOpenUrl:self.url pageLoaded:YES];
self.url = nil;
}
}
- (void)processOpenUrl:(NSURL*)url pageLoaded:(BOOL)pageLoaded
{
if (!pageLoaded) {
// query the webview for readystate
NSString* readyState = [self.webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
pageLoaded = [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
}
if (pageLoaded) {
// calls into javascript global function 'handleOpenURL'
NSString* jsString = [NSString stringWithFormat:@"document.addEventListener('deviceready',function(){if (typeof handleOpenURL === 'function') { handleOpenURL(\"%@\");}});", url];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
} else {
// save for when page has loaded
self.url = url;
}
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface CDVInvokedUrlCommand : NSObject {
NSString* _callbackId;
NSString* _className;
NSString* _methodName;
NSArray* _arguments;
}
@property (nonatomic, readonly) NSArray* arguments;
@property (nonatomic, readonly) NSString* callbackId;
@property (nonatomic, readonly) NSString* className;
@property (nonatomic, readonly) NSString* methodName;
+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry;
- (id)initWithArguments:(NSArray*)arguments
callbackId:(NSString*)callbackId
className:(NSString*)className
methodName:(NSString*)methodName;
- (id)initFromJson:(NSArray*)jsonEntry;
// Returns the argument at the given index.
// If index >= the number of arguments, returns nil.
// If the argument at the given index is NSNull, returns nil.
- (id)argumentAtIndex:(NSUInteger)index;
// Same as above, but returns defaultValue instead of nil.
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue;
// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVInvokedUrlCommand.h"
#import "CDVJSON_private.h"
#import "NSData+Base64.h"
@implementation CDVInvokedUrlCommand
@synthesize arguments = _arguments;
@synthesize callbackId = _callbackId;
@synthesize className = _className;
@synthesize methodName = _methodName;
+ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry
{
return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry];
}
- (id)initFromJson:(NSArray*)jsonEntry
{
id tmp = [jsonEntry objectAtIndex:0];
NSString* callbackId = tmp == [NSNull null] ? nil : tmp;
NSString* className = [jsonEntry objectAtIndex:1];
NSString* methodName = [jsonEntry objectAtIndex:2];
NSMutableArray* arguments = [jsonEntry objectAtIndex:3];
return [self initWithArguments:arguments
callbackId:callbackId
className:className
methodName:methodName];
}
- (id)initWithArguments:(NSArray*)arguments
callbackId:(NSString*)callbackId
className:(NSString*)className
methodName:(NSString*)methodName
{
self = [super init];
if (self != nil) {
_arguments = arguments;
_callbackId = callbackId;
_className = className;
_methodName = methodName;
}
[self massageArguments];
return self;
}
- (void)massageArguments
{
NSMutableArray* newArgs = nil;
for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) {
id arg = [_arguments objectAtIndex:i];
if (![arg isKindOfClass:[NSDictionary class]]) {
continue;
}
NSDictionary* dict = arg;
NSString* type = [dict objectForKey:@"CDVType"];
if (!type || ![type isEqualToString:@"ArrayBuffer"]) {
continue;
}
NSString* data = [dict objectForKey:@"data"];
if (!data) {
continue;
}
if (newArgs == nil) {
newArgs = [NSMutableArray arrayWithArray:_arguments];
_arguments = newArgs;
}
[newArgs replaceObjectAtIndex:i withObject:[NSData cdv_dataFromBase64String:data]];
}
}
- (id)argumentAtIndex:(NSUInteger)index
{
return [self argumentAtIndex:index withDefault:nil];
}
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue
{
return [self argumentAtIndex:index withDefault:defaultValue andClass:nil];
}
- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass
{
if (index >= [_arguments count]) {
return defaultValue;
}
id ret = [_arguments objectAtIndex:index];
if (ret == [NSNull null]) {
ret = defaultValue;
}
if ((aClass != nil) && ![ret isKindOfClass:aClass]) {
ret = defaultValue;
}
return ret;
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVAvailabilityDeprecated.h"
@interface NSArray (CDVJSONSerializing)
- (NSString*)JSONString CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");
@end
@interface NSDictionary (CDVJSONSerializing)
- (NSString*)JSONString CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");
@end
@interface NSString (CDVJSONSerializing)
- (id)JSONObject CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");
- (id)JSONFragment CDV_DEPRECATED(3.8 .0, "Use NSJSONSerialization instead.");
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVJSON_private.h"
@implementation NSArray (CDVJSONSerializing)
- (NSString*)JSONString
{
return [self cdv_JSONString];
}
@end
@implementation NSDictionary (CDVJSONSerializing)
- (NSString*)JSONString
{
return [self cdv_JSONString];
}
@end
@implementation NSString (CDVJSONSerializing)
- (id)JSONObject
{
return [self cdv_JSONObject];
}
- (id)JSONFragment
{
return [self cdv_JSONFragment];
}
@end
/*
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.
*/
@interface NSArray (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString;
@end
@interface NSDictionary (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString;
@end
@interface NSString (CDVJSONSerializingPrivate)
- (id)cdv_JSONObject;
- (id)cdv_JSONFragment;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVJSON_private.h"
#import <Foundation/NSJSONSerialization.h>
@implementation NSArray (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString
{
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
options:0
error:&error];
if (error != nil) {
NSLog(@"NSArray JSONString error: %@", [error localizedDescription]);
return nil;
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
@end
@implementation NSDictionary (CDVJSONSerializingPrivate)
- (NSString*)cdv_JSONString
{
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
options:NSJSONWritingPrettyPrinted
error:&error];
if (error != nil) {
NSLog(@"NSDictionary JSONString error: %@", [error localizedDescription]);
return nil;
} else {
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
}
@end
@implementation NSString (CDVJSONSerializingPrivate)
- (id)cdv_JSONObject
{
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingMutableContainers
error:&error];
if (error != nil) {
NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
}
return object;
}
- (id)cdv_JSONFragment
{
NSError* error = nil;
id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:&error];
if (error != nil) {
NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
}
return object;
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPlugin.h"
#define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain"
#define kCDVLocalStorageFileOperationError 1
@interface CDVLocalStorage : CDVPlugin
@property (nonatomic, readonly, strong) NSMutableArray* backupInfo;
- (BOOL)shouldBackup;
- (BOOL)shouldRestore;
- (void)backup:(CDVInvokedUrlCommand*)command;
- (void)restore:(CDVInvokedUrlCommand*)command;
+ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType;
// Visible for testing.
+ (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict
bundlePath:(NSString*)bundlePath
fileManager:(NSFileManager*)fileManager;
@end
@interface CDVBackupInfo : NSObject
@property (nonatomic, copy) NSString* original;
@property (nonatomic, copy) NSString* backup;
@property (nonatomic, copy) NSString* label;
- (BOOL)shouldBackup;
- (BOOL)shouldRestore;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVLocalStorage.h"
#import "CDV.h"
@interface CDVLocalStorage ()
@property (nonatomic, readwrite, strong) NSMutableArray* backupInfo; // array of CDVBackupInfo objects
@property (nonatomic, readwrite, weak) id <UIWebViewDelegate> webviewDelegate;
@end
@implementation CDVLocalStorage
@synthesize backupInfo, webviewDelegate;
- (void)pluginInitialize
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive)
name:UIApplicationWillResignActiveNotification object:nil];
BOOL cloudBackup = [@"cloud" isEqualToString : self.commandDelegate.settings[[@"BackupWebStorage" lowercaseString]]];
self.backupInfo = [[self class] createBackupInfoWithCloudBackup:cloudBackup];
}
#pragma mark -
#pragma mark Plugin interface methods
+ (NSMutableArray*)createBackupInfoWithTargetDir:(NSString*)targetDir backupDir:(NSString*)backupDir targetDirNests:(BOOL)targetDirNests backupDirNests:(BOOL)backupDirNests rename:(BOOL)rename
{
/*
This "helper" does so much work and has so many options it would probably be clearer to refactor the whole thing.
Basically, there are three database locations:
1. "Normal" dir -- LIB/<nested dires WebKit/LocalStorage etc>/<normal filenames>
2. "Caches" dir -- LIB/Caches/<normal filenames>
3. "Backup" dir -- DOC/Backups/<renamed filenames>
And between these three, there are various migration paths, most of which only consider 2 of the 3, which is why this helper is based on 2 locations and has a notion of "direction".
*/
NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:3];
NSString* original;
NSString* backup;
CDVBackupInfo* backupItem;
// ////////// LOCALSTORAGE
original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/file__0.localstorage":@"file__0.localstorage"];
backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage" : @"")];
backup = [backup stringByAppendingPathComponent:(rename ? @"localstorage.appdata.db" : @"file__0.localstorage")];
backupItem = [[CDVBackupInfo alloc] init];
backupItem.backup = backup;
backupItem.original = original;
backupItem.label = @"localStorage database";
[backupInfo addObject:backupItem];
// ////////// WEBSQL MAIN DB
original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/Databases.db":@"Databases.db"];
backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage" : @"")];
backup = [backup stringByAppendingPathComponent:(rename ? @"websqlmain.appdata.db" : @"Databases.db")];
backupItem = [[CDVBackupInfo alloc] init];
backupItem.backup = backup;
backupItem.original = original;
backupItem.label = @"websql main database";
[backupInfo addObject:backupItem];
// ////////// WEBSQL DATABASES
original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/file__0":@"file__0"];
backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage" : @"")];
backup = [backup stringByAppendingPathComponent:(rename ? @"websqldbs.appdata.db" : @"file__0")];
backupItem = [[CDVBackupInfo alloc] init];
backupItem.backup = backup;
backupItem.original = original;
backupItem.label = @"websql databases";
[backupInfo addObject:backupItem];
return backupInfo;
}
+ (NSMutableArray*)createBackupInfoWithCloudBackup:(BOOL)cloudBackup
{
// create backup info from backup folder to caches folder
NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* cacheFolder = [appLibraryFolder stringByAppendingPathComponent:@"Caches"];
NSString* backupsFolder = [appDocumentsFolder stringByAppendingPathComponent:@"Backups"];
// create the backups folder, if needed
[[NSFileManager defaultManager] createDirectoryAtPath:backupsFolder withIntermediateDirectories:YES attributes:nil error:nil];
[self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:backupsFolder] skip:!cloudBackup];
return [self createBackupInfoWithTargetDir:cacheFolder backupDir:backupsFolder targetDirNests:NO backupDirNests:NO rename:YES];
}
+ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL*)URL skip:(BOOL)skip
{
NSAssert(IsAtLeastiOSVersion(@"5.1"), @"Cannot mark files for NSURLIsExcludedFromBackupKey on iOS less than 5.1");
NSError* error = nil;
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:skip] forKey:NSURLIsExcludedFromBackupKey error:&error];
if (!success) {
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
}
return success;
}
+ (BOOL)copyFrom:(NSString*)src to:(NSString*)dest error:(NSError* __autoreleasing*)error
{
NSFileManager* fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:src]) {
NSString* errorString = [NSString stringWithFormat:@"%@ file does not exist.", src];
if (error != NULL) {
(*error) = [NSError errorWithDomain:kCDVLocalStorageErrorDomain
code:kCDVLocalStorageFileOperationError
userInfo:[NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey]];
}
return NO;
}
// generate unique filepath in temp directory
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
NSString* tempBackup = [[NSTemporaryDirectory() stringByAppendingPathComponent:(__bridge NSString*)uuidString] stringByAppendingPathExtension:@"bak"];
CFRelease(uuidString);
CFRelease(uuidRef);
BOOL destExists = [fileManager fileExistsAtPath:dest];
// backup the dest
if (destExists && ![fileManager copyItemAtPath:dest toPath:tempBackup error:error]) {
return NO;
}
// remove the dest
if (destExists && ![fileManager removeItemAtPath:dest error:error]) {
return NO;
}
// create path to dest
if (!destExists && ![fileManager createDirectoryAtPath:[dest stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:error]) {
return NO;
}
// copy src to dest
if ([fileManager copyItemAtPath:src toPath:dest error:error]) {
// success - cleanup - delete the backup to the dest
if ([fileManager fileExistsAtPath:tempBackup]) {
[fileManager removeItemAtPath:tempBackup error:error];
}
return YES;
} else {
// failure - we restore the temp backup file to dest
[fileManager copyItemAtPath:tempBackup toPath:dest error:error];
// cleanup - delete the backup to the dest
if ([fileManager fileExistsAtPath:tempBackup]) {
[fileManager removeItemAtPath:tempBackup error:error];
}
return NO;
}
}
- (BOOL)shouldBackup
{
for (CDVBackupInfo* info in self.backupInfo) {
if ([info shouldBackup]) {
return YES;
}
}
return NO;
}
- (BOOL)shouldRestore
{
for (CDVBackupInfo* info in self.backupInfo) {
if ([info shouldRestore]) {
return YES;
}
}
return NO;
}
/* copy from webkitDbLocation to persistentDbLocation */
- (void)backup:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = command.callbackId;
NSError* __autoreleasing error = nil;
CDVPluginResult* result = nil;
NSString* message = nil;
for (CDVBackupInfo* info in self.backupInfo) {
if ([info shouldBackup]) {
[[self class] copyFrom:info.original to:info.backup error:&error];
if (callbackId) {
if (error == nil) {
message = [NSString stringWithFormat:@"Backed up: %@", info.label];
NSLog(@"%@", message);
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
} else {
message = [NSString stringWithFormat:@"Error in CDVLocalStorage (%@) backup: %@", info.label, [error localizedDescription]];
NSLog(@"%@", message);
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
}
}
}
}
}
/* copy from persistentDbLocation to webkitDbLocation */
- (void)restore:(CDVInvokedUrlCommand*)command
{
NSError* __autoreleasing error = nil;
CDVPluginResult* result = nil;
NSString* message = nil;
for (CDVBackupInfo* info in self.backupInfo) {
if ([info shouldRestore]) {
[[self class] copyFrom:info.backup to:info.original error:&error];
if (error == nil) {
message = [NSString stringWithFormat:@"Restored: %@", info.label];
NSLog(@"%@", message);
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
} else {
message = [NSString stringWithFormat:@"Error in CDVLocalStorage (%@) restore: %@", info.label, [error localizedDescription]];
NSLog(@"%@", message);
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
}
}
}
+ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType
{
[self __verifyAndFixDatabaseLocations];
[self __restoreLegacyDatabaseLocationsWithBackupType:backupType];
}
+ (void)__verifyAndFixDatabaseLocations
{
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* bundlePath = [[mainBundle bundlePath] stringByDeletingLastPathComponent];
NSString* bundleIdentifier = [[mainBundle infoDictionary] objectForKey:@"CFBundleIdentifier"];
NSString* appPlistPath = [bundlePath stringByAppendingPathComponent:[NSString stringWithFormat:@"Library/Preferences/%@.plist", bundleIdentifier]];
NSMutableDictionary* appPlistDict = [NSMutableDictionary dictionaryWithContentsOfFile:appPlistPath];
BOOL modified = [[self class] __verifyAndFixDatabaseLocationsWithAppPlistDict:appPlistDict
bundlePath:bundlePath
fileManager:[NSFileManager defaultManager]];
if (modified) {
BOOL ok = [appPlistDict writeToFile:appPlistPath atomically:YES];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"Fix applied for database locations?: %@", ok ? @"YES" : @"NO");
}
}
+ (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict
bundlePath:(NSString*)bundlePath
fileManager:(NSFileManager*)fileManager
{
NSString* libraryCaches = @"Library/Caches";
NSString* libraryWebKit = @"Library/WebKit";
NSArray* keysToCheck = [NSArray arrayWithObjects:
@"WebKitLocalStorageDatabasePathPreferenceKey",
@"WebDatabaseDirectory",
nil];
BOOL dirty = NO;
for (NSString* key in keysToCheck) {
NSString* value = [appPlistDict objectForKey:key];
// verify key exists, and path is in app bundle, if not - fix
if ((value != nil) && ![value hasPrefix:bundlePath]) {
// the pathSuffix to use may be wrong - OTA upgrades from < 5.1 to 5.1 do keep the old path Library/WebKit,
// while Xcode synced ones do change the storage location to Library/Caches
NSString* newBundlePath = [bundlePath stringByAppendingPathComponent:libraryCaches];
if (![fileManager fileExistsAtPath:newBundlePath]) {
newBundlePath = [bundlePath stringByAppendingPathComponent:libraryWebKit];
}
[appPlistDict setValue:newBundlePath forKey:key];
dirty = YES;
}
}
return dirty;
}
+ (void)__restoreLegacyDatabaseLocationsWithBackupType:(NSString*)backupType
{
// on iOS 6, if you toggle between cloud/local backup, you must move database locations. Default upgrade from iOS5.1 to iOS6 is like a toggle from local to cloud.
if (!IsAtLeastiOSVersion(@"6.0")) {
return;
}
NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:0];
if ([backupType isEqualToString:@"cloud"]) {
#ifdef DEBUG
NSLog(@"\n\nStarted backup to iCloud! Please be careful."
"\nYour application might be rejected by Apple if you store too much data."
"\nFor more information please read \"iOS Data Storage Guidelines\" at:"
"\nhttps://developer.apple.com/icloud/documentation/data-storage/"
"\nTo disable web storage backup to iCloud, set the BackupWebStorage preference to \"local\" in the Cordova config.xml file\n\n");
#endif
// We would like to restore old backups/caches databases to the new destination (nested in lib folder)
[backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appDocumentsFolder stringByAppendingPathComponent:@"Backups"] targetDirNests:YES backupDirNests:NO rename:YES]];
[backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appLibraryFolder stringByAppendingPathComponent:@"Caches"] targetDirNests:YES backupDirNests:NO rename:NO]];
} else {
// For ios6 local backups we also want to restore from Backups dir -- but we don't need to do that here, since the plugin will do that itself.
[backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:[appLibraryFolder stringByAppendingPathComponent:@"Caches"] backupDir:appLibraryFolder targetDirNests:NO backupDirNests:YES rename:NO]];
}
NSFileManager* manager = [NSFileManager defaultManager];
for (CDVBackupInfo* info in backupInfo) {
if ([manager fileExistsAtPath:info.backup]) {
if ([info shouldRestore]) {
NSLog(@"Restoring old webstorage backup. From: '%@' To: '%@'.", info.backup, info.original);
[self copyFrom:info.backup to:info.original error:nil];
}
NSLog(@"Removing old webstorage backup: '%@'.", info.backup);
[manager removeItemAtPath:info.backup error:nil];
}
}
[[NSUserDefaults standardUserDefaults] setBool:[backupType isEqualToString:@"cloud"] forKey:@"WebKitStoreWebDataForBackup"];
}
#pragma mark -
#pragma mark Notification handlers
- (void)onResignActive
{
UIDevice* device = [UIDevice currentDevice];
NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"];
BOOL isMultitaskingSupported = [device respondsToSelector:@selector(isMultitaskingSupported)] && [device isMultitaskingSupported];
if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default)
exitsOnSuspend = [NSNumber numberWithBool:NO];
}
if (exitsOnSuspend) {
[self backup:nil];
} else if (isMultitaskingSupported) {
__block UIBackgroundTaskIdentifier backgroundTaskID = UIBackgroundTaskInvalid;
backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
NSLog(@"Background task to backup WebSQL/LocalStorage expired.");
}];
CDVLocalStorage __weak* weakSelf = self;
[self.commandDelegate runInBackground:^{
[weakSelf backup:nil];
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
}];
}
}
- (void)onAppTerminate
{
[self onResignActive];
}
- (void)onReset
{
[self restore:nil];
}
@end
#pragma mark -
#pragma mark CDVBackupInfo implementation
@implementation CDVBackupInfo
@synthesize original, backup, label;
- (BOOL)file:(NSString*)aPath isNewerThanFile:(NSString*)bPath
{
NSFileManager* fileManager = [NSFileManager defaultManager];
NSError* __autoreleasing error = nil;
NSDictionary* aPathAttribs = [fileManager attributesOfItemAtPath:aPath error:&error];
NSDictionary* bPathAttribs = [fileManager attributesOfItemAtPath:bPath error:&error];
NSDate* aPathModDate = [aPathAttribs objectForKey:NSFileModificationDate];
NSDate* bPathModDate = [bPathAttribs objectForKey:NSFileModificationDate];
if ((nil == aPathModDate) && (nil == bPathModDate)) {
return NO;
}
return [aPathModDate compare:bPathModDate] == NSOrderedDescending || bPathModDate == nil;
}
- (BOOL)item:(NSString*)aPath isNewerThanItem:(NSString*)bPath
{
NSFileManager* fileManager = [NSFileManager defaultManager];
BOOL aPathIsDir = NO, bPathIsDir = NO;
BOOL aPathExists = [fileManager fileExistsAtPath:aPath isDirectory:&aPathIsDir];
[fileManager fileExistsAtPath:bPath isDirectory:&bPathIsDir];
if (!aPathExists) {
return NO;
}
if (!(aPathIsDir && bPathIsDir)) { // just a file
return [self file:aPath isNewerThanFile:bPath];
}
// essentially we want rsync here, but have to settle for our poor man's implementation
// we get the files in aPath, and see if it is newer than the file in bPath
// (it is newer if it doesn't exist in bPath) if we encounter the FIRST file that is newer,
// we return YES
NSDirectoryEnumerator* directoryEnumerator = [fileManager enumeratorAtPath:aPath];
NSString* path;
while ((path = [directoryEnumerator nextObject])) {
NSString* aPathFile = [aPath stringByAppendingPathComponent:path];
NSString* bPathFile = [bPath stringByAppendingPathComponent:path];
BOOL isNewer = [self file:aPathFile isNewerThanFile:bPathFile];
if (isNewer) {
return YES;
}
}
return NO;
}
- (BOOL)shouldBackup
{
return [self item:self.original isNewerThanItem:self.backup];
}
- (BOOL)shouldRestore
{
return [self item:self.backup isNewerThanItem:self.original];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CDVPluginResult.h"
#import "NSMutableArray+QueueAdditions.h"
#import "CDVCommandDelegate.h"
extern NSString* const CDVPageDidLoadNotification;
extern NSString* const CDVPluginHandleOpenURLNotification;
extern NSString* const CDVPluginResetNotification;
extern NSString* const CDVLocalNotification;
extern NSString* const CDVRemoteNotification;
extern NSString* const CDVRemoteNotificationError;
@interface CDVPlugin : NSObject {}
@property (nonatomic, weak) UIWebView* webView;
@property (nonatomic, weak) UIViewController* viewController;
@property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate;
@property (readonly, assign) BOOL hasPendingOperation;
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView CDV_DEPRECATED(3.9.2, "Use pluginInitialize method instead. This will be removed in 4.0.0");
- (void)pluginInitialize;
- (void)handleOpenURL:(NSNotification*)notification;
- (void)onAppTerminate;
- (void)onMemoryWarning;
- (void)onReset;
- (void)dispose;
/*
// see initWithWebView implementation
- (void) onPause {}
- (void) onResume {}
- (void) onOrientationWillChange {}
- (void) onOrientationDidChange {}
- (void)didReceiveLocalNotification:(NSNotification *)notification;
*/
- (id)appDelegate;
- (NSString*)writeJavascript:(NSString*)javascript CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of evalJs:. This will be removed in 4.0.0");
- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of sendPluginResult:callbackId. This will be removed in 4.0.0");
- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate equivalent of sendPluginResult:callbackId. This will be removed in 4.0.0");
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPlugin.h"
NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification";
NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification";
NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification";
NSString* const CDVLocalNotification = @"CDVLocalNotification";
NSString* const CDVRemoteNotification = @"CDVRemoteNotification";
NSString* const CDVRemoteNotificationError = @"CDVRemoteNotificationError";
@interface CDVPlugin ()
@property (readwrite, assign) BOOL hasPendingOperation;
@end
@implementation CDVPlugin
@synthesize webView, viewController, commandDelegate, hasPendingOperation;
// Do not override these methods. Use pluginInitialize instead.
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings
{
return [self initWithWebView:theWebView];
}
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:theWebView];
self.webView = theWebView;
}
return self;
}
- (void)pluginInitialize
{
// You can listen to more app notifications, see:
// http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
// NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
// Added in 2.3.0
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil];
// Added in 2.5.0
// [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];
}
- (void)dispose
{
viewController = nil;
commandDelegate = nil;
webView = nil;
}
/*
// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts
- (void) onPause {}
- (void) onResume {}
- (void) onOrientationWillChange {}
- (void) onOrientationDidChange {}
*/
/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
- (void)handleOpenURL:(NSNotification*)notification
{
// override to handle urls sent to your app
// register your url schemes in your App-Info.plist
NSURL* url = [notification object];
if ([url isKindOfClass:[NSURL class]]) {
/* Do your thing! */
}
}
/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
- (void)onAppTerminate
{
// override this if you need to do any cleanup on app exit
}
- (void)onMemoryWarning
{
// override to remove caches, etc
}
- (void)onReset
{
// Override to cancel any long-running requests when the WebView navigates or refreshes.
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock:
}
- (id)appDelegate
{
return [[UIApplication sharedApplication] delegate];
}
- (NSString*)writeJavascript:(NSString*)javascript
{
return [self.webView stringByEvaluatingJavaScriptFromString:javascript];
}
- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId
{
[self.commandDelegate evalJs:[pluginResult toSuccessCallbackString:callbackId]];
return @"";
}
- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId
{
[self.commandDelegate evalJs:[pluginResult toErrorCallbackString:callbackId]];
return @"";
}
// default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything.
// - (void)didReceiveLocalNotification:(NSNotification *)notification
// {
// // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification
// }
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVAvailability.h"
typedef enum {
CDVCommandStatus_NO_RESULT = 0,
CDVCommandStatus_OK,
CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION,
CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION,
CDVCommandStatus_INSTANTIATION_EXCEPTION,
CDVCommandStatus_MALFORMED_URL_EXCEPTION,
CDVCommandStatus_IO_EXCEPTION,
CDVCommandStatus_INVALID_ACTION,
CDVCommandStatus_JSON_EXCEPTION,
CDVCommandStatus_ERROR
} CDVCommandStatus;
@interface CDVPluginResult : NSObject {}
@property (nonatomic, strong, readonly) NSNumber* status;
@property (nonatomic, strong, readonly) id message;
@property (nonatomic, strong) NSNumber* keepCallback;
// This property can be used to scope the lifetime of another object. For example,
// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy.
@property (nonatomic, strong) id associatedObject;
- (CDVPluginResult*)init;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;
+ (void)setVerbose:(BOOL)verbose;
+ (BOOL)isVerbose;
- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback;
- (NSString*)argumentsAsJSON;
// These methods are used by the legacy plugin return result method
- (NSString*)toJSONString CDV_DEPRECATED(3.6, "Only used by toSuccessCallbackString and toErrorCallbackString which are deprecated. This will be removed in 4.0.0");
- (NSString*)toSuccessCallbackString:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate method sendPluginResult:callbackId instead. This will be removed in 4.0.0");
- (NSString*)toErrorCallbackString:(NSString*)callbackId CDV_DEPRECATED(3.6, "Use the CDVCommandDelegate method sendPluginResult:callbackId instead. This will be removed in 4.0.0");
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVPluginResult.h"
#import "CDVJSON_private.h"
#import "CDVDebug.h"
#import "NSData+Base64.h"
@interface CDVPluginResult ()
- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage;
@end
@implementation CDVPluginResult
@synthesize status, message, keepCallback, associatedObject;
static NSArray* org_apache_cordova_CommandStatusMsgs;
id messageFromArrayBuffer(NSData* data)
{
return @{
@"CDVType" : @"ArrayBuffer",
@"data" :[data cdv_base64EncodedString]
};
}
id massageMessage(id message)
{
if ([message isKindOfClass:[NSData class]]) {
return messageFromArrayBuffer(message);
}
return message;
}
id messageFromMultipart(NSArray* theMessages)
{
NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages];
for (NSUInteger i = 0; i < messages.count; ++i) {
[messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])];
}
return @{
@"CDVType" : @"MultiPart",
@"messages" : messages
};
}
+ (void)initialize
{
org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result",
@"OK",
@"Class not found",
@"Illegal access",
@"Instantiation error",
@"Malformed url",
@"IO error",
@"Invalid action",
@"JSON error",
@"Error",
nil];
}
- (CDVPluginResult*)init
{
return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil];
}
- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage
{
self = [super init];
if (self) {
status = [NSNumber numberWithInt:statusOrdinal];
message = theMessage;
keepCallback = [NSNumber numberWithBool:NO];
}
return self;
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal
{
return [[self alloc] initWithStatus:statusOrdinal message:nil];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages
{
return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)];
}
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode
{
NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]};
return [[self alloc] initWithStatus:statusOrdinal message:errDict];
}
- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback
{
[self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]];
}
- (NSString*)argumentsAsJSON
{
id arguments = (self.message == nil ? [NSNull null] : self.message);
NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments];
NSString* argumentsJSON = [argumentsWrappedInArray cdv_JSONString];
argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)];
return argumentsJSON;
}
// These methods are used by the legacy plugin return result method
- (NSString*)toJSONString
{
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
self.status, @"status",
self.message ? self. message:[NSNull null], @"message",
self.keepCallback, @"keepCallback",
nil];
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:NSJSONWritingPrettyPrinted
error:&error];
NSString* resultString = nil;
if (error != nil) {
NSLog(@"toJSONString error: %@", [error localizedDescription]);
} else {
resultString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
if ([[self class] isVerbose]) {
NSLog(@"PluginResult:toJSONString - %@", resultString);
}
return resultString;
}
- (NSString*)toSuccessCallbackString:(NSString*)callbackId
{
NSString* successCB = [NSString stringWithFormat:@"cordova.callbackSuccess('%@',%@);", callbackId, [self toJSONString]];
if ([[self class] isVerbose]) {
NSLog(@"PluginResult toSuccessCallbackString: %@", successCB);
}
return successCB;
}
- (NSString*)toErrorCallbackString:(NSString*)callbackId
{
NSString* errorCB = [NSString stringWithFormat:@"cordova.callbackError('%@',%@);", callbackId, [self toJSONString]];
if ([[self class] isVerbose]) {
NSLog(@"PluginResult toErrorCallbackString: %@", errorCB);
}
return errorCB;
}
static BOOL gIsVerbose = NO;
+ (void)setVerbose:(BOOL)verbose
{
gIsVerbose = verbose;
}
+ (BOOL)isVerbose
{
return gIsVerbose;
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@protocol CDVScreenOrientationDelegate <NSObject>
- (NSUInteger)supportedInterfaceOrientations;
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (BOOL)shouldAutorotate;
@end
/*
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.
*/
// This file was emptied out in 3.6.0 release (July 2014).
// It will be deleted in a future release.
#import <CoreLocation/CoreLocation.h>
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface CDVTimer : NSObject
+ (void)start:(NSString*)name;
+ (void)stop:(NSString*)name;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVTimer.h"
#pragma mark CDVTimerItem
@interface CDVTimerItem : NSObject
@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSDate* started;
@property (nonatomic, strong) NSDate* ended;
- (void)log;
@end
@implementation CDVTimerItem
- (void)log
{
NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0);
}
@end
#pragma mark CDVTimer
@interface CDVTimer ()
@property (nonatomic, strong) NSMutableDictionary* items;
@end
@implementation CDVTimer
#pragma mark object methods
- (id)init
{
if (self = [super init]) {
self.items = [NSMutableDictionary dictionaryWithCapacity:6];
}
return self;
}
- (void)add:(NSString*)name
{
if ([self.items objectForKey:[name lowercaseString]] == nil) {
CDVTimerItem* item = [CDVTimerItem new];
item.name = name;
item.started = [NSDate new];
[self.items setObject:item forKey:[name lowercaseString]];
} else {
NSLog(@"Timer called '%@' already exists.", name);
}
}
- (void)remove:(NSString*)name
{
CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]];
if (item != nil) {
item.ended = [NSDate new];
[item log];
[self.items removeObjectForKey:[name lowercaseString]];
} else {
NSLog(@"Timer called '%@' does not exist.", name);
}
}
- (void)removeAll
{
[self.items removeAllObjects];
}
#pragma mark class methods
+ (void)start:(NSString*)name
{
[[CDVTimer sharedInstance] add:name];
}
+ (void)stop:(NSString*)name
{
[[CDVTimer sharedInstance] remove:name];
}
+ (void)clearAll
{
[[CDVTimer sharedInstance] removeAll];
}
+ (CDVTimer*)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static CDVTimer* _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVAvailability.h"
@class CDVViewController;
@interface CDVURLProtocol : NSURLProtocol {}
+ (void)registerViewController:(CDVViewController*)viewController;
+ (void)unregisterViewController:(CDVViewController*)viewController;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <AssetsLibrary/ALAsset.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AssetsLibrary/ALAssetsLibrary.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "CDVURLProtocol.h"
#import "CDVCommandQueue.h"
#import "CDVWhitelist.h"
#import "CDVViewController.h"
static CDVWhitelist* gWhitelist = nil;
// Contains a set of NSNumbers of addresses of controllers. It doesn't store
// the actual pointer to avoid retaining.
static NSMutableSet* gRegisteredControllers = nil;
NSString* const kCDVAssetsLibraryPrefixes = @"assets-library://";
// Returns the registered view controller that sent the given request.
// If the user-agent is not from a UIWebView, or if it's from an unregistered one,
// then nil is returned.
static CDVViewController *viewControllerForRequest(NSURLRequest* request)
{
// The exec bridge explicitly sets the VC address in a header.
// This works around the User-Agent not being set for file: URLs.
NSString* addrString = [request valueForHTTPHeaderField:@"vc"];
if (addrString == nil) {
NSString* userAgent = [request valueForHTTPHeaderField:@"User-Agent"];
if (userAgent == nil) {
return nil;
}
NSUInteger bracketLocation = [userAgent rangeOfString:@"(" options:NSBackwardsSearch].location;
if (bracketLocation == NSNotFound) {
return nil;
}
addrString = [userAgent substringFromIndex:bracketLocation + 1];
}
long long viewControllerAddress = [addrString longLongValue];
@synchronized(gRegisteredControllers) {
if (![gRegisteredControllers containsObject:[NSNumber numberWithLongLong:viewControllerAddress]]) {
return nil;
}
}
return (__bridge CDVViewController*)(void*)viewControllerAddress;
}
@implementation CDVURLProtocol
+ (void)registerPGHttpURLProtocol {}
+ (void)registerURLProtocol {}
// Called to register the URLProtocol, and to make it away of an instance of
// a ViewController.
+ (void)registerViewController:(CDVViewController*)viewController
{
if (gRegisteredControllers == nil) {
[NSURLProtocol registerClass:[CDVURLProtocol class]];
gRegisteredControllers = [[NSMutableSet alloc] initWithCapacity:8];
// The whitelist doesn't change, so grab the first one and store it.
gWhitelist = viewController.whitelist;
// Note that we grab the whitelist from the first viewcontroller for now - but this will change
// when we allow a registered viewcontroller to have its own whitelist (e.g InAppBrowser)
// Differentiating the requests will be through the 'vc' http header below as used for the js->objc bridge.
// The 'vc' value is generated by casting the viewcontroller object to a (long long) value (see CDVViewController::webViewDidFinishLoad)
if (gWhitelist == nil) {
NSLog(@"WARNING: NO whitelist has been set in CDVURLProtocol.");
}
}
@synchronized(gRegisteredControllers) {
[gRegisteredControllers addObject:[NSNumber numberWithLongLong:(long long)viewController]];
}
}
+ (void)unregisterViewController:(CDVViewController*)viewController
{
@synchronized(gRegisteredControllers) {
[gRegisteredControllers removeObject:[NSNumber numberWithLongLong:(long long)viewController]];
}
}
+ (BOOL)canInitWithRequest:(NSURLRequest*)theRequest
{
NSURL* theUrl = [theRequest URL];
CDVViewController* viewController = viewControllerForRequest(theRequest);
if ([[theUrl absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) {
return YES;
} else if (viewController != nil) {
if ([[theUrl path] isEqualToString:@"/!gap_exec"]) {
NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];
NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];
if (requestId == nil) {
NSLog(@"!cordova request missing rc header");
return NO;
}
BOOL hasCmds = [queuedCommandsJSON length] > 0;
if (hasCmds) {
SEL sel = @selector(enqueueCommandBatch:);
[viewController.commandQueue performSelectorOnMainThread:sel withObject:queuedCommandsJSON waitUntilDone:NO];
[viewController.commandQueue performSelectorOnMainThread:@selector(executePending) withObject:nil waitUntilDone:NO];
} else {
SEL sel = @selector(processXhrExecBridgePoke:);
[viewController.commandQueue performSelectorOnMainThread:sel withObject:[NSNumber numberWithInteger:[requestId integerValue]] waitUntilDone:NO];
}
// Returning NO here would be 20% faster, but it spams WebInspector's console with failure messages.
// If JS->Native bridge speed is really important for an app, they should use the iframe bridge.
// Returning YES here causes the request to come through canInitWithRequest two more times.
// For this reason, we return NO when cmds exist.
return !hasCmds;
}
// we only care about http and https connections.
// CORS takes care of http: trying to access file: URLs.
if ([gWhitelist schemeIsAllowed:[theUrl scheme]]) {
// if it FAILS the whitelist, we return TRUE, so we can fail the connection later
return ![gWhitelist URLIsAllowed:theUrl];
}
}
return NO;
}
+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
{
// NSLog(@"%@ received %@", self, NSStringFromSelector(_cmd));
return request;
}
- (void)startLoading
{
// NSLog(@"%@ received %@ - start", self, NSStringFromSelector(_cmd));
NSURL* url = [[self request] URL];
if ([[url path] isEqualToString:@"/!gap_exec"]) {
[self sendResponseWithResponseCode:200 data:nil mimeType:nil];
return;
} else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefixes]) {
ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
if (asset) {
// We have the asset! Get the data and send it along.
ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
Byte* buffer = (Byte*)malloc((unsigned long)[assetRepresentation size]);
NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:(NSUInteger)[assetRepresentation size] error:nil];
NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
[self sendResponseWithResponseCode:200 data:data mimeType:MIMEType];
} else {
// Retrieving the asset failed for some reason. Send an error.
[self sendResponseWithResponseCode:404 data:nil mimeType:nil];
}
};
ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
// Retrieving the asset failed for some reason. Send an error.
[self sendResponseWithResponseCode:401 data:nil mimeType:nil];
};
ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary assetForURL:url resultBlock:resultBlock failureBlock:failureBlock];
return;
}
NSString* body = [gWhitelist errorStringForURL:url];
[self sendResponseWithResponseCode:401 data:[body dataUsingEncoding:NSASCIIStringEncoding] mimeType:nil];
}
- (void)stopLoading
{
// do any cleanup here
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
{
return NO;
}
- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType
{
if (mimeType == nil) {
mimeType = @"text/plain";
}
NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] initWithURL:[[self request] URL] statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : mimeType}];
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
if (data != nil) {
[[self client] URLProtocol:self didLoadData:data];
}
[[self client] URLProtocolDidFinishLoading:self];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface CDVUserAgentUtil : NSObject
+ (NSString*)originalUserAgent;
+ (void)acquireLock:(void (^)(NSInteger lockToken))block;
+ (void)releaseLock:(NSInteger*)lockToken;
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVUserAgentUtil.h"
#import <UIKit/UIKit.h>
// #define VerboseLog NSLog
#define VerboseLog(...) do {} while (0)
static NSString* const kCdvUserAgentKey = @"Cordova-User-Agent";
static NSString* const kCdvUserAgentVersionKey = @"Cordova-User-Agent-Version";
static NSString* gOriginalUserAgent = nil;
static NSInteger gNextLockToken = 0;
static NSInteger gCurrentLockToken = 0;
static NSMutableArray* gPendingSetUserAgentBlocks = nil;
@implementation CDVUserAgentUtil
+ (NSString*)originalUserAgent
{
if (gOriginalUserAgent == nil) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppLocaleDidChange:)
name:NSCurrentLocaleDidChangeNotification object:nil];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
NSString* localeStr = [[NSLocale currentLocale] localeIdentifier];
// Record the model since simulator can change it without re-install (CB-5420).
NSString* model = [UIDevice currentDevice].model;
NSString* systemAndLocale = [NSString stringWithFormat:@"%@ %@ %@", model, systemVersion, localeStr];
NSString* cordovaUserAgentVersion = [userDefaults stringForKey:kCdvUserAgentVersionKey];
gOriginalUserAgent = [userDefaults stringForKey:kCdvUserAgentKey];
BOOL cachedValueIsOld = ![systemAndLocale isEqualToString:cordovaUserAgentVersion];
if ((gOriginalUserAgent == nil) || cachedValueIsOld) {
UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
gOriginalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
[userDefaults setObject:gOriginalUserAgent forKey:kCdvUserAgentKey];
[userDefaults setObject:systemAndLocale forKey:kCdvUserAgentVersionKey];
[userDefaults synchronize];
}
}
return gOriginalUserAgent;
}
+ (void)onAppLocaleDidChange:(NSNotification*)notification
{
// TODO: We should figure out how to update the user-agent of existing UIWebViews when this happens.
// Maybe use the PDF bug (noted in setUserAgent:).
gOriginalUserAgent = nil;
}
+ (void)acquireLock:(void (^)(NSInteger lockToken))block
{
if (gCurrentLockToken == 0) {
gCurrentLockToken = ++gNextLockToken;
VerboseLog(@"Gave lock %d", gCurrentLockToken);
block(gCurrentLockToken);
} else {
if (gPendingSetUserAgentBlocks == nil) {
gPendingSetUserAgentBlocks = [[NSMutableArray alloc] initWithCapacity:4];
}
VerboseLog(@"Waiting for lock");
[gPendingSetUserAgentBlocks addObject:block];
}
}
+ (void)releaseLock:(NSInteger*)lockToken
{
if (*lockToken == 0) {
return;
}
NSAssert(gCurrentLockToken == *lockToken, @"Got token %ld, expected %ld", (long)*lockToken, (long)gCurrentLockToken);
VerboseLog(@"Released lock %d", *lockToken);
if ([gPendingSetUserAgentBlocks count] > 0) {
void (^block)() = [gPendingSetUserAgentBlocks objectAtIndex:0];
[gPendingSetUserAgentBlocks removeObjectAtIndex:0];
gCurrentLockToken = ++gNextLockToken;
NSLog(@"Gave lock %ld", (long)gCurrentLockToken);
block(gCurrentLockToken);
} else {
gCurrentLockToken = 0;
}
*lockToken = 0;
}
+ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken
{
NSAssert(gCurrentLockToken == lockToken, @"Got token %ld, expected %ld", (long)lockToken, (long)gCurrentLockToken);
VerboseLog(@"User-Agent set to: %@", value);
// Setting the UserAgent must occur before a UIWebView is instantiated.
// It is read per instantiation, so it does not affect previously created views.
// Except! When a PDF is loaded, all currently active UIWebViews reload their
// User-Agent from the NSUserDefaults some time after the DidFinishLoad of the PDF bah!
NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:value, @"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import <Foundation/NSJSONSerialization.h>
#import "CDVAvailability.h"
#import "CDVInvokedUrlCommand.h"
#import "CDVCommandDelegate.h"
#import "CDVCommandQueue.h"
#import "CDVWhitelist.h"
#import "CDVScreenOrientationDelegate.h"
#import "CDVPlugin.h"
@interface CDVViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{
@protected
id <CDVCommandDelegate> _commandDelegate;
@protected
CDVCommandQueue* _commandQueue;
NSString* _userAgent;
}
@property (nonatomic, strong) IBOutlet UIWebView* webView;
@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readonly, strong) NSDictionary* pluginsMap;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSXMLParser* configParser;
@property (nonatomic, readonly, strong) CDVWhitelist* whitelist CDV_DEPRECATED(3.9.2, "Use URLisAllowed to check specific URL. This will be removed in 4.0.0"); // readonly for public
@property (nonatomic, readonly, assign) BOOL loadFromString CDV_DEPRECATED(3.9.2, "This will be removed in 4.0.0");
@property (nonatomic, readwrite, copy) NSString* wwwFolderName;
@property (nonatomic, readwrite, copy) NSString* startPage;
@property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue;
@property (nonatomic, readonly, strong) id <CDVCommandDelegate> commandDelegate;
/**
The complete user agent that Cordova will use when sending web requests.
*/
@property (nonatomic, readonly) NSString* userAgent;
/**
The base user agent data that Cordova will use to build its user agent. If this
property isn't set, Cordova will use the standard web view user agent as its
base.
*/
@property (nonatomic, readwrite, copy) NSString* baseUserAgent;
+ (NSDictionary*)getBundlePlist:(NSString*)plistName CDV_DEPRECATED(3.9.2, "This will be removed in 4.0.0");
+ (NSString*)applicationDocumentsDirectory CDV_DEPRECATED(3.9.2, "This will be removed in 4.0.0");
- (void)printMultitaskingInfo CDV_DEPRECATED(3.9.2, "This will be removed in 4.0.0");
- (void)createGapView CDV_DEPRECATED(3.9.2, "This will be removed in 4.0.0");
- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds;
- (void)javascriptAlert:(NSString*)text CDV_DEPRECATED(3.9.2, "Use the CDVCommandDelegate evalJs: directly. This will be removed in 4.0.0");
- (NSString*)appURLScheme;
- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations;
- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation;
- (id)getCommandInstance:(NSString*)pluginName;
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className;
- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName;
- (BOOL)URLisAllowed:(NSURL*)url;
- (void)processOpenUrl:(NSURL*)url;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <objc/message.h>
#import "CDV.h"
#import "CDVCommandDelegateImpl.h"
#import "CDVConfigParser.h"
#import "CDVUserAgentUtil.h"
#import "CDVWebViewDelegate.h"
#import <AVFoundation/AVFoundation.h>
#import "CDVHandleOpenURL.h"
#define degreesToRadian(x) (M_PI * (x) / 180.0)
@interface CDVViewController () {
NSInteger _userAgentLockToken;
CDVWebViewDelegate* _webViewDelegate;
}
@property (nonatomic, readwrite, strong) NSXMLParser* configParser;
@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) CDVWhitelist* whitelist;
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readwrite, strong) NSArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
@property (nonatomic, readwrite, strong) NSArray* supportedOrientations;
@property (nonatomic, readwrite, assign) BOOL loadFromString;
@property (readwrite, assign) BOOL initialized;
@property (atomic, strong) NSURL* openURL;
@end
@implementation CDVViewController
@synthesize webView, supportedOrientations;
@synthesize pluginObjects, pluginsMap, whitelist, startupPluginNames;
@synthesize configParser, settings, loadFromString;
@synthesize wwwFolderName, startPage, initialized, openURL, baseUserAgent;
@synthesize commandDelegate = _commandDelegate;
@synthesize commandQueue = _commandQueue;
- (void)__init
{
if ((self != nil) && !self.initialized) {
_commandQueue = [[CDVCommandQueue alloc] initWithViewController:self];
_commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:)
name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:)
name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPageDidLoad:)
name:CDVPageDidLoadNotification object:nil];
// read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist
self.supportedOrientations = [self parseInterfaceOrientations:
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]];
[self printVersion];
[self printMultitaskingInfo];
[self printPlatformVersionWarning];
self.initialized = YES;
// load config.xml settings
[self loadSettings];
}
}
- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
[self __init];
return self;
}
- (id)initWithCoder:(NSCoder*)aDecoder
{
self = [super initWithCoder:aDecoder];
[self __init];
return self;
}
- (id)init
{
self = [super init];
[self __init];
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)printVersion
{
NSLog(@"Apache Cordova native platform version %@ is starting.", CDV_VERSION);
}
- (void)printPlatformVersionWarning
{
if (!IsAtLeastiOSVersion(@"6.0")) {
NSLog(@"CRITICAL: For Cordova 3.5.0 and above, you will need to upgrade to at least iOS 6.0 or greater. Your current version of iOS is %@.",
[[UIDevice currentDevice] systemVersion]
);
}
}
- (void)printMultitaskingInfo
{
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:@selector(isMultitaskingSupported)]) {
backgroundSupported = device.multitaskingSupported;
}
NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"];
if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default)
exitsOnSuspend = [NSNumber numberWithBool:NO];
}
NSLog(@"Multi-tasking -> Device: %@, App: %@", (backgroundSupported ? @"YES" : @"NO"), (![exitsOnSuspend intValue]) ? @"YES" : @"NO");
}
- (BOOL)URLisAllowed:(NSURL*)url
{
if (self.whitelist == nil) {
return YES;
}
return [self.whitelist URLIsAllowed:url];
}
- (void)loadSettings
{
CDVConfigParser* delegate = [[CDVConfigParser alloc] init];
// read from config.xml in the app bundle
NSString* path = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"xml"];
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSAssert(NO, @"ERROR: config.xml does not exist. Please run cordova-ios/bin/cordova_plist_to_config_xml path/to/project.");
return;
}
NSURL* url = [NSURL fileURLWithPath:path];
configParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
if (configParser == nil) {
NSLog(@"Failed to initialize XML parser.");
return;
}
[configParser setDelegate:((id < NSXMLParserDelegate >)delegate)];
[configParser parse];
// Get the plugin dictionary, whitelist and settings from the delegate.
self.pluginsMap = delegate.pluginsDict;
self.startupPluginNames = delegate.startupPluginNames;
self.whitelist = [[CDVWhitelist alloc] initWithArray:delegate.whitelistHosts];
self.settings = delegate.settings;
// And the start folder/page.
self.wwwFolderName = @"www";
self.startPage = delegate.startPage;
if (self.startPage == nil) {
self.startPage = @"index.html";
}
// Initialize the plugin objects dict.
self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
}
- (NSURL*)appUrl
{
NSURL* appURL = nil;
if ([self.startPage rangeOfString:@"://"].location != NSNotFound) {
appURL = [NSURL URLWithString:self.startPage];
} else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]];
} else {
// CB-3005 strip parameters from start page to check if page exists in resources
NSURL* startURL = [NSURL URLWithString:self.startPage];
NSString* startFilePath = [self.commandDelegate pathForResource:[startURL path]];
if (startFilePath == nil) {
self.loadFromString = YES;
appURL = nil;
} else {
appURL = [NSURL fileURLWithPath:startFilePath];
// CB-3005 Add on the query params or fragment.
NSString* startPageNoParentDirs = self.startPage;
NSRange r = [startPageNoParentDirs rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"?#"] options:0];
if (r.location != NSNotFound) {
NSString* queryAndOrFragment = [self.startPage substringFromIndex:r.location];
appURL = [NSURL URLWithString:queryAndOrFragment relativeToURL:appURL];
}
}
}
return appURL;
}
- (NSURL*)errorUrl
{
NSURL* errorURL = nil;
id setting = [self settingForKey:@"ErrorUrl"];
if (setting) {
NSString* errorUrlString = (NSString*)setting;
if ([errorUrlString rangeOfString:@"://"].location != NSNotFound) {
errorURL = [NSURL URLWithString:errorUrlString];
} else {
NSURL* url = [NSURL URLWithString:(NSString*)setting];
NSString* errorFilePath = [self.commandDelegate pathForResource:[url path]];
if (errorFilePath) {
errorURL = [NSURL fileURLWithPath:errorFilePath];
}
}
}
return errorURL;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
// // Fix the iOS 5.1 SECURITY_ERR bug (CB-347), this must be before the webView is instantiated ////
NSString* backupWebStorageType = @"cloud"; // default value
id backupWebStorage = [self settingForKey:@"BackupWebStorage"];
if ([backupWebStorage isKindOfClass:[NSString class]]) {
backupWebStorageType = backupWebStorage;
}
[self setSetting:backupWebStorageType forKey:@"BackupWebStorage"];
if (IsAtLeastiOSVersion(@"5.1")) {
[CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType];
}
// // Instantiate the WebView ///////////////
if (!self.webView) {
[self createGapView];
}
// Configure WebView
_webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
self.webView.delegate = _webViewDelegate;
// register this viewcontroller with the NSURLProtocol, only after the User-Agent is set
[CDVURLProtocol registerViewController:self];
// /////////////////
NSString* enableViewportScale = [self settingForKey:@"EnableViewportScale"];
NSNumber* allowInlineMediaPlayback = [self settingForKey:@"AllowInlineMediaPlayback"];
BOOL mediaPlaybackRequiresUserAction = YES; // default value
if ([self settingForKey:@"MediaPlaybackRequiresUserAction"]) {
mediaPlaybackRequiresUserAction = [(NSNumber*)[self settingForKey:@"MediaPlaybackRequiresUserAction"] boolValue];
}
self.webView.scalesPageToFit = [enableViewportScale boolValue];
/*
* Fire up CDVLocalStorage to work-around WebKit storage limitations: on all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for cloud backup.
*/
if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) ||
([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion(@"6.0")))) {
[self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])];
}
/*
* This is for iOS 4.x, where you can allow inline <video> and <audio>, and also autoplay them
*/
if ([allowInlineMediaPlayback boolValue] && [self.webView respondsToSelector:@selector(allowsInlineMediaPlayback)]) {
self.webView.allowsInlineMediaPlayback = YES;
}
if ((mediaPlaybackRequiresUserAction == NO) && [self.webView respondsToSelector:@selector(mediaPlaybackRequiresUserAction)]) {
self.webView.mediaPlaybackRequiresUserAction = NO;
}
// By default, overscroll bouncing is allowed.
// UIWebViewBounce has been renamed to DisallowOverscroll, but both are checked.
BOOL bounceAllowed = YES;
NSNumber* disallowOverscroll = [self settingForKey:@"DisallowOverscroll"];
if (disallowOverscroll == nil) {
NSNumber* bouncePreference = [self settingForKey:@"UIWebViewBounce"];
bounceAllowed = (bouncePreference == nil || [bouncePreference boolValue]);
} else {
bounceAllowed = ![disallowOverscroll boolValue];
}
// prevent webView from bouncing
// based on the DisallowOverscroll/UIWebViewBounce key in config.xml
if (!bounceAllowed) {
if ([self.webView respondsToSelector:@selector(scrollView)]) {
((UIScrollView*)[self.webView scrollView]).bounces = NO;
} else {
for (id subview in self.webView.subviews) {
if ([[subview class] isSubclassOfClass:[UIScrollView class]]) {
((UIScrollView*)subview).bounces = NO;
}
}
}
}
NSString* decelerationSetting = [self settingForKey:@"UIWebViewDecelerationSpeed"];
if (![@"fast" isEqualToString:decelerationSetting]) {
[self.webView.scrollView setDecelerationRate:UIScrollViewDecelerationRateNormal];
}
/*
* iOS 6.0 UIWebView properties
*/
if (IsAtLeastiOSVersion(@"6.0")) {
BOOL keyboardDisplayRequiresUserAction = YES; // KeyboardDisplayRequiresUserAction - defaults to YES
if ([self settingForKey:@"KeyboardDisplayRequiresUserAction"] != nil) {
if ([self settingForKey:@"KeyboardDisplayRequiresUserAction"]) {
keyboardDisplayRequiresUserAction = [(NSNumber*)[self settingForKey:@"KeyboardDisplayRequiresUserAction"] boolValue];
}
}
// property check for compiling under iOS < 6
if ([self.webView respondsToSelector:@selector(setKeyboardDisplayRequiresUserAction:)]) {
[self.webView setValue:[NSNumber numberWithBool:keyboardDisplayRequiresUserAction] forKey:@"keyboardDisplayRequiresUserAction"];
}
BOOL suppressesIncrementalRendering = NO; // SuppressesIncrementalRendering - defaults to NO
if ([self settingForKey:@"SuppressesIncrementalRendering"] != nil) {
if ([self settingForKey:@"SuppressesIncrementalRendering"]) {
suppressesIncrementalRendering = [(NSNumber*)[self settingForKey:@"SuppressesIncrementalRendering"] boolValue];
}
}
// property check for compiling under iOS < 6
if ([self.webView respondsToSelector:@selector(setSuppressesIncrementalRendering:)]) {
[self.webView setValue:[NSNumber numberWithBool:suppressesIncrementalRendering] forKey:@"suppressesIncrementalRendering"];
}
}
/*
* iOS 7.0 UIWebView properties
*/
if (IsAtLeastiOSVersion(@"7.0")) {
SEL ios7sel = nil;
id prefObj = nil;
CGFloat gapBetweenPages = 0.0; // default
prefObj = [self settingForKey:@"GapBetweenPages"];
if (prefObj != nil) {
gapBetweenPages = [prefObj floatValue];
}
// property check for compiling under iOS < 7
ios7sel = NSSelectorFromString(@"setGapBetweenPages:");
if ([self.webView respondsToSelector:ios7sel]) {
[self.webView setValue:[NSNumber numberWithFloat:gapBetweenPages] forKey:@"gapBetweenPages"];
}
CGFloat pageLength = 0.0; // default
prefObj = [self settingForKey:@"PageLength"];
if (prefObj != nil) {
pageLength = [[self settingForKey:@"PageLength"] floatValue];
}
// property check for compiling under iOS < 7
ios7sel = NSSelectorFromString(@"setPageLength:");
if ([self.webView respondsToSelector:ios7sel]) {
[self.webView setValue:[NSNumber numberWithBool:pageLength] forKey:@"pageLength"];
}
NSInteger paginationBreakingMode = 0; // default - UIWebPaginationBreakingModePage
prefObj = [self settingForKey:@"PaginationBreakingMode"];
if (prefObj != nil) {
NSArray* validValues = @[@"page", @"column"];
NSString* prefValue = [validValues objectAtIndex:0];
if ([prefObj isKindOfClass:[NSString class]]) {
prefValue = prefObj;
}
paginationBreakingMode = [validValues indexOfObject:[prefValue lowercaseString]];
if (paginationBreakingMode == NSNotFound) {
paginationBreakingMode = 0;
}
}
// property check for compiling under iOS < 7
ios7sel = NSSelectorFromString(@"setPaginationBreakingMode:");
if ([self.webView respondsToSelector:ios7sel]) {
[self.webView setValue:[NSNumber numberWithInteger:paginationBreakingMode] forKey:@"paginationBreakingMode"];
}
NSInteger paginationMode = 0; // default - UIWebPaginationModeUnpaginated
prefObj = [self settingForKey:@"PaginationMode"];
if (prefObj != nil) {
NSArray* validValues = @[@"unpaginated", @"lefttoright", @"toptobottom", @"bottomtotop", @"righttoleft"];
NSString* prefValue = [validValues objectAtIndex:0];
if ([prefObj isKindOfClass:[NSString class]]) {
prefValue = prefObj;
}
paginationMode = [validValues indexOfObject:[prefValue lowercaseString]];
if (paginationMode == NSNotFound) {
paginationMode = 0;
}
}
// property check for compiling under iOS < 7
ios7sel = NSSelectorFromString(@"setPaginationMode:");
if ([self.webView respondsToSelector:ios7sel]) {
[self.webView setValue:[NSNumber numberWithInteger:paginationMode] forKey:@"paginationMode"];
}
}
if ([self.startupPluginNames count] > 0) {
[CDVTimer start:@"TotalPluginStartup"];
for (NSString* pluginName in self.startupPluginNames) {
[CDVTimer start:pluginName];
[self getCommandInstance:pluginName];
[CDVTimer stop:pluginName];
}
[CDVTimer stop:@"TotalPluginStartup"];
}
[self registerPlugin:[[CDVHandleOpenURL alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVHandleOpenURL class])];
// /////////////////
NSURL* appURL = [self appUrl];
[CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
_userAgentLockToken = lockToken;
[CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
if (appURL) {
NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
[self.webView loadRequest:appReq];
} else {
NSString* loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage];
NSLog(@"%@", loadErr);
NSURL* errorUrl = [self errorUrl];
if (errorUrl) {
errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [loadErr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl];
NSLog(@"%@", [errorUrl absoluteString]);
[self.webView loadRequest:[NSURLRequest requestWithURL:errorUrl]];
} else {
NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
[self.webView loadHTMLString:html baseURL:nil];
}
}
}];
}
- (id)settingForKey:(NSString*)key
{
return [[self settings] objectForKey:[key lowercaseString]];
}
- (void)setSetting:(id)setting forKey:(NSString*)key
{
[[self settings] setObject:setting forKey:[key lowercaseString]];
}
- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
{
NSMutableArray* result = [[NSMutableArray alloc] init];
if (orientations != nil) {
NSEnumerator* enumerator = [orientations objectEnumerator];
NSString* orientationString;
while (orientationString = [enumerator nextObject]) {
if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
} else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]];
} else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]];
} else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]];
}
}
}
// default
if ([result count] == 0) {
[result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
}
return result;
}
- (NSInteger)mapIosOrientationToJsOrientation:(UIInterfaceOrientation)orientation
{
switch (orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
return 180;
case UIInterfaceOrientationLandscapeLeft:
return -90;
case UIInterfaceOrientationLandscapeRight:
return 90;
case UIInterfaceOrientationPortrait:
return 0;
default:
return 0;
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// First, ask the webview via JS if it supports the new orientation
NSString* jsCall = [NSString stringWithFormat:
@"window.shouldRotateToOrientation && window.shouldRotateToOrientation(%ld);"
, (long)[self mapIosOrientationToJsOrientation:interfaceOrientation]];
NSString* res = [webView stringByEvaluatingJavaScriptFromString:jsCall];
if ([res length] > 0) {
return [res boolValue];
}
// if js did not handle the new orientation (no return value), use values from the plist (via supportedOrientations)
return [self supportsOrientation:interfaceOrientation];
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger ret = 0;
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) {
ret = ret | (1 << UIInterfaceOrientationPortrait);
}
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown);
}
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight]) {
ret = ret | (1 << UIInterfaceOrientationLandscapeRight);
}
if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft]) {
ret = ret | (1 << UIInterfaceOrientationLandscapeLeft);
}
return ret;
}
- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation
{
return [self.supportedOrientations containsObject:[NSNumber numberWithInt:orientation]];
}
- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds
{
return [[UIWebView alloc] initWithFrame:bounds];
}
- (NSString*)userAgent
{
if (_userAgent != nil) {
return _userAgent;
}
NSString* localBaseUserAgent;
if (self.baseUserAgent != nil) {
localBaseUserAgent = self.baseUserAgent;
} else if ([self settingForKey:@"OverrideUserAgent"] != nil) {
localBaseUserAgent = [self settingForKey:@"OverrideUserAgent"];
} else {
localBaseUserAgent = [CDVUserAgentUtil originalUserAgent];
}
NSString* appendUserAgent = [self settingForKey:@"AppendUserAgent"];
if (appendUserAgent) {
_userAgent = [NSString stringWithFormat:@"%@ %@", localBaseUserAgent, appendUserAgent];
} else {
// Use our address as a unique number to append to the User-Agent.
_userAgent = [NSString stringWithFormat:@"%@ (%lld)", localBaseUserAgent, (long long)self];
}
return _userAgent;
}
- (void)createGapView
{
CGRect webViewBounds = self.view.bounds;
webViewBounds.origin = self.view.bounds.origin;
self.webView = [self newCordovaViewWithFrame:webViewBounds];
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[self.view addSubview:self.webView];
[self.view sendSubviewToBack:self.webView];
}
- (void)didReceiveMemoryWarning
{
// iterate through all the plugin objects, and call hasPendingOperation
// if at least one has a pending operation, we don't call [super didReceiveMemoryWarning]
NSEnumerator* enumerator = [self.pluginObjects objectEnumerator];
CDVPlugin* plugin;
BOOL doPurge = YES;
while ((plugin = [enumerator nextObject])) {
if (plugin.hasPendingOperation) {
NSLog(@"Plugin '%@' has a pending operation, memory purge is delayed for didReceiveMemoryWarning.", NSStringFromClass([plugin class]));
doPurge = NO;
}
}
if (doPurge) {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.webView.delegate = nil;
self.webView = nil;
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
[super viewDidUnload];
}
#pragma mark UIWebViewDelegate
/**
When web application loads Add stuff to the DOM, mainly the user-defined settings from the Settings.plist file, and
the device's data such as device ID, platform version, etc.
*/
- (void)webViewDidStartLoad:(UIWebView*)theWebView
{
NSLog(@"Resetting plugins due to page load.");
[_commandQueue resetRequestId];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:self.webView]];
}
/**
Called when the webview finishes loading. This stops the activity view.
*/
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
NSLog(@"Finished load of: %@", theWebView.request.URL);
// It's safe to release the lock even if this is just a sub-frame that's finished loading.
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
/*
* Hide the Top Activity THROBBER in the Battery Bar
*/
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:self.webView]];
}
- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
{
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
NSString* message = [NSString stringWithFormat:@"Failed to load webpage with error: %@", [error localizedDescription]];
NSLog(@"%@", message);
NSURL* errorUrl = [self errorUrl];
if (errorUrl) {
errorUrl = [NSURL URLWithString:[NSString stringWithFormat:@"?error=%@", [message stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]] relativeToURL:errorUrl];
NSLog(@"%@", [errorUrl absoluteString]);
[theWebView loadRequest:[NSURLRequest requestWithURL:errorUrl]];
}
}
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL* url = [request URL];
/*
* Execute any commands queued with cordova.exec() on the JS side.
* The part of the URL after gap:// is irrelevant.
*/
if ([[url scheme] isEqualToString:@"gap"]) {
[_commandQueue fetchCommandsFromJs];
// The delegate is called asynchronously in this case, so we don't have to use
// flushCommandQueueWithDelayedJs (setTimeout(0)) as we do with hash changes.
[_commandQueue executePending];
return NO;
}
if ([[url fragment] hasPrefix:@"%01"] || [[url fragment] hasPrefix:@"%02"]) {
// Delegate is called *immediately* for hash changes. This means that any
// calls to stringByEvaluatingJavascriptFromString will occur in the middle
// of an existing (paused) call stack. This doesn't cause errors, but may
// be unexpected to callers (exec callbacks will be called before exec() even
// returns). To avoid this, we do not do any synchronous JS evals by using
// flushCommandQueueWithDelayedJs.
NSString* inlineCommands = [[url fragment] substringFromIndex:3];
if ([inlineCommands length] == 0) {
// Reach in right away since the WebCore / Main thread are already synchronized.
[_commandQueue fetchCommandsFromJs];
} else {
inlineCommands = [inlineCommands stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[_commandQueue enqueueCommandBatch:inlineCommands];
}
// Switch these for minor performance improvements, and to really live on the wild side.
// Callbacks will occur in the middle of the location.hash = ... statement!
[(CDVCommandDelegateImpl*)_commandDelegate flushCommandQueueWithDelayedJs];
// [_commandQueue executePending];
// Although we return NO, the hash change does end up taking effect.
return NO;
}
/*
* Give plugins the chance to handle the url
*/
for (NSString* pluginName in pluginObjects) {
CDVPlugin* plugin = [pluginObjects objectForKey:pluginName];
SEL selector = NSSelectorFromString(@"shouldOverrideLoadWithRequest:navigationType:");
if ([plugin respondsToSelector:selector]) {
if (((BOOL (*)(id, SEL, id, int))objc_msgSend)(plugin, selector, request, navigationType) == YES) {
return NO;
}
}
}
/*
* If a URL is being loaded that's a file/http/https URL, just load it internally
*/
if ([url isFileURL]) {
return YES;
}
/*
* If we loaded the HTML from a string, we let the app handle it
*/
else if (self.loadFromString == YES) {
self.loadFromString = NO;
return YES;
}
/*
* all tel: scheme urls we let the UIWebview handle it using the default behavior
*/
else if ([[url scheme] isEqualToString:@"tel"]) {
return YES;
}
/*
* all about: scheme urls are not handled
*/
else if ([[url scheme] isEqualToString:@"about"]) {
return NO;
}
/*
* all data: scheme urls are handled
*/
else if ([[url scheme] isEqualToString:@"data"]) {
return YES;
}
/*
* Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
*/
else {
if ([self.whitelist schemeIsAllowed:[url scheme]]) {
return [self.whitelist URLIsAllowed:url];
} else {
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
} else { // handle any custom schemes to plugins
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
}
}
return NO;
}
return YES;
}
#pragma mark GapHelpers
- (void)javascriptAlert:(NSString*)text
{
NSString* jsString = [NSString stringWithFormat:@"alert('%@');", text];
[self.commandDelegate evalJs:jsString];
}
+ (NSString*)applicationDocumentsDirectory
{
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* basePath = (([paths count] > 0) ? ([paths objectAtIndex : 0]) : nil);
return basePath;
}
#pragma mark CordovaCommands
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className
{
if ([plugin respondsToSelector:@selector(setViewController:)]) {
[plugin setViewController:self];
}
if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
[plugin setCommandDelegate:_commandDelegate];
}
[self.pluginObjects setObject:plugin forKey:className];
[plugin pluginInitialize];
}
- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName
{
if ([plugin respondsToSelector:@selector(setViewController:)]) {
[plugin setViewController:self];
}
if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
[plugin setCommandDelegate:_commandDelegate];
}
NSString* className = NSStringFromClass([plugin class]);
[self.pluginObjects setObject:plugin forKey:className];
[self.pluginsMap setValue:className forKey:[pluginName lowercaseString]];
[plugin pluginInitialize];
}
/**
Returns an instance of a CordovaCommand object, based on its name. If one exists already, it is returned.
*/
- (id)getCommandInstance:(NSString*)pluginName
{
// first, we try to find the pluginName in the pluginsMap
// (acts as a whitelist as well) if it does not exist, we return nil
// NOTE: plugin names are matched as lowercase to avoid problems - however, a
// possible issue is there can be duplicates possible if you had:
// "org.apache.cordova.Foo" and "org.apache.cordova.foo" - only the lower-cased entry will match
NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]];
if (className == nil) {
return nil;
}
id obj = [self.pluginObjects objectForKey:className];
if (!obj) {
obj = [[NSClassFromString(className)alloc] initWithWebView:webView];
if (obj != nil) {
[self registerPlugin:obj withClassName:className];
} else {
NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName);
}
}
return obj;
}
#pragma mark -
- (NSString*)appURLScheme
{
NSString* URLScheme = nil;
NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"];
if (URLTypes != nil) {
NSDictionary* dict = [URLTypes objectAtIndex:0];
if (dict != nil) {
NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"];
if (URLSchemes != nil) {
URLScheme = [URLSchemes objectAtIndex:0];
}
}
}
return URLScheme;
}
/**
Returns the contents of the named plist bundle, loaded as a dictionary object
*/
+ (NSDictionary*)getBundlePlist:(NSString*)plistName
{
NSString* errorDesc = nil;
NSPropertyListFormat format;
NSString* plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"];
NSData* plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSDictionary* temp = (NSDictionary*)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format errorDescription:&errorDesc];
return temp;
}
#pragma mark -
#pragma mark UIApplicationDelegate impl
/*
This method lets your application know that it is about to be terminated and purged from memory entirely
*/
- (void)onAppWillTerminate:(NSNotification*)notification
{
// empty the tmp directory
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError* __autoreleasing err = nil;
// clear contents of NSTemporaryDirectory
NSString* tempDirectoryPath = NSTemporaryDirectory();
NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
NSString* fileName = nil;
BOOL result;
while ((fileName = [directoryEnumerator nextObject])) {
NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
result = [fileMgr removeItemAtPath:filePath error:&err];
if (!result && err) {
NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
}
}
}
/*
This method is called to let your application know that it is about to move from the active to inactive state.
You should use this method to pause ongoing tasks, disable timer, ...
*/
- (void)onAppWillResignActive:(NSNotification*)notification
{
// NSLog(@"%@",@"applicationWillResignActive");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resign');" scheduledOnRunLoop:NO];
}
/*
In iOS 4.0 and later, this method is called as part of the transition from the background to the inactive state.
You can use this method to undo many of the changes you made to your application upon entering the background.
invariably followed by applicationDidBecomeActive
*/
- (void)onAppWillEnterForeground:(NSNotification*)notification
{
// NSLog(@"%@",@"applicationWillEnterForeground");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resume');"];
/** Clipboard fix **/
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSString *string = pasteboard.string;
if (string) {
[pasteboard setValue:string forPasteboardType:@"public.text"];
}
}
// This method is called to let your application know that it moved from the inactive to active state.
- (void)onAppDidBecomeActive:(NSNotification*)notification
{
// NSLog(@"%@",@"applicationDidBecomeActive");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('active');"];
}
/*
In iOS 4.0 and later, this method is called instead of the applicationWillTerminate: method
when the user quits an application that supports background execution.
*/
- (void)onAppDidEnterBackground:(NSNotification*)notification
{
// NSLog(@"%@",@"applicationDidEnterBackground");
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO];
}
// ///////////////////////
- (void)onPageDidLoad:(NSNotification*)notification
{
if (self.openURL) {
[self processOpenUrl:self.openURL pageLoaded:YES];
self.openURL = nil;
}
}
- (void)processOpenUrl:(NSURL*)url pageLoaded:(BOOL)pageLoaded
{
if (!pageLoaded) {
// query the webview for readystate
NSString* readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
pageLoaded = [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
}
if (pageLoaded) {
// calls into javascript global function 'handleOpenURL'
NSString* jsString = [NSString stringWithFormat:@"if (typeof handleOpenURL === 'function') { handleOpenURL(\"%@\");}", url];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
} else {
// save for when page has loaded
self.openURL = url;
}
}
- (void)processOpenUrl:(NSURL*)url
{
[self processOpenUrl:url pageLoaded:NO];
}
// ///////////////////////
- (void)dealloc
{
[CDVURLProtocol unregisterViewController:self];
[[NSNotificationCenter defaultCenter] removeObserver:self];
self.webView.delegate = nil;
self.webView = nil;
[CDVUserAgentUtil releaseLock:&_userAgentLockToken];
[_commandQueue dispose];
[[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import "CDVAvailability.h"
/**
* Distinguishes top-level navigations from sub-frame navigations.
* shouldStartLoadWithRequest is called for every request, but didStartLoad
* and didFinishLoad is called only for top-level navigations.
* Relevant bug: CB-2389
*/
@interface CDVWebViewDelegate : NSObject <UIWebViewDelegate>{
__weak NSObject <UIWebViewDelegate>* _delegate;
NSInteger _loadCount;
NSInteger _state;
NSInteger _curLoadToken;
NSInteger _loadStartPollCount;
}
- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate;
- (BOOL)request:(NSURLRequest*)newRequest isEqualToRequestAfterStrippingFragments:(NSURLRequest*)originalRequest;
@end
/*
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.
*/
//
// Testing shows:
//
// In all cases, webView.request.URL is the previous page's URL (or empty) during the didStartLoad callback.
// When loading a page with a redirect:
// 1. shouldStartLoading (requestURL is target page)
// 2. didStartLoading
// 3. shouldStartLoading (requestURL is redirect target)
// 4. didFinishLoad (request.URL is redirect target)
//
// Note the lack of a second didStartLoading **
//
// When loading a page with iframes:
// 1. shouldStartLoading (requestURL is main page)
// 2. didStartLoading
// 3. shouldStartLoading (requestURL is one of the iframes)
// 4. didStartLoading
// 5. didFinishLoad
// 6. didFinishLoad
//
// Note there is no way to distinguish which didFinishLoad maps to which didStartLoad **
//
// Loading a page by calling window.history.go(-1):
// 1. didStartLoading
// 2. didFinishLoad
//
// Note the lack of a shouldStartLoading call **
// Actually - this is fixed on iOS6. iOS6 has a shouldStart. **
//
// Loading a page by calling location.reload()
// 1. shouldStartLoading
// 2. didStartLoading
// 3. didFinishLoad
//
// Loading a page with an iframe that fails to load:
// 1. shouldStart (main page)
// 2. didStart
// 3. shouldStart (iframe)
// 4. didStart
// 5. didFailWithError
// 6. didFinish
//
// Loading a page with an iframe that fails to load due to an invalid URL:
// 1. shouldStart (main page)
// 2. didStart
// 3. shouldStart (iframe)
// 5. didFailWithError
// 6. didFinish
//
// This case breaks our logic since there is a missing didStart. To prevent this,
// we check URLs in shouldStart and return NO if they are invalid.
//
// Loading a page with an invalid URL
// 1. shouldStart (main page)
// 2. didFailWithError
//
// TODO: Record order when page is re-navigated before the first navigation finishes.
//
#import "CDVWebViewDelegate.h"
#import "CDVAvailability.h"
// #define VerboseLog NSLog
#define VerboseLog(...) do {} while (0)
typedef enum {
STATE_IDLE = 0,
STATE_WAITING_FOR_LOAD_START = 1,
STATE_WAITING_FOR_LOAD_FINISH = 2,
STATE_IOS5_POLLING_FOR_LOAD_START = 3,
STATE_IOS5_POLLING_FOR_LOAD_FINISH = 4,
STATE_CANCELLED = 5
} State;
static NSString *stripFragment(NSString* url)
{
NSRange r = [url rangeOfString:@"#"];
if (r.location == NSNotFound) {
return url;
}
return [url substringToIndex:r.location];
}
@implementation CDVWebViewDelegate
- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate
{
self = [super init];
if (self != nil) {
_delegate = delegate;
_loadCount = -1;
_state = STATE_IDLE;
}
return self;
}
- (BOOL)request:(NSURLRequest*)newRequest isEqualToRequestAfterStrippingFragments:(NSURLRequest*)originalRequest
{
if (originalRequest.URL && newRequest.URL) {
NSString* originalRequestUrl = [originalRequest.URL absoluteString];
NSString* newRequestUrl = [newRequest.URL absoluteString];
NSString* baseOriginalRequestUrl = stripFragment(originalRequestUrl);
NSString* baseNewRequestUrl = stripFragment(newRequestUrl);
return [baseOriginalRequestUrl isEqualToString:baseNewRequestUrl];
}
return NO;
}
- (BOOL)isPageLoaded:(UIWebView*)webView
{
NSString* readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
return [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
}
- (BOOL)isJsLoadTokenSet:(UIWebView*)webView
{
NSString* loadToken = [webView stringByEvaluatingJavaScriptFromString:@"window.__cordovaLoadToken"];
return [[NSString stringWithFormat:@"%ld", (long)_curLoadToken] isEqualToString:loadToken];
}
- (void)setLoadToken:(UIWebView*)webView
{
_curLoadToken += 1;
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.__cordovaLoadToken=%ld", (long)_curLoadToken]];
}
- (NSString*)evalForCurrentURL:(UIWebView*)webView
{
return [webView stringByEvaluatingJavaScriptFromString:@"location.href"];
}
- (void)pollForPageLoadStart:(UIWebView*)webView
{
if (_state != STATE_IOS5_POLLING_FOR_LOAD_START) {
return;
}
if (![self isJsLoadTokenSet:webView]) {
VerboseLog(@"Polled for page load start. result = YES!");
_state = STATE_IOS5_POLLING_FOR_LOAD_FINISH;
[self setLoadToken:webView];
if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[_delegate webViewDidStartLoad:webView];
}
[self pollForPageLoadFinish:webView];
} else {
VerboseLog(@"Polled for page load start. result = NO");
// Poll only for 1 second, and then fall back on checking only when delegate methods are called.
++_loadStartPollCount;
if (_loadStartPollCount < (1000 * .05)) {
[self performSelector:@selector(pollForPageLoadStart:) withObject:webView afterDelay:.05];
}
}
}
- (void)pollForPageLoadFinish:(UIWebView*)webView
{
if (_state != STATE_IOS5_POLLING_FOR_LOAD_FINISH) {
return;
}
if ([self isPageLoaded:webView]) {
VerboseLog(@"Polled for page load finish. result = YES!");
_state = STATE_IDLE;
if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[_delegate webViewDidFinishLoad:webView];
}
} else {
VerboseLog(@"Polled for page load finish. result = NO");
[self performSelector:@selector(pollForPageLoadFinish:) withObject:webView afterDelay:.05];
}
}
- (BOOL)shouldLoadRequest:(NSURLRequest*)request
{
NSString* scheme = [[request URL] scheme];
if ([scheme isEqualToString:@"mailto"] || [scheme isEqualToString:@"tel"] || [scheme isEqualToString:@"sms"] || [scheme isEqualToString:@"blob"]) {
return YES;
}
return [NSURLConnection canHandleRequest:request];
}
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
BOOL shouldLoad = YES;
if ([_delegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
}
VerboseLog(@"webView shouldLoad=%d (before) state=%d loadCount=%d URL=%@", shouldLoad, _state, _loadCount, request.URL);
if (shouldLoad) {
// When devtools refresh occurs, it blindly uses the same request object. If a history.replaceState() has occured, then
// mainDocumentURL != URL even though it's a top-level navigation.
BOOL isDevToolsRefresh = (request == webView.request);
BOOL isTopLevelNavigation = isDevToolsRefresh || [request.URL isEqual:[request mainDocumentURL]];
if (isTopLevelNavigation) {
// Ignore hash changes that don't navigate to a different page.
// webView.request does actually update when history.replaceState() gets called.
if ([self request:request isEqualToRequestAfterStrippingFragments:webView.request]) {
NSString* prevURL = [self evalForCurrentURL:webView];
if ([prevURL isEqualToString:[request.URL absoluteString]]) {
VerboseLog(@"Page reload detected.");
} else {
VerboseLog(@"Detected hash change shouldLoad");
return shouldLoad;
}
}
switch (_state) {
case STATE_WAITING_FOR_LOAD_FINISH:
// Redirect case.
// We expect loadCount == 1.
if (_loadCount != 1) {
NSLog(@"CDVWebViewDelegate: Detected redirect when loadCount=%ld", (long)_loadCount);
}
break;
case STATE_IDLE:
case STATE_IOS5_POLLING_FOR_LOAD_START:
case STATE_CANCELLED:
// Page navigation start.
_loadCount = 0;
_state = STATE_WAITING_FOR_LOAD_START;
break;
default:
{
NSString* description = [NSString stringWithFormat:@"CDVWebViewDelegate: Navigation started when state=%ld", (long)_state];
NSLog(@"%@", description);
_loadCount = 0;
_state = STATE_WAITING_FOR_LOAD_START;
if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
NSDictionary* errorDictionary = @{NSLocalizedDescriptionKey : description};
NSError* error = [[NSError alloc] initWithDomain:@"CDVWebViewDelegate" code:1 userInfo:errorDictionary];
[_delegate webView:webView didFailLoadWithError:error];
}
}
}
} else {
// Deny invalid URLs so that we don't get the case where we go straight from
// webViewShouldLoad -> webViewDidFailLoad (messes up _loadCount).
shouldLoad = shouldLoad && [self shouldLoadRequest:request];
}
VerboseLog(@"webView shouldLoad=%d (after) isTopLevelNavigation=%d state=%d loadCount=%d", shouldLoad, isTopLevelNavigation, _state, _loadCount);
}
return shouldLoad;
}
- (void)webViewDidStartLoad:(UIWebView*)webView
{
VerboseLog(@"webView didStartLoad (before). state=%d loadCount=%d", _state, _loadCount);
BOOL fireCallback = NO;
switch (_state) {
case STATE_IDLE:
if (IsAtLeastiOSVersion(@"6.0")) {
break;
}
// If history.go(-1) is used pre-iOS6, the shouldStartLoadWithRequest function is not called.
// Without shouldLoad, we can't distinguish an iframe from a top-level navigation.
// We could try to distinguish using [UIWebView canGoForward], but that's too much complexity,
// and would work only on the first time it was used.
// Our work-around is to set a JS variable and poll until it disappears (from a navigation).
_state = STATE_IOS5_POLLING_FOR_LOAD_START;
_loadStartPollCount = 0;
[self setLoadToken:webView];
[self pollForPageLoadStart:webView];
break;
case STATE_CANCELLED:
fireCallback = YES;
_state = STATE_WAITING_FOR_LOAD_FINISH;
_loadCount += 1;
break;
case STATE_WAITING_FOR_LOAD_START:
if (_loadCount != 0) {
NSLog(@"CDVWebViewDelegate: Unexpected loadCount in didStart. count=%ld", (long)_loadCount);
}
fireCallback = YES;
_state = STATE_WAITING_FOR_LOAD_FINISH;
_loadCount = 1;
break;
case STATE_WAITING_FOR_LOAD_FINISH:
_loadCount += 1;
break;
case STATE_IOS5_POLLING_FOR_LOAD_START:
[self pollForPageLoadStart:webView];
break;
case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
[self pollForPageLoadFinish:webView];
break;
default:
NSLog(@"CDVWebViewDelegate: Unexpected didStart with state=%ld loadCount=%ld", (long)_state, (long)_loadCount);
}
VerboseLog(@"webView didStartLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
[_delegate webViewDidStartLoad:webView];
}
}
- (void)webViewDidFinishLoad:(UIWebView*)webView
{
VerboseLog(@"webView didFinishLoad (before). state=%d loadCount=%d", _state, _loadCount);
BOOL fireCallback = NO;
switch (_state) {
case STATE_IDLE:
break;
case STATE_WAITING_FOR_LOAD_START:
NSLog(@"CDVWebViewDelegate: Unexpected didFinish while waiting for load start.");
break;
case STATE_WAITING_FOR_LOAD_FINISH:
if (_loadCount == 1) {
fireCallback = YES;
_state = STATE_IDLE;
}
_loadCount -= 1;
break;
case STATE_IOS5_POLLING_FOR_LOAD_START:
[self pollForPageLoadStart:webView];
break;
case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
[self pollForPageLoadFinish:webView];
break;
}
VerboseLog(@"webView didFinishLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[_delegate webViewDidFinishLoad:webView];
}
}
- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
{
VerboseLog(@"webView didFailLoad (before). state=%d loadCount=%d", _state, _loadCount);
BOOL fireCallback = NO;
switch (_state) {
case STATE_IDLE:
break;
case STATE_WAITING_FOR_LOAD_START:
if ([error code] == NSURLErrorCancelled) {
_state = STATE_CANCELLED;
} else {
_state = STATE_IDLE;
}
fireCallback = YES;
break;
case STATE_WAITING_FOR_LOAD_FINISH:
if ([error code] != NSURLErrorCancelled) {
if (_loadCount == 1) {
_state = STATE_IDLE;
fireCallback = YES;
}
_loadCount = -1;
} else {
fireCallback = YES;
_state = STATE_CANCELLED;
_loadCount -= 1;
}
break;
case STATE_IOS5_POLLING_FOR_LOAD_START:
[self pollForPageLoadStart:webView];
break;
case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
[self pollForPageLoadFinish:webView];
break;
}
VerboseLog(@"webView didFailLoad (after). state=%d loadCount=%d, fireCallback=%d", _state, _loadCount, fireCallback);
if (fireCallback && [_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
[_delegate webView:webView didFailLoadWithError:error];
}
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
extern NSString* const kCDVDefaultWhitelistRejectionString;
@interface CDVWhitelist : NSObject
@property (nonatomic, copy) NSString* whitelistRejectionFormatString;
- (id)initWithArray:(NSArray*)array;
- (BOOL)schemeIsAllowed:(NSString*)scheme;
- (BOOL)URLIsAllowed:(NSURL*)url;
- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure;
- (NSString*)errorStringForURL:(NSURL*)url;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CDVWhitelist.h"
NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme";
@interface CDVWhitelistPattern : NSObject {
@private
NSRegularExpression* _scheme;
NSRegularExpression* _host;
NSNumber* _port;
NSRegularExpression* _path;
}
+ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards;
- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path;
- (bool)matches:(NSURL*)url;
@end
@implementation CDVWhitelistPattern
+ (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards
{
NSString* regex = [NSRegularExpression escapedPatternForString:pattern];
if (allowWildcards) {
regex = [regex stringByReplacingOccurrencesOfString:@"\\*" withString:@".*"];
/* [NSURL path] has the peculiarity that a trailing slash at the end of a path
* will be omitted. This regex tweak compensates for that.
*/
if ([regex hasSuffix:@"\\/.*"]) {
regex = [NSString stringWithFormat:@"%@(\\/.*)?", [regex substringToIndex:([regex length] - 4)]];
}
}
return [NSString stringWithFormat:@"%@$", regex];
}
- (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path
{
self = [super init]; // Potentially change "self"
if (self) {
if ((scheme == nil) || [scheme isEqualToString:@"*"]) {
_scheme = nil;
} else {
_scheme = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:scheme allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
}
if ([host isEqualToString:@"*"]) {
_host = nil;
} else if ([host hasPrefix:@"*."]) {
_host = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"([a-z0-9.-]*\\.)?%@", [CDVWhitelistPattern regexFromPattern:[host substringFromIndex:2] allowWildcards:false]] options:NSRegularExpressionCaseInsensitive error:nil];
} else {
_host = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:host allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
}
if ((port == nil) || [port isEqualToString:@"*"]) {
_port = nil;
} else {
_port = [[NSNumber alloc] initWithInteger:[port integerValue]];
}
if ((path == nil) || [path isEqualToString:@"/*"]) {
_path = nil;
} else {
_path = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:path allowWildcards:YES] options:0 error:nil];
}
}
return self;
}
- (bool)matches:(NSURL*)url
{
return (_scheme == nil || [_scheme numberOfMatchesInString:[url scheme] options:NSMatchingAnchored range:NSMakeRange(0, [[url scheme] length])]) &&
(_host == nil || [_host numberOfMatchesInString:[url host] options:NSMatchingAnchored range:NSMakeRange(0, [[url host] length])]) &&
(_port == nil || [[url port] isEqualToNumber:_port]) &&
(_path == nil || [_path numberOfMatchesInString:[url path] options:NSMatchingAnchored range:NSMakeRange(0, [[url path] length])])
;
}
@end
@interface CDVWhitelist ()
@property (nonatomic, readwrite, strong) NSMutableArray* whitelist;
@property (nonatomic, readwrite, strong) NSMutableSet* permittedSchemes;
- (void)addWhiteListEntry:(NSString*)pattern;
@end
@implementation CDVWhitelist
@synthesize whitelist, permittedSchemes, whitelistRejectionFormatString;
- (id)initWithArray:(NSArray*)array
{
self = [super init];
if (self) {
self.whitelist = [[NSMutableArray alloc] init];
self.permittedSchemes = [[NSMutableSet alloc] init];
self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;
for (NSString* pattern in array) {
[self addWhiteListEntry:pattern];
}
}
return self;
}
- (BOOL)isIPv4Address:(NSString*)externalHost
{
// an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255.
// for our purposes, b can also be the wildcard character '*'
// we could use a regex to solve this problem but then I would have two problems
// anyways, this is much clearer and maintainable
NSArray* octets = [externalHost componentsSeparatedByString:@"."];
NSUInteger num_octets = [octets count];
// quick check
if (num_octets != 4) {
return NO;
}
// restrict number parsing to 0-255
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]];
[numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]];
// iterate through each octet, and test for a number between 0-255 or if it equals '*'
for (NSUInteger i = 0; i < num_octets; ++i) {
NSString* octet = [octets objectAtIndex:i];
if ([octet isEqualToString:@"*"]) { // passes - check next octet
continue;
} else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return
return NO;
}
}
return YES;
}
- (void)addWhiteListEntry:(NSString*)origin
{
if (self.whitelist == nil) {
return;
}
if ([origin isEqualToString:@"*"]) {
NSLog(@"Unlimited access to network resources");
self.whitelist = nil;
self.permittedSchemes = nil;
} else { // specific access
NSRegularExpression* parts = [NSRegularExpression regularExpressionWithPattern:@"^((\\*|[A-Za-z-]+)://)?(((\\*\\.)?[^*/:]+)|\\*)?(:(\\d+))?(/.*)?" options:0 error:nil];
NSTextCheckingResult* m = [parts firstMatchInString:origin options:NSMatchingAnchored range:NSMakeRange(0, [origin length])];
if (m != nil) {
NSRange r;
NSString* scheme = nil;
r = [m rangeAtIndex:2];
if (r.location != NSNotFound) {
scheme = [origin substringWithRange:r];
}
NSString* host = nil;
r = [m rangeAtIndex:3];
if (r.location != NSNotFound) {
host = [origin substringWithRange:r];
}
// Special case for two urls which are allowed to have empty hosts
if (([scheme isEqualToString:@"file"] || [scheme isEqualToString:@"content"]) && (host == nil)) {
host = @"*";
}
NSString* port = nil;
r = [m rangeAtIndex:7];
if (r.location != NSNotFound) {
port = [origin substringWithRange:r];
}
NSString* path = nil;
r = [m rangeAtIndex:8];
if (r.location != NSNotFound) {
path = [origin substringWithRange:r];
}
if (scheme == nil) {
// XXX making it stupid friendly for people who forget to include protocol/SSL
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"http" host:host port:port path:path]];
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"https" host:host port:port path:path]];
} else {
[self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:scheme host:host port:port path:path]];
}
if (self.permittedSchemes != nil) {
if ([scheme isEqualToString:@"*"]) {
self.permittedSchemes = nil;
} else if (scheme != nil) {
[self.permittedSchemes addObject:scheme];
}
}
}
}
}
- (BOOL)schemeIsAllowed:(NSString*)scheme
{
if ([scheme isEqualToString:@"http"] ||
[scheme isEqualToString:@"https"] ||
[scheme isEqualToString:@"ftp"] ||
[scheme isEqualToString:@"ftps"]) {
return YES;
}
return (self.permittedSchemes == nil) || [self.permittedSchemes containsObject:scheme];
}
- (BOOL)URLIsAllowed:(NSURL*)url
{
return [self URLIsAllowed:url logFailure:YES];
}
- (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure
{
// Shortcut acceptance: Are all urls whitelisted ("*" in whitelist)?
if (whitelist == nil) {
return YES;
}
// Shortcut rejection: Check that the scheme is supported
NSString* scheme = [[url scheme] lowercaseString];
if (![self schemeIsAllowed:scheme]) {
if (logFailure) {
NSLog(@"%@", [self errorStringForURL:url]);
}
return NO;
}
// http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list
if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) {
NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@%@", kCDVDefaultSchemeName, [url host], [[url path] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
// If it is allowed, we are done. If not, continue to check for the actual scheme-specific list
if ([self URLIsAllowed:newUrl logFailure:NO]) {
return YES;
}
}
// Check the url against patterns in the whitelist
for (CDVWhitelistPattern* p in self.whitelist) {
if ([p matches:url]) {
return YES;
}
}
if (logFailure) {
NSLog(@"%@", [self errorStringForURL:url]);
}
// if we got here, the url host is not in the white-list, do nothing
return NO;
}
- (NSString*)errorStringForURL:(NSURL*)url
{
return [NSString stringWithFormat:self.whitelistRejectionFormatString, [url absoluteString]];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVAvailabilityDeprecated.h"
@interface NSArray (Comparisons)
- (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault CDV_DEPRECATED(3.8 .0, "Use [command argumentAtIndex] instead.");
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "NSArray+Comparisons.h"
@implementation NSArray (Comparisons)
- (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault
{
id obj = nil;
@try {
if (index < [self count]) {
obj = [self objectAtIndex:index];
}
if ((obj == [NSNull null]) || (obj == nil)) {
return aDefault;
}
}
@catch(NSException* exception) {
NSLog(@"Exception - Name: %@ Reason: %@", [exception name], [exception reason]);
}
return obj;
}
@end
//
// NSData+Base64.h
// base64
//
// Created by Matt Gallagher on 2009/06/03.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software. Permission is granted to anyone to
// use this software for any purpose, including commercial applications, and to
// alter it and redistribute it freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source
// distribution.
//
#import <Foundation/Foundation.h>
#import "CDVAvailabilityDeprecated.h"
void *CDVNewBase64Decode(
const char* inputBuffer,
size_t length,
size_t * outputLength);
char *CDVNewBase64Encode(
const void* inputBuffer,
size_t length,
bool separateLines,
size_t * outputLength);
@interface NSData (CDVBase64)
+ (NSData*)dataFromBase64String:(NSString*)aString CDV_DEPRECATED(3.8 .0, "Use cdv_dataFromBase64String");
- (NSString*)base64EncodedString CDV_DEPRECATED(3.8 .0, "Use [NSData cdv_base64EncodedString]");
+ (NSData*)cdv_dataFromBase64String:(NSString*)aString CDV_DEPRECATED(3.9.2, "Use NSData initWithBase64EncodedString instead. This will be removed in 4.0.0");
- (NSString*)cdv_base64EncodedString CDV_DEPRECATED(3.9.2, "Use NSData base64EncodedStringWithOptions instead. This will be removed in 4.0.0");
@end
//
// NSData+Base64.m
// base64
//
// Created by Matt Gallagher on 2009/06/03.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software. Permission is granted to anyone to
// use this software for any purpose, including commercial applications, and to
// alter it and redistribute it freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source
// distribution.
//
#import "NSData+Base64.h"
//
// Mapping from 6 bit pattern to ASCII character.
//
static unsigned char base64EncodeLookup[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//
// Definition for "masked-out" areas of the base64DecodeLookup mapping
//
#define xx 65
//
// Mapping from ASCII character to 6 bit pattern.
//
static unsigned char base64DecodeLookup[256] =
{
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx,
xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx,
xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
};
//
// Fundamental sizes of the binary and base64 encode/decode units in bytes
//
#define BINARY_UNIT_SIZE 3
#define BASE64_UNIT_SIZE 4
//
// NewBase64Decode
//
// Decodes the base64 ASCII string in the inputBuffer to a newly malloced
// output buffer.
//
// inputBuffer - the source ASCII string for the decode
// length - the length of the string or -1 (to specify strlen should be used)
// outputLength - if not-NULL, on output will contain the decoded length
//
// returns the decoded buffer. Must be free'd by caller. Length is given by
// outputLength.
//
void *CDVNewBase64Decode(
const char* inputBuffer,
size_t length,
size_t * outputLength)
{
if (length == -1) {
length = strlen(inputBuffer);
}
size_t outputBufferSize =
((length + BASE64_UNIT_SIZE - 1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE;
unsigned char* outputBuffer = (unsigned char*)malloc(outputBufferSize);
size_t i = 0;
size_t j = 0;
while (i < length) {
//
// Accumulate 4 valid characters (ignore everything else)
//
unsigned char accumulated[BASE64_UNIT_SIZE];
size_t accumulateIndex = 0;
while (i < length) {
unsigned char decode = base64DecodeLookup[inputBuffer[i++]];
if (decode != xx) {
accumulated[accumulateIndex] = decode;
accumulateIndex++;
if (accumulateIndex == BASE64_UNIT_SIZE) {
break;
}
}
}
//
// Store the 6 bits from each of the 4 characters as 3 bytes
//
// (Uses improved bounds checking suggested by Alexandre Colucci)
//
if (accumulateIndex >= 2) {
outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);
}
if (accumulateIndex >= 3) {
outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);
}
if (accumulateIndex >= 4) {
outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];
}
j += accumulateIndex - 1;
}
if (outputLength) {
*outputLength = j;
}
return outputBuffer;
}
//
// NewBase64Encode
//
// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced
// output buffer.
//
// inputBuffer - the source data for the encode
// length - the length of the input in bytes
// separateLines - if zero, no CR/LF characters will be added. Otherwise
// a CR/LF pair will be added every 64 encoded chars.
// outputLength - if not-NULL, on output will contain the encoded length
// (not including terminating 0 char)
//
// returns the encoded buffer. Must be free'd by caller. Length is given by
// outputLength.
//
char *CDVNewBase64Encode(
const void* buffer,
size_t length,
bool separateLines,
size_t * outputLength)
{
const unsigned char* inputBuffer = (const unsigned char*)buffer;
#define MAX_NUM_PADDING_CHARS 2
#define OUTPUT_LINE_LENGTH 64
#define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE)
#define CR_LF_SIZE 2
//
// Byte accurate calculation of final buffer size
//
size_t outputBufferSize =
((length / BINARY_UNIT_SIZE)
+ ((length % BINARY_UNIT_SIZE) ? 1 : 0))
* BASE64_UNIT_SIZE;
if (separateLines) {
outputBufferSize +=
(outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;
}
//
// Include space for a terminating zero
//
outputBufferSize += 1;
//
// Allocate the output buffer
//
char* outputBuffer = (char*)malloc(outputBufferSize);
if (!outputBuffer) {
return NULL;
}
size_t i = 0;
size_t j = 0;
const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;
size_t lineEnd = lineLength;
while (true) {
if (lineEnd > length) {
lineEnd = length;
}
for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) {
//
// Inner loop: turn 48 bytes into 64 base64 characters
//
outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
| ((inputBuffer[i + 1] & 0xF0) >> 4)];
outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)
| ((inputBuffer[i + 2] & 0xC0) >> 6)];
outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F];
}
if (lineEnd == length) {
break;
}
//
// Add the newline
//
// outputBuffer[j++] = '\r';
// outputBuffer[j++] = '\n';
lineEnd += lineLength;
}
if (i + 1 < length) {
//
// Handle the single '=' case
//
outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
| ((inputBuffer[i + 1] & 0xF0) >> 4)];
outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];
outputBuffer[j++] = '=';
} else if (i < length) {
//
// Handle the double '=' case
//
outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4];
outputBuffer[j++] = '=';
outputBuffer[j++] = '=';
}
outputBuffer[j] = 0;
//
// Set the output length and return the buffer
//
if (outputLength) {
*outputLength = j;
}
return outputBuffer;
}
@implementation NSData (CDVBase64)
//
// dataFromBase64String:
//
// Creates an NSData object containing the base64 decoded representation of
// the base64 string 'aString'
//
// Parameters:
// aString - the base64 string to decode
//
// returns the autoreleased NSData representation of the base64 string
//
+ (NSData*)cdv_dataFromBase64String:(NSString*)aString
{
size_t outputLength = 0;
void* outputBuffer = CDVNewBase64Decode([aString UTF8String], [aString length], &outputLength);
return [NSData dataWithBytesNoCopy:outputBuffer length:outputLength freeWhenDone:YES];
}
//
// base64EncodedString
//
// Creates an NSString object that contains the base 64 encoding of the
// receiver's data. Lines are broken at 64 characters long.
//
// returns an autoreleased NSString being the base 64 representation of the
// receiver.
//
- (NSString*)cdv_base64EncodedString
{
size_t outputLength = 0;
char* outputBuffer =
CDVNewBase64Encode([self bytes], [self length], true, &outputLength);
NSString* result = [[NSString alloc] initWithBytesNoCopy:outputBuffer
length:outputLength
encoding:NSASCIIStringEncoding
freeWhenDone:YES];
return result;
}
+ (NSData*)dataFromBase64String:(NSString*)aString
{
return [self cdv_dataFromBase64String:aString];
}
- (NSString*)base64EncodedString
{
return [self cdv_base64EncodedString];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVAvailabilityDeprecated.h"
@interface NSDictionary (org_apache_cordova_NSDictionary_Extension)
- (bool)existsValue:(NSString*)expectedValue forKey:(NSString*)key CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue withRange:(NSRange)range CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (BOOL)typeValueForKey:(NSString*)key isArray:(BOOL*)bArray isNull:(BOOL*)bNull isNumber:(BOOL*)bNumber isString:(BOOL*)bString CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (BOOL)valueForKeyIsArray:(NSString*)key CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (BOOL)valueForKeyIsNull:(NSString*)key CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (BOOL)valueForKeyIsString:(NSString*)key CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (BOOL)valueForKeyIsNumber:(NSString*)key CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
- (NSDictionary*)dictionaryWithLowercaseKeys CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "NSDictionary+Extensions.h"
#import <math.h>
@implementation NSDictionary (org_apache_cordova_NSDictionary_Extension)
- (bool)existsValue:(NSString*)expectedValue forKey:(NSString*)key
{
id val = [self valueForKey:key];
bool exists = false;
if (val != nil) {
exists = [(NSString*)val compare : expectedValue options : NSCaseInsensitiveSearch] == 0;
}
return exists;
}
- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue withRange:(NSRange)range
{
NSInteger value = defaultValue;
NSNumber* val = [self valueForKey:key]; // value is an NSNumber
if (val != nil) {
value = [val integerValue];
}
// min, max checks
value = MAX(range.location, value);
value = MIN(range.length, value);
return value;
}
- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue
{
NSInteger value = defaultValue;
NSNumber* val = [self valueForKey:key]; // value is an NSNumber
if (val != nil) {
value = [val integerValue];
}
return value;
}
/*
* Determine the type of object stored in a dictionary
* IN:
* (BOOL*) bString - if exists will be set to YES if object is an NSString, NO if not
* (BOOL*) bNull - if exists will be set to YES if object is an NSNull, NO if not
* (BOOL*) bArray - if exists will be set to YES if object is an NSArray, NO if not
* (BOOL*) bNumber - if exists will be set to YES if object is an NSNumber, NO if not
*
* OUT:
* YES if key exists
* NO if key does not exist. Input parameters remain untouched
*
*/
- (BOOL)typeValueForKey:(NSString*)key isArray:(BOOL*)bArray isNull:(BOOL*)bNull isNumber:(BOOL*)bNumber isString:(BOOL*)bString
{
BOOL bExists = YES;
NSObject* value = [self objectForKey:key];
if (value) {
bExists = YES;
if (bString) {
*bString = [value isKindOfClass:[NSString class]];
}
if (bNull) {
*bNull = [value isKindOfClass:[NSNull class]];
}
if (bArray) {
*bArray = [value isKindOfClass:[NSArray class]];
}
if (bNumber) {
*bNumber = [value isKindOfClass:[NSNumber class]];
}
}
return bExists;
}
- (BOOL)valueForKeyIsArray:(NSString*)key
{
BOOL bArray = NO;
NSObject* value = [self objectForKey:key];
if (value) {
bArray = [value isKindOfClass:[NSArray class]];
}
return bArray;
}
- (BOOL)valueForKeyIsNull:(NSString*)key
{
BOOL bNull = NO;
NSObject* value = [self objectForKey:key];
if (value) {
bNull = [value isKindOfClass:[NSNull class]];
}
return bNull;
}
- (BOOL)valueForKeyIsString:(NSString*)key
{
BOOL bString = NO;
NSObject* value = [self objectForKey:key];
if (value) {
bString = [value isKindOfClass:[NSString class]];
}
return bString;
}
- (BOOL)valueForKeyIsNumber:(NSString*)key
{
BOOL bNumber = NO;
NSObject* value = [self objectForKey:key];
if (value) {
bNumber = [value isKindOfClass:[NSNumber class]];
}
return bNumber;
}
- (NSDictionary*)dictionaryWithLowercaseKeys
{
NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:self.count];
NSString* key;
for (key in self) {
[result setObject:[self objectForKey:key] forKey:[key lowercaseString]];
}
return result;
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
@interface NSMutableArray (QueueAdditions)
- (id)pop;
- (id)queueHead;
- (id)dequeue;
- (void)enqueue:(id)obj;
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "NSMutableArray+QueueAdditions.h"
@implementation NSMutableArray (QueueAdditions)
- (id)queueHead
{
if ([self count] == 0) {
return nil;
}
return [self objectAtIndex:0];
}
- (__autoreleasing id)dequeue
{
if ([self count] == 0) {
return nil;
}
id head = [self objectAtIndex:0];
if (head != nil) {
// [[head retain] autorelease]; ARC - the __autoreleasing on the return value should so the same thing
[self removeObjectAtIndex:0];
}
return head;
}
- (id)pop
{
return [self dequeue];
}
- (void)enqueue:(id)object
{
[self addObject:object];
}
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <Foundation/Foundation.h>
#import "CDVAvailabilityDeprecated.h"
@interface UIDevice (org_apache_cordova_UIDevice_Extension)
/*
Get the unique identifier from the app bundle's folder, which is already a GUID
Upgrading and/or deleting the app and re-installing will get you a new GUID, so
this is only unique per install per device.
*/
- (NSString*)uniqueAppInstanceIdentifier CDV_DEPRECATED(3.8 .0, "API is slated for removal in 4.0.0");
@end
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
#import "UIDevice+Extensions.h"
@implementation UIDevice (org_apache_cordova_UIDevice_Extension)
- (NSString*)uniqueAppInstanceIdentifier
{
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
static NSString* UUID_KEY = @"CDVUUID";
NSString* app_uuid = [userDefaults stringForKey:UUID_KEY];
if (app_uuid == nil) {
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
app_uuid = [NSString stringWithString:(__bridge NSString*)uuidString];
[userDefaults setObject:app_uuid forKey:UUID_KEY];
[userDefaults synchronize];
CFRelease(uuidString);
CFRelease(uuidRef);
}
return app_uuid;
}
@end
...@@ -7,121 +7,120 @@ ...@@ -7,121 +7,120 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1B701028177A61CF00AE11F4 /* CDVShared.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B701026177A61CF00AE11F4 /* CDVShared.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30193A501AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 30193A4E1AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.m */; };
1F92F4A01314023E0046367C /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F92F49E1314023E0046367C /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30193A511AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 30193A4F1AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.h */; };
1F92F4A11314023E0046367C /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F92F49F1314023E0046367C /* CDVPluginResult.m */; }; 3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */; };
301F2F2A14F3C9CA003FE9FC /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 301F2F2914F3C9CA003FE9FC /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3093E2241B16D6A3003F381A /* CDVIntentAndNavigationFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */; };
302965BC13A94E9D007046C5 /* CDVDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 302965BB13A94E9D007046C5 /* CDVDebug.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7E7F69B61ABA35D8007546F4 /* CDVLocalStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CFB1AB9028C008C4574 /* CDVLocalStorage.h */; };
3034979C1513D56A0090E688 /* CDVLocalStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3034979A1513D56A0090E688 /* CDVLocalStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7E7F69B81ABA368F007546F4 /* CDVUIWebViewEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D001AB9028C008C4574 /* CDVUIWebViewEngine.h */; };
3034979E1513D56A0090E688 /* CDVLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3034979B1513D56A0090E688 /* CDVLocalStorage.m */; }; 7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */; };
30392E4E14F4FCAB00B9E0B8 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 30392E4D14F4FCAB00B9E0B8 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D021AB9028C008C4574 /* CDVDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF21AB9028C008C4574 /* CDVDebug.h */; };
3062D120151D0EDB000D9128 /* UIDevice+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3062D11E151D0EDB000D9128 /* UIDevice+Extensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D031AB9028C008C4574 /* CDVJSON_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */; };
3062D122151D0EDB000D9128 /* UIDevice+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3062D11F151D0EDB000D9128 /* UIDevice+Extensions.m */; }; 7ED95D041AB9028C008C4574 /* CDVJSON_private.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */; };
3073E9ED1656D51200957977 /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 3073E9EC1656D51200957977 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D051AB9028C008C4574 /* CDVPlugin+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */; };
30C684801406CB38004C1A8E /* CDVWhitelist.h in Headers */ = {isa = PBXBuildFile; fileRef = 30C6847E1406CB38004C1A8E /* CDVWhitelist.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D071AB9028C008C4574 /* CDVHandleOpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */; };
30C684821406CB38004C1A8E /* CDVWhitelist.m in Sources */ = {isa = PBXBuildFile; fileRef = 30C6847F1406CB38004C1A8E /* CDVWhitelist.m */; }; 7ED95D091AB9028C008C4574 /* CDVLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CFC1AB9028C008C4574 /* CDVLocalStorage.m */; };
30C684941407044B004C1A8E /* CDVURLProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 30C684921407044A004C1A8E /* CDVURLProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D0A1AB9028C008C4574 /* CDVUIWebViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95CFE1AB9028C008C4574 /* CDVUIWebViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
30C684961407044B004C1A8E /* CDVURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 30C684931407044A004C1A8E /* CDVURLProtocol.m */; }; 7ED95D0B1AB9028C008C4574 /* CDVUIWebViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95CFF1AB9028C008C4574 /* CDVUIWebViewDelegate.m */; };
30E33AF213A7E24B00594D64 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E33AF013A7E24B00594D64 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D0D1AB9028C008C4574 /* CDVUIWebViewEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D011AB9028C008C4574 /* CDVUIWebViewEngine.m */; };
30E33AF313A7E24B00594D64 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 30E33AF113A7E24B00594D64 /* CDVPlugin.m */; }; 7ED95D351AB9029B008C4574 /* CDV.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D0F1AB9029B008C4574 /* CDV.h */; settings = {ATTRIBUTES = (Public, ); }; };
30E563CF13E217EC00C949AA /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E563CD13E217EC00C949AA /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
30E563D013E217EC00C949AA /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 30E563CE13E217EC00C949AA /* NSMutableArray+QueueAdditions.m */; }; 7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */; };
30E6B8CD1A8ADD900025B9EE /* CDVHandleOpenURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 30E6B8CB1A8ADD900025B9EE /* CDVHandleOpenURL.h */; }; 7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D121AB9029B008C4574 /* CDVAvailability.h */; settings = {ATTRIBUTES = (Public, ); }; };
30E6B8CE1A8ADD900025B9EE /* CDVHandleOpenURL.m in Sources */ = {isa = PBXBuildFile; fileRef = 30E6B8CC1A8ADD900025B9EE /* CDVHandleOpenURL.m */; }; 7ED95D391AB9029B008C4574 /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; };
30F3930B169F839700B22307 /* CDVJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 30F39309169F839700B22307 /* CDVJSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D3A1AB9029B008C4574 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
30F3930C169F839700B22307 /* CDVJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 30F3930A169F839700B22307 /* CDVJSON.m */; }; 7ED95D3B1AB9029B008C4574 /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; };
30F5EBAB14CA26E700987760 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 30F5EBA914CA26E700987760 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */; };
7E14B5A81705050A0032169E /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E14B5A61705050A0032169E /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D3D1AB9029B008C4574 /* CDVCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
7E14B5A91705050A0032169E /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E14B5A71705050A0032169E /* CDVTimer.m */; }; 7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */; };
7E22B88519E4C0210026F95E /* CDVAvailabilityDeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E22B88419E4C0210026F95E /* CDVAvailabilityDeprecated.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D3F1AB9029B008C4574 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D191AB9029B008C4574 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; };
8852C43A14B65FD800F0E735 /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 8852C43614B65FD800F0E735 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D401AB9029B008C4574 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */; };
8852C43C14B65FD800F0E735 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8852C43714B65FD800F0E735 /* CDVViewController.m */; }; 7ED95D411AB9029B008C4574 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
8887FD681090FBE7009987E8 /* NSDictionary+Extensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD281090FBE7009987E8 /* NSDictionary+Extensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D421AB9029B008C4574 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */; };
8887FD691090FBE7009987E8 /* NSDictionary+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */; }; 7ED95D431AB9029B008C4574 /* CDVPlugin+Resources.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */; settings = {ATTRIBUTES = (Public, ); }; };
8887FD741090FBE7009987E8 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D441AB9029B008C4574 /* CDVPlugin+Resources.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */; };
8887FD751090FBE7009987E8 /* CDVInvokedUrlCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD351090FBE7009987E8 /* CDVInvokedUrlCommand.m */; }; 7ED95D451AB9029B008C4574 /* CDVPlugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */; settings = {ATTRIBUTES = (Public, ); }; };
8887FD8F1090FBE7009987E8 /* NSData+Base64.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD501090FBE7009987E8 /* NSData+Base64.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D461AB9029B008C4574 /* CDVPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D201AB9029B008C4574 /* CDVPlugin.m */; };
8887FD901090FBE7009987E8 /* NSData+Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD511090FBE7009987E8 /* NSData+Base64.m */; }; 7ED95D471AB9029B008C4574 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D211AB9029B008C4574 /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB3B3547161CB44D003DBE7D /* CDVCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = EB3B3545161CB44D003DBE7D /* CDVCommandQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D481AB9029B008C4574 /* CDVPluginResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D221AB9029B008C4574 /* CDVPluginResult.m */; };
EB3B3548161CB44D003DBE7D /* CDVCommandQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = EB3B3546161CB44D003DBE7D /* CDVCommandQueue.m */; }; 7ED95D491AB9029B008C4574 /* CDVScreenOrientationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB3B357C161F2A45003DBE7D /* CDVCommandDelegateImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = EB3B357A161F2A44003DBE7D /* CDVCommandDelegateImpl.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D4A1AB9029B008C4574 /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D241AB9029B008C4574 /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB3B357D161F2A45003DBE7D /* CDVCommandDelegateImpl.m in Sources */ = {isa = PBXBuildFile; fileRef = EB3B357B161F2A45003DBE7D /* CDVCommandDelegateImpl.m */; }; 7ED95D4B1AB9029B008C4574 /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D251AB9029B008C4574 /* CDVTimer.m */; };
EB6A98541A77EE470013FCDB /* CDVJSON_private.m in Sources */ = {isa = PBXBuildFile; fileRef = EB6A98521A77EE470013FCDB /* CDVJSON_private.m */; }; 7ED95D4C1AB9029B008C4574 /* CDVURLProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D261AB9029B008C4574 /* CDVURLProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
EB96673B16A8970A00D86CDF /* CDVUserAgentUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = EB96673916A8970900D86CDF /* CDVUserAgentUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D4D1AB9029B008C4574 /* CDVURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D271AB9029B008C4574 /* CDVURLProtocol.m */; };
EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */; }; 7ED95D4E1AB9029B008C4574 /* CDVUserAgentUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D281AB9029B008C4574 /* CDVUserAgentUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };
EBA3557315ABD38C00F4DE24 /* NSArray+Comparisons.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D4F1AB9029B008C4574 /* CDVUserAgentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D291AB9029B008C4574 /* CDVUserAgentUtil.m */; };
EBA3557515ABD38C00F4DE24 /* NSArray+Comparisons.m in Sources */ = {isa = PBXBuildFile; fileRef = EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */; }; 7ED95D501AB9029B008C4574 /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2A1AB9029B008C4574 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */; }; 7ED95D511AB9029B008C4574 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2B1AB9029B008C4574 /* CDVViewController.m */; };
EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D521AB9029B008C4574 /* CDVWebViewEngineProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
F858FBC6166009A8007DA594 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = F858FBC4166009A8007DA594 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7ED95D531AB9029B008C4574 /* CDVWhitelist.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */; settings = {ATTRIBUTES = (Public, ); }; };
F858FBC7166009A8007DA594 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F858FBC5166009A8007DA594 /* CDVConfigParser.m */; }; 7ED95D541AB9029B008C4574 /* CDVWhitelist.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */; };
7ED95D571AB9029B008C4574 /* NSDictionary+CordovaPreferences.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */; };
7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */; };
A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */; };
A3B082D51BB15CEA00D8DC35 /* CDVGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1B701026177A61CF00AE11F4 /* CDVShared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVShared.h; path = Classes/CDVShared.h; sourceTree = "<group>"; }; 30193A4E1AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVUIWebViewNavigationDelegate.m; sourceTree = "<group>"; };
1F92F49E1314023E0046367C /* CDVPluginResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVPluginResult.h; path = Classes/CDVPluginResult.h; sourceTree = "<group>"; }; 30193A4F1AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVUIWebViewNavigationDelegate.h; sourceTree = "<group>"; };
1F92F49F1314023E0046367C /* CDVPluginResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVPluginResult.m; path = Classes/CDVPluginResult.m; sourceTree = "<group>"; };
301F2F2914F3C9CA003FE9FC /* CDV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDV.h; path = Classes/CDV.h; sourceTree = "<group>"; };
302965BB13A94E9D007046C5 /* CDVDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDebug.h; path = Classes/CDVDebug.h; sourceTree = "<group>"; };
30325A0B136B343700982B63 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; }; 30325A0B136B343700982B63 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
3034979A1513D56A0090E688 /* CDVLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVLocalStorage.h; path = Classes/CDVLocalStorage.h; sourceTree = "<group>"; }; 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVIntentAndNavigationFilter.h; sourceTree = "<group>"; };
3034979B1513D56A0090E688 /* CDVLocalStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVLocalStorage.m; path = Classes/CDVLocalStorage.m; sourceTree = "<group>"; }; 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVIntentAndNavigationFilter.m; sourceTree = "<group>"; };
30392E4D14F4FCAB00B9E0B8 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVAvailability.h; path = Classes/CDVAvailability.h; sourceTree = "<group>"; };
3062D11E151D0EDB000D9128 /* UIDevice+Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIDevice+Extensions.h"; path = "Classes/UIDevice+Extensions.h"; sourceTree = "<group>"; };
3062D11F151D0EDB000D9128 /* UIDevice+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIDevice+Extensions.m"; path = "Classes/UIDevice+Extensions.m"; sourceTree = "<group>"; };
3073E9EC1656D51200957977 /* CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVScreenOrientationDelegate.h; path = Classes/CDVScreenOrientationDelegate.h; sourceTree = "<group>"; };
30C6847E1406CB38004C1A8E /* CDVWhitelist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVWhitelist.h; path = Classes/CDVWhitelist.h; sourceTree = "<group>"; };
30C6847F1406CB38004C1A8E /* CDVWhitelist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVWhitelist.m; path = Classes/CDVWhitelist.m; sourceTree = "<group>"; };
30C684921407044A004C1A8E /* CDVURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVURLProtocol.h; path = Classes/CDVURLProtocol.h; sourceTree = "<group>"; };
30C684931407044A004C1A8E /* CDVURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVURLProtocol.m; path = Classes/CDVURLProtocol.m; sourceTree = "<group>"; };
30E33AF013A7E24B00594D64 /* CDVPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVPlugin.h; path = Classes/CDVPlugin.h; sourceTree = "<group>"; };
30E33AF113A7E24B00594D64 /* CDVPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVPlugin.m; path = Classes/CDVPlugin.m; sourceTree = "<group>"; };
30E563CD13E217EC00C949AA /* NSMutableArray+QueueAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableArray+QueueAdditions.h"; path = "Classes/NSMutableArray+QueueAdditions.h"; sourceTree = "<group>"; };
30E563CE13E217EC00C949AA /* NSMutableArray+QueueAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableArray+QueueAdditions.m"; path = "Classes/NSMutableArray+QueueAdditions.m"; sourceTree = "<group>"; };
30E6B8CB1A8ADD900025B9EE /* CDVHandleOpenURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVHandleOpenURL.h; path = Classes/CDVHandleOpenURL.h; sourceTree = "<group>"; };
30E6B8CC1A8ADD900025B9EE /* CDVHandleOpenURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVHandleOpenURL.m; path = Classes/CDVHandleOpenURL.m; sourceTree = "<group>"; };
30F39309169F839700B22307 /* CDVJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVJSON.h; path = Classes/CDVJSON.h; sourceTree = "<group>"; };
30F3930A169F839700B22307 /* CDVJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVJSON.m; path = Classes/CDVJSON.m; sourceTree = "<group>"; };
30F5EBA914CA26E700987760 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVCommandDelegate.h; path = Classes/CDVCommandDelegate.h; sourceTree = "<group>"; };
686357AA141002F100DF4CF2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
686357AC141002F100DF4CF2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
686357AE141002F100DF4CF2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
686357CC14100AAD00DF4CF2 /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; };
686357CE14100ADA00DF4CF2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
686357CF14100ADB00DF4CF2 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
686357D014100ADE00DF4CF2 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; };
686357D214100AE700DF4CF2 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
686357D414100AF200DF4CF2 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
686357DC14100B1600DF4CF2 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
68A32D7114102E1C006B237C /* libCordova.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCordova.a; sourceTree = BUILT_PRODUCTS_DIR; }; 68A32D7114102E1C006B237C /* libCordova.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCordova.a; sourceTree = BUILT_PRODUCTS_DIR; };
68A32D7414103017006B237C /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 7ED95CF21AB9028C008C4574 /* CDVDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVDebug.h; sourceTree = "<group>"; };
7E14B5A61705050A0032169E /* CDVTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVTimer.h; path = Classes/CDVTimer.h; sourceTree = "<group>"; }; 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVJSON_private.h; sourceTree = "<group>"; };
7E14B5A71705050A0032169E /* CDVTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVTimer.m; path = Classes/CDVTimer.m; sourceTree = "<group>"; }; 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVJSON_private.m; sourceTree = "<group>"; };
7E22B88419E4C0210026F95E /* CDVAvailabilityDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVAvailabilityDeprecated.h; path = Classes/CDVAvailabilityDeprecated.h; sourceTree = "<group>"; }; 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Private.h"; sourceTree = "<group>"; };
8220B5C316D5427E00EC3921 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVHandleOpenURL.h; sourceTree = "<group>"; };
8852C43614B65FD800F0E735 /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVViewController.h; path = Classes/CDVViewController.h; sourceTree = "<group>"; }; 7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVHandleOpenURL.m; sourceTree = "<group>"; };
8852C43714B65FD800F0E735 /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVViewController.m; path = Classes/CDVViewController.m; sourceTree = "<group>"; }; 7ED95CFB1AB9028C008C4574 /* CDVLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVLocalStorage.h; sourceTree = "<group>"; };
8887FD281090FBE7009987E8 /* NSDictionary+Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Extensions.h"; path = "Classes/NSDictionary+Extensions.h"; sourceTree = "<group>"; }; 7ED95CFC1AB9028C008C4574 /* CDVLocalStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVLocalStorage.m; sourceTree = "<group>"; };
8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Extensions.m"; path = "Classes/NSDictionary+Extensions.m"; sourceTree = "<group>"; }; 7ED95CFE1AB9028C008C4574 /* CDVUIWebViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVUIWebViewDelegate.h; sourceTree = "<group>"; };
8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVInvokedUrlCommand.h; path = Classes/CDVInvokedUrlCommand.h; sourceTree = "<group>"; }; 7ED95CFF1AB9028C008C4574 /* CDVUIWebViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVUIWebViewDelegate.m; sourceTree = "<group>"; };
8887FD351090FBE7009987E8 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVInvokedUrlCommand.m; path = Classes/CDVInvokedUrlCommand.m; sourceTree = "<group>"; }; 7ED95D001AB9028C008C4574 /* CDVUIWebViewEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVUIWebViewEngine.h; sourceTree = "<group>"; };
8887FD501090FBE7009987E8 /* NSData+Base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+Base64.h"; path = "Classes/NSData+Base64.h"; sourceTree = "<group>"; }; 7ED95D011AB9028C008C4574 /* CDVUIWebViewEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVUIWebViewEngine.m; sourceTree = "<group>"; };
8887FD511090FBE7009987E8 /* NSData+Base64.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+Base64.m"; path = "Classes/NSData+Base64.m"; sourceTree = "<group>"; }; 7ED95D0F1AB9029B008C4574 /* CDV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDV.h; sourceTree = "<group>"; };
7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAppDelegate.h; sourceTree = "<group>"; };
7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVAppDelegate.m; sourceTree = "<group>"; };
7ED95D121AB9029B008C4574 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailability.h; sourceTree = "<group>"; };
7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVAvailabilityDeprecated.h; sourceTree = "<group>"; };
7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegate.h; sourceTree = "<group>"; };
7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandDelegateImpl.h; sourceTree = "<group>"; };
7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandDelegateImpl.m; sourceTree = "<group>"; };
7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVCommandQueue.h; sourceTree = "<group>"; };
7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVCommandQueue.m; sourceTree = "<group>"; };
7ED95D191AB9029B008C4574 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVConfigParser.h; sourceTree = "<group>"; };
7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVConfigParser.m; sourceTree = "<group>"; };
7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVInvokedUrlCommand.h; sourceTree = "<group>"; };
7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVInvokedUrlCommand.m; sourceTree = "<group>"; };
7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CDVPlugin+Resources.h"; sourceTree = "<group>"; };
7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CDVPlugin+Resources.m"; sourceTree = "<group>"; };
7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPlugin.h; sourceTree = "<group>"; };
7ED95D201AB9029B008C4574 /* CDVPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPlugin.m; sourceTree = "<group>"; };
7ED95D211AB9029B008C4574 /* CDVPluginResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVPluginResult.h; sourceTree = "<group>"; };
7ED95D221AB9029B008C4574 /* CDVPluginResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVPluginResult.m; sourceTree = "<group>"; };
7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVScreenOrientationDelegate.h; sourceTree = "<group>"; };
7ED95D241AB9029B008C4574 /* CDVTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVTimer.h; sourceTree = "<group>"; };
7ED95D251AB9029B008C4574 /* CDVTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVTimer.m; sourceTree = "<group>"; };
7ED95D261AB9029B008C4574 /* CDVURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVURLProtocol.h; sourceTree = "<group>"; };
7ED95D271AB9029B008C4574 /* CDVURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVURLProtocol.m; sourceTree = "<group>"; };
7ED95D281AB9029B008C4574 /* CDVUserAgentUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVUserAgentUtil.h; sourceTree = "<group>"; };
7ED95D291AB9029B008C4574 /* CDVUserAgentUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVUserAgentUtil.m; sourceTree = "<group>"; };
7ED95D2A1AB9029B008C4574 /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVViewController.h; sourceTree = "<group>"; };
7ED95D2B1AB9029B008C4574 /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVViewController.m; sourceTree = "<group>"; };
7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWebViewEngineProtocol.h; sourceTree = "<group>"; };
7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWhitelist.h; sourceTree = "<group>"; };
7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWhitelist.m; sourceTree = "<group>"; };
7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+CordovaPreferences.h"; sourceTree = "<group>"; };
7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+CordovaPreferences.m"; sourceTree = "<group>"; };
7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+QueueAdditions.h"; sourceTree = "<group>"; };
7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+QueueAdditions.m"; sourceTree = "<group>"; };
A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVGestureHandler.h; sourceTree = "<group>"; };
A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVGestureHandler.m; sourceTree = "<group>"; };
AA747D9E0F9514B9006C5449 /* CordovaLib_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CordovaLib_Prefix.pch; sourceTree = SOURCE_ROOT; }; AA747D9E0F9514B9006C5449 /* CordovaLib_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CordovaLib_Prefix.pch; sourceTree = SOURCE_ROOT; };
EB3B3545161CB44D003DBE7D /* CDVCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVCommandQueue.h; path = Classes/CDVCommandQueue.h; sourceTree = "<group>"; };
EB3B3546161CB44D003DBE7D /* CDVCommandQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVCommandQueue.m; path = Classes/CDVCommandQueue.m; sourceTree = "<group>"; };
EB3B357A161F2A44003DBE7D /* CDVCommandDelegateImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVCommandDelegateImpl.h; path = Classes/CDVCommandDelegateImpl.h; sourceTree = "<group>"; };
EB3B357B161F2A45003DBE7D /* CDVCommandDelegateImpl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVCommandDelegateImpl.m; path = Classes/CDVCommandDelegateImpl.m; sourceTree = "<group>"; };
EB6A98521A77EE470013FCDB /* CDVJSON_private.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVJSON_private.m; path = Classes/CDVJSON_private.m; sourceTree = "<group>"; };
EB6A98531A77EE470013FCDB /* CDVJSON_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVJSON_private.h; path = Classes/CDVJSON_private.h; sourceTree = "<group>"; };
EB96673916A8970900D86CDF /* CDVUserAgentUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVUserAgentUtil.h; path = Classes/CDVUserAgentUtil.h; sourceTree = "<group>"; };
EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVUserAgentUtil.m; path = Classes/CDVUserAgentUtil.m; sourceTree = "<group>"; };
EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+Comparisons.h"; path = "Classes/NSArray+Comparisons.h"; sourceTree = "<group>"; };
EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+Comparisons.m"; path = "Classes/NSArray+Comparisons.m"; sourceTree = "<group>"; };
EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVWebViewDelegate.m; path = Classes/CDVWebViewDelegate.m; sourceTree = "<group>"; };
EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVWebViewDelegate.h; path = Classes/CDVWebViewDelegate.h; sourceTree = "<group>"; };
F858FBC4166009A8007DA594 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVConfigParser.h; path = Classes/CDVConfigParser.h; sourceTree = "<group>"; };
F858FBC5166009A8007DA594 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVConfigParser.m; path = Classes/CDVConfigParser.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -146,120 +145,131 @@ ...@@ -146,120 +145,131 @@
0867D691FE84028FC02AAC07 /* CordovaLib */ = { 0867D691FE84028FC02AAC07 /* CordovaLib */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8887FD101090FB43009987E8 /* Classes */, 7ED95D0E1AB9029B008C4574 /* Public */,
32C88DFF0371C24200C91783 /* Other Sources */, 7ED95CF11AB9028C008C4574 /* Private */,
0867D69AFE84028FC02AAC07 /* Frameworks */,
034768DFFF38A50411DB9C8B /* Products */, 034768DFFF38A50411DB9C8B /* Products */,
30325A0B136B343700982B63 /* VERSION */, 30325A0B136B343700982B63 /* VERSION */,
); );
name = CordovaLib; name = CordovaLib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
0867D69AFE84028FC02AAC07 /* Frameworks */ = { 3093E2201B16D6A3003F381A /* CDVIntentAndNavigationFilter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
68A32D7414103017006B237C /* AddressBook.framework */, 3093E2211B16D6A3003F381A /* CDVIntentAndNavigationFilter.h */,
8220B5C316D5427E00EC3921 /* AssetsLibrary.framework */, 3093E2221B16D6A3003F381A /* CDVIntentAndNavigationFilter.m */,
686357DC14100B1600DF4CF2 /* CoreMedia.framework */,
686357CE14100ADA00DF4CF2 /* AudioToolbox.framework */,
686357CF14100ADB00DF4CF2 /* AVFoundation.framework */,
686357D014100ADE00DF4CF2 /* CoreLocation.framework */,
686357D214100AE700DF4CF2 /* MobileCoreServices.framework */,
686357D414100AF200DF4CF2 /* SystemConfiguration.framework */,
686357CC14100AAD00DF4CF2 /* AddressBookUI.framework */,
686357AA141002F100DF4CF2 /* UIKit.framework */,
686357AC141002F100DF4CF2 /* Foundation.framework */,
686357AE141002F100DF4CF2 /* CoreGraphics.framework */,
); );
name = Frameworks; path = CDVIntentAndNavigationFilter;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3054098714B77FF3009841CA /* Cleaver */ = { 7ED95CF11AB9028C008C4574 /* Private */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F858FBC4166009A8007DA594 /* CDVConfigParser.h */, AA747D9E0F9514B9006C5449 /* CordovaLib_Prefix.pch */,
F858FBC5166009A8007DA594 /* CDVConfigParser.m */, 7ED95CF21AB9028C008C4574 /* CDVDebug.h */,
8852C43614B65FD800F0E735 /* CDVViewController.h */, 7ED95CF31AB9028C008C4574 /* CDVJSON_private.h */,
8852C43714B65FD800F0E735 /* CDVViewController.m */, 7ED95CF41AB9028C008C4574 /* CDVJSON_private.m */,
EB3B3545161CB44D003DBE7D /* CDVCommandQueue.h */, 7ED95CF51AB9028C008C4574 /* CDVPlugin+Private.h */,
EB3B3546161CB44D003DBE7D /* CDVCommandQueue.m */, 7ED95CF61AB9028C008C4574 /* Plugins */,
); );
name = Cleaver; name = Private;
path = Classes/Private;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
32C88DFF0371C24200C91783 /* Other Sources */ = { 7ED95CF61AB9028C008C4574 /* Plugins */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
AA747D9E0F9514B9006C5449 /* CordovaLib_Prefix.pch */, A3B082D11BB15CEA00D8DC35 /* CDVGestureHandler */,
3093E2201B16D6A3003F381A /* CDVIntentAndNavigationFilter */,
7ED95CF71AB9028C008C4574 /* CDVHandleOpenURL */,
7ED95CFA1AB9028C008C4574 /* CDVLocalStorage */,
7ED95CFD1AB9028C008C4574 /* CDVUIWebViewEngine */,
);
path = Plugins;
sourceTree = "<group>";
};
7ED95CF71AB9028C008C4574 /* CDVHandleOpenURL */ = {
isa = PBXGroup;
children = (
7ED95CF81AB9028C008C4574 /* CDVHandleOpenURL.h */,
7ED95CF91AB9028C008C4574 /* CDVHandleOpenURL.m */,
); );
name = "Other Sources"; path = CDVHandleOpenURL;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
888700D710922F56009987E8 /* Commands */ = { 7ED95CFA1AB9028C008C4574 /* CDVLocalStorage */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
30E6B8CB1A8ADD900025B9EE /* CDVHandleOpenURL.h */, 7ED95CFB1AB9028C008C4574 /* CDVLocalStorage.h */,
30E6B8CC1A8ADD900025B9EE /* CDVHandleOpenURL.m */, 7ED95CFC1AB9028C008C4574 /* CDVLocalStorage.m */,
7E22B88419E4C0210026F95E /* CDVAvailabilityDeprecated.h */,
EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */,
EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */,
301F2F2914F3C9CA003FE9FC /* CDV.h */,
3034979A1513D56A0090E688 /* CDVLocalStorage.h */,
3034979B1513D56A0090E688 /* CDVLocalStorage.m */,
30392E4D14F4FCAB00B9E0B8 /* CDVAvailability.h */,
30F5EBA914CA26E700987760 /* CDVCommandDelegate.h */,
EB3B357A161F2A44003DBE7D /* CDVCommandDelegateImpl.h */,
EB3B357B161F2A45003DBE7D /* CDVCommandDelegateImpl.m */,
30C684921407044A004C1A8E /* CDVURLProtocol.h */,
30C684931407044A004C1A8E /* CDVURLProtocol.m */,
30C6847E1406CB38004C1A8E /* CDVWhitelist.h */,
1B701026177A61CF00AE11F4 /* CDVShared.h */,
30C6847F1406CB38004C1A8E /* CDVWhitelist.m */,
30E33AF013A7E24B00594D64 /* CDVPlugin.h */,
30E33AF113A7E24B00594D64 /* CDVPlugin.m */,
1F92F49E1314023E0046367C /* CDVPluginResult.h */,
1F92F49F1314023E0046367C /* CDVPluginResult.m */,
8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */,
8887FD351090FBE7009987E8 /* CDVInvokedUrlCommand.m */,
3073E9EC1656D51200957977 /* CDVScreenOrientationDelegate.h */,
30F39309169F839700B22307 /* CDVJSON.h */,
30F3930A169F839700B22307 /* CDVJSON.m */,
EB6A98521A77EE470013FCDB /* CDVJSON_private.m */,
EB6A98531A77EE470013FCDB /* CDVJSON_private.h */,
EB96673916A8970900D86CDF /* CDVUserAgentUtil.h */,
EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */,
); );
name = Commands; path = CDVLocalStorage;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
888700D910923009009987E8 /* Util */ = { 7ED95CFD1AB9028C008C4574 /* CDVUIWebViewEngine */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3062D11E151D0EDB000D9128 /* UIDevice+Extensions.h */, 30193A4E1AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.m */,
3062D11F151D0EDB000D9128 /* UIDevice+Extensions.m */, 30193A4F1AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.h */,
EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */, 7ED95CFE1AB9028C008C4574 /* CDVUIWebViewDelegate.h */,
EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */, 7ED95CFF1AB9028C008C4574 /* CDVUIWebViewDelegate.m */,
8887FD281090FBE7009987E8 /* NSDictionary+Extensions.h */, 7ED95D001AB9028C008C4574 /* CDVUIWebViewEngine.h */,
8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */, 7ED95D011AB9028C008C4574 /* CDVUIWebViewEngine.m */,
302965BB13A94E9D007046C5 /* CDVDebug.h */,
30E563CD13E217EC00C949AA /* NSMutableArray+QueueAdditions.h */,
30E563CE13E217EC00C949AA /* NSMutableArray+QueueAdditions.m */,
8887FD501090FBE7009987E8 /* NSData+Base64.h */,
8887FD511090FBE7009987E8 /* NSData+Base64.m */,
7E14B5A61705050A0032169E /* CDVTimer.h */,
7E14B5A71705050A0032169E /* CDVTimer.m */,
); );
name = Util; path = CDVUIWebViewEngine;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
8887FD101090FB43009987E8 /* Classes */ = { 7ED95D0E1AB9029B008C4574 /* Public */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3054098714B77FF3009841CA /* Cleaver */, 7ED95D0F1AB9029B008C4574 /* CDV.h */,
888700D710922F56009987E8 /* Commands */, 7ED95D101AB9029B008C4574 /* CDVAppDelegate.h */,
888700D910923009009987E8 /* Util */, 7ED95D111AB9029B008C4574 /* CDVAppDelegate.m */,
7ED95D121AB9029B008C4574 /* CDVAvailability.h */,
7ED95D131AB9029B008C4574 /* CDVAvailabilityDeprecated.h */,
7ED95D141AB9029B008C4574 /* CDVCommandDelegate.h */,
7ED95D151AB9029B008C4574 /* CDVCommandDelegateImpl.h */,
7ED95D161AB9029B008C4574 /* CDVCommandDelegateImpl.m */,
7ED95D171AB9029B008C4574 /* CDVCommandQueue.h */,
7ED95D181AB9029B008C4574 /* CDVCommandQueue.m */,
7ED95D191AB9029B008C4574 /* CDVConfigParser.h */,
7ED95D1A1AB9029B008C4574 /* CDVConfigParser.m */,
7ED95D1B1AB9029B008C4574 /* CDVInvokedUrlCommand.h */,
7ED95D1C1AB9029B008C4574 /* CDVInvokedUrlCommand.m */,
7ED95D1D1AB9029B008C4574 /* CDVPlugin+Resources.h */,
7ED95D1E1AB9029B008C4574 /* CDVPlugin+Resources.m */,
7ED95D1F1AB9029B008C4574 /* CDVPlugin.h */,
7ED95D201AB9029B008C4574 /* CDVPlugin.m */,
7ED95D211AB9029B008C4574 /* CDVPluginResult.h */,
7ED95D221AB9029B008C4574 /* CDVPluginResult.m */,
7ED95D231AB9029B008C4574 /* CDVScreenOrientationDelegate.h */,
7ED95D241AB9029B008C4574 /* CDVTimer.h */,
7ED95D251AB9029B008C4574 /* CDVTimer.m */,
7ED95D261AB9029B008C4574 /* CDVURLProtocol.h */,
7ED95D271AB9029B008C4574 /* CDVURLProtocol.m */,
7ED95D281AB9029B008C4574 /* CDVUserAgentUtil.h */,
7ED95D291AB9029B008C4574 /* CDVUserAgentUtil.m */,
7ED95D2A1AB9029B008C4574 /* CDVViewController.h */,
7ED95D2B1AB9029B008C4574 /* CDVViewController.m */,
7ED95D2C1AB9029B008C4574 /* CDVWebViewEngineProtocol.h */,
7ED95D2D1AB9029B008C4574 /* CDVWhitelist.h */,
7ED95D2E1AB9029B008C4574 /* CDVWhitelist.m */,
7ED95D311AB9029B008C4574 /* NSDictionary+CordovaPreferences.h */,
7ED95D321AB9029B008C4574 /* NSDictionary+CordovaPreferences.m */,
7ED95D331AB9029B008C4574 /* NSMutableArray+QueueAdditions.h */,
7ED95D341AB9029B008C4574 /* NSMutableArray+QueueAdditions.m */,
); );
name = Classes; name = Public;
path = Classes/Public;
sourceTree = "<group>";
};
A3B082D11BB15CEA00D8DC35 /* CDVGestureHandler */ = {
isa = PBXGroup;
children = (
A3B082D21BB15CEA00D8DC35 /* CDVGestureHandler.h */,
A3B082D31BB15CEA00D8DC35 /* CDVGestureHandler.m */,
);
path = CDVGestureHandler;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
...@@ -269,33 +279,37 @@ ...@@ -269,33 +279,37 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
8887FD681090FBE7009987E8 /* NSDictionary+Extensions.h in Headers */, 7ED95D521AB9029B008C4574 /* CDVWebViewEngineProtocol.h in Headers */,
8887FD741090FBE7009987E8 /* CDVInvokedUrlCommand.h in Headers */, 7ED95D491AB9029B008C4574 /* CDVScreenOrientationDelegate.h in Headers */,
8887FD8F1090FBE7009987E8 /* NSData+Base64.h in Headers */, 7ED95D351AB9029B008C4574 /* CDV.h in Headers */,
1F92F4A01314023E0046367C /* CDVPluginResult.h in Headers */, A3B082D41BB15CEA00D8DC35 /* CDVGestureHandler.h in Headers */,
30E33AF213A7E24B00594D64 /* CDVPlugin.h in Headers */, 7ED95D3B1AB9029B008C4574 /* CDVCommandDelegateImpl.h in Headers */,
30E6B8CD1A8ADD900025B9EE /* CDVHandleOpenURL.h in Headers */, 7ED95D3D1AB9029B008C4574 /* CDVCommandQueue.h in Headers */,
302965BC13A94E9D007046C5 /* CDVDebug.h in Headers */, 7ED95D531AB9029B008C4574 /* CDVWhitelist.h in Headers */,
30E563CF13E217EC00C949AA /* NSMutableArray+QueueAdditions.h in Headers */, 7ED95D361AB9029B008C4574 /* CDVAppDelegate.h in Headers */,
30C684801406CB38004C1A8E /* CDVWhitelist.h in Headers */, 7ED95D431AB9029B008C4574 /* CDVPlugin+Resources.h in Headers */,
30C684941407044B004C1A8E /* CDVURLProtocol.h in Headers */, 7ED95D381AB9029B008C4574 /* CDVAvailability.h in Headers */,
8852C43A14B65FD800F0E735 /* CDVViewController.h in Headers */, 7ED95D0A1AB9028C008C4574 /* CDVUIWebViewDelegate.h in Headers */,
30F5EBAB14CA26E700987760 /* CDVCommandDelegate.h in Headers */, 7ED95D471AB9029B008C4574 /* CDVPluginResult.h in Headers */,
301F2F2A14F3C9CA003FE9FC /* CDV.h in Headers */, 7ED95D591AB9029B008C4574 /* NSMutableArray+QueueAdditions.h in Headers */,
30392E4E14F4FCAB00B9E0B8 /* CDVAvailability.h in Headers */, 7ED95D411AB9029B008C4574 /* CDVInvokedUrlCommand.h in Headers */,
7E22B88519E4C0210026F95E /* CDVAvailabilityDeprecated.h in Headers */, 7ED95D571AB9029B008C4574 /* NSDictionary+CordovaPreferences.h in Headers */,
3034979C1513D56A0090E688 /* CDVLocalStorage.h in Headers */, 7ED95D451AB9029B008C4574 /* CDVPlugin.h in Headers */,
3062D120151D0EDB000D9128 /* UIDevice+Extensions.h in Headers */, 7ED95D4C1AB9029B008C4574 /* CDVURLProtocol.h in Headers */,
EBA3557315ABD38C00F4DE24 /* NSArray+Comparisons.h in Headers */, 7ED95D3A1AB9029B008C4574 /* CDVCommandDelegate.h in Headers */,
EB3B3547161CB44D003DBE7D /* CDVCommandQueue.h in Headers */, 7ED95D391AB9029B008C4574 /* CDVAvailabilityDeprecated.h in Headers */,
EB3B357C161F2A45003DBE7D /* CDVCommandDelegateImpl.h in Headers */, 7ED95D4E1AB9029B008C4574 /* CDVUserAgentUtil.h in Headers */,
1B701028177A61CF00AE11F4 /* CDVShared.h in Headers */, 7ED95D4A1AB9029B008C4574 /* CDVTimer.h in Headers */,
3073E9ED1656D51200957977 /* CDVScreenOrientationDelegate.h in Headers */, 7ED95D3F1AB9029B008C4574 /* CDVConfigParser.h in Headers */,
F858FBC6166009A8007DA594 /* CDVConfigParser.h in Headers */, 7ED95D501AB9029B008C4574 /* CDVViewController.h in Headers */,
30F3930B169F839700B22307 /* CDVJSON.h in Headers */, 7ED95D031AB9028C008C4574 /* CDVJSON_private.h in Headers */,
EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */, 7ED95D021AB9028C008C4574 /* CDVDebug.h in Headers */,
EB96673B16A8970A00D86CDF /* CDVUserAgentUtil.h in Headers */, 7ED95D051AB9028C008C4574 /* CDVPlugin+Private.h in Headers */,
7E14B5A81705050A0032169E /* CDVTimer.h in Headers */, 7E7F69B61ABA35D8007546F4 /* CDVLocalStorage.h in Headers */,
3093E2231B16D6A3003F381A /* CDVIntentAndNavigationFilter.h in Headers */,
7E7F69B81ABA368F007546F4 /* CDVUIWebViewEngine.h in Headers */,
7E7F69B91ABA3692007546F4 /* CDVHandleOpenURL.h in Headers */,
30193A511AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -353,27 +367,29 @@ ...@@ -353,27 +367,29 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
8887FD691090FBE7009987E8 /* NSDictionary+Extensions.m in Sources */, 7ED95D511AB9029B008C4574 /* CDVViewController.m in Sources */,
8887FD751090FBE7009987E8 /* CDVInvokedUrlCommand.m in Sources */, 7ED95D581AB9029B008C4574 /* NSDictionary+CordovaPreferences.m in Sources */,
8887FD901090FBE7009987E8 /* NSData+Base64.m in Sources */, 7ED95D371AB9029B008C4574 /* CDVAppDelegate.m in Sources */,
1F92F4A11314023E0046367C /* CDVPluginResult.m in Sources */, 7ED95D0B1AB9028C008C4574 /* CDVUIWebViewDelegate.m in Sources */,
30E33AF313A7E24B00594D64 /* CDVPlugin.m in Sources */, 7ED95D3C1AB9029B008C4574 /* CDVCommandDelegateImpl.m in Sources */,
30E563D013E217EC00C949AA /* NSMutableArray+QueueAdditions.m in Sources */, 7ED95D041AB9028C008C4574 /* CDVJSON_private.m in Sources */,
30C684821406CB38004C1A8E /* CDVWhitelist.m in Sources */, 7ED95D541AB9029B008C4574 /* CDVWhitelist.m in Sources */,
30C684961407044B004C1A8E /* CDVURLProtocol.m in Sources */, 7ED95D421AB9029B008C4574 /* CDVInvokedUrlCommand.m in Sources */,
8852C43C14B65FD800F0E735 /* CDVViewController.m in Sources */, 7ED95D4B1AB9029B008C4574 /* CDVTimer.m in Sources */,
3034979E1513D56A0090E688 /* CDVLocalStorage.m in Sources */, 7ED95D4F1AB9029B008C4574 /* CDVUserAgentUtil.m in Sources */,
3062D122151D0EDB000D9128 /* UIDevice+Extensions.m in Sources */, 7ED95D401AB9029B008C4574 /* CDVConfigParser.m in Sources */,
EBA3557515ABD38C00F4DE24 /* NSArray+Comparisons.m in Sources */, A3B082D51BB15CEA00D8DC35 /* CDVGestureHandler.m in Sources */,
EB3B3548161CB44D003DBE7D /* CDVCommandQueue.m in Sources */, 7ED95D071AB9028C008C4574 /* CDVHandleOpenURL.m in Sources */,
EB6A98541A77EE470013FCDB /* CDVJSON_private.m in Sources */, 30193A501AE6350A0069A75F /* CDVUIWebViewNavigationDelegate.m in Sources */,
EB3B357D161F2A45003DBE7D /* CDVCommandDelegateImpl.m in Sources */, 7ED95D5A1AB9029B008C4574 /* NSMutableArray+QueueAdditions.m in Sources */,
F858FBC7166009A8007DA594 /* CDVConfigParser.m in Sources */, 7ED95D3E1AB9029B008C4574 /* CDVCommandQueue.m in Sources */,
30F3930C169F839700B22307 /* CDVJSON.m in Sources */, 7ED95D481AB9029B008C4574 /* CDVPluginResult.m in Sources */,
EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */, 7ED95D441AB9029B008C4574 /* CDVPlugin+Resources.m in Sources */,
30E6B8CE1A8ADD900025B9EE /* CDVHandleOpenURL.m in Sources */, 7ED95D4D1AB9029B008C4574 /* CDVURLProtocol.m in Sources */,
EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */, 7ED95D0D1AB9028C008C4574 /* CDVUIWebViewEngine.m in Sources */,
7E14B5A91705050A0032169E /* CDVTimer.m in Sources */, 7ED95D461AB9029B008C4574 /* CDVPlugin.m in Sources */,
7ED95D091AB9028C008C4574 /* CDVLocalStorage.m in Sources */,
3093E2241B16D6A3003F381A /* CDVIntentAndNavigationFilter.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -384,6 +400,7 @@ ...@@ -384,6 +400,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DSTROOT = "/tmp/$(PROJECT_NAME).dst"; DSTROOT = "/tmp/$(PROJECT_NAME).dst";
...@@ -407,6 +424,7 @@ ...@@ -407,6 +424,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
DSTROOT = "/tmp/$(PROJECT_NAME).dst"; DSTROOT = "/tmp/$(PROJECT_NAME).dst";
GCC_MODEL_TUNING = G5; GCC_MODEL_TUNING = G5;
...@@ -416,7 +434,7 @@ ...@@ -416,7 +434,7 @@
GCC_THUMB_SUPPORT = NO; GCC_THUMB_SUPPORT = NO;
GCC_VERSION = ""; GCC_VERSION = "";
INSTALL_PATH = /usr/local/lib; INSTALL_PATH = /usr/local/lib;
IPHONEOS_DEPLOYMENT_TARGET = 6.0; IPHONEOS_DEPLOYMENT_TARGET = 7.0;
PRODUCT_NAME = Cordova; PRODUCT_NAME = Cordova;
PUBLIC_HEADERS_FOLDER_PATH = include/Cordova; PUBLIC_HEADERS_FOLDER_PATH = include/Cordova;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
...@@ -443,7 +461,7 @@ ...@@ -443,7 +461,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0; IPHONEOS_DEPLOYMENT_TARGET = 7.0;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-DDEBUG"; OTHER_CFLAGS = "-DDEBUG";
PUBLIC_HEADERS_FOLDER_PATH = include/Cordova; PUBLIC_HEADERS_FOLDER_PATH = include/Cordova;
...@@ -472,7 +490,7 @@ ...@@ -472,7 +490,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 6.0; IPHONEOS_DEPLOYMENT_TARGET = 7.0;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
PUBLIC_HEADERS_FOLDER_PATH = include/Cordova; PUBLIC_HEADERS_FOLDER_PATH = include/Cordova;
SDKROOT = iphoneos; SDKROOT = iphoneos;
......
// Platform: ios // Platform: ios
// 49a8db57fa070d20ea7b304a53ffec3d7250c5af // ded62dda172755defaf75378ed007dc05730ec22
/* /*
Licensed to the Apache Software Foundation (ASF) under one Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file or more contributor license agreements. See the NOTICE file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
under the License. under the License.
*/ */
;(function() { ;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '3.9.2'; var PLATFORM_VERSION_BUILD_LABEL = '4.0.1';
// file: src/scripts/require.js // file: src/scripts/require.js
/*jshint -W079 */ /*jshint -W079 */
...@@ -817,58 +817,23 @@ module.exports = channel; ...@@ -817,58 +817,23 @@ module.exports = channel;
}); });
// file: e:/cordova/cordova-ios/cordova-js-src/exec.js // file: /Users/shaz/Documents/Git/Apache/cordova-ios/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) { define("cordova/exec", function(require, exports, module) {
/*global require, module, atob, document */
/** /**
* Creates a gap bridge iframe used to notify the native code about queued * Creates a gap bridge iframe used to notify the native code about queued
* commands. * commands.
*/ */
var cordova = require('cordova'), var cordova = require('cordova'),
channel = require('cordova/channel'),
utils = require('cordova/utils'), utils = require('cordova/utils'),
base64 = require('cordova/base64'), base64 = require('cordova/base64'),
// XHR mode does not work on iOS 4.2.
// XHR mode's main advantage is working around a bug in -webkit-scroll, which
// doesn't exist only on iOS 5.x devices.
// IFRAME_NAV is the fastest.
// IFRAME_HASH could be made to enable synchronous bridge calls if we wanted this feature.
jsToNativeModes = {
IFRAME_NAV: 0, // Default. Uses a new iframe for each poke.
// XHR bridge appears to be flaky sometimes: CB-3900, CB-3359, CB-5457, CB-4970, CB-4998, CB-5134
XHR_NO_PAYLOAD: 1, // About the same speed as IFRAME_NAV. Performance not about the same as IFRAME_NAV, but more variable.
XHR_WITH_PAYLOAD: 2, // Flakey, and not as performant
XHR_OPTIONAL_PAYLOAD: 3, // Flakey, and not as performant
IFRAME_HASH_NO_PAYLOAD: 4, // Not fully baked. A bit faster than IFRAME_NAV, but risks jank since poke happens synchronously.
IFRAME_HASH_WITH_PAYLOAD: 5, // Slower than no payload. Maybe since it has to be URI encoded / decoded.
WK_WEBVIEW_BINDING: 6 // Only way that works for WKWebView :)
},
bridgeMode,
execIframe, execIframe,
execHashIframe,
hashToggle = 1,
execXhr,
requestCount = 0,
vcHeaderValue = null,
commandQueue = [], // Contains pending JS->Native messages. commandQueue = [], // Contains pending JS->Native messages.
isInContextOfEvalJs = 0, isInContextOfEvalJs = 0,
failSafeTimerId = 0; failSafeTimerId = 0;
function shouldBundleCommandJson() {
if (bridgeMode === jsToNativeModes.XHR_WITH_PAYLOAD) {
return true;
}
if (bridgeMode === jsToNativeModes.XHR_OPTIONAL_PAYLOAD) {
var payloadLength = 0;
for (var i = 0; i < commandQueue.length; ++i) {
payloadLength += commandQueue[i].length;
}
// The value here was determined using the benchmark within CordovaLibApp on an iPad 3.
return payloadLength < 4500;
}
return false;
}
function massageArgsJsToNative(args) { function massageArgsJsToNative(args) {
if (!args || utils.typeName(args) != 'Array') { if (!args || utils.typeName(args) != 'Array') {
return args; return args;
...@@ -919,17 +884,10 @@ function convertMessageToArgsNativeToJs(message) { ...@@ -919,17 +884,10 @@ function convertMessageToArgsNativeToJs(message) {
} }
function iOSExec() { function iOSExec() {
if (bridgeMode === undefined) {
bridgeMode = jsToNativeModes.IFRAME_NAV;
}
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { var successCallback, failCallback, service, action, actionArgs;
bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING;
}
var successCallback, failCallback, service, action, actionArgs, splitCommand;
var callbackId = null; var callbackId = null;
if (typeof arguments[0] !== "string") { if (typeof arguments[0] !== 'string') {
// FORMAT ONE // FORMAT ONE
successCallback = arguments[0]; successCallback = arguments[0];
failCallback = arguments[1]; failCallback = arguments[1];
...@@ -943,18 +901,9 @@ function iOSExec() { ...@@ -943,18 +901,9 @@ function iOSExec() {
// an invalid callbackId and passes it even if no callbacks were given. // an invalid callbackId and passes it even if no callbacks were given.
callbackId = 'INVALID'; callbackId = 'INVALID';
} else { } else {
// FORMAT TWO, REMOVED throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
try { 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'
splitCommand = arguments[0].split("."); );
action = splitCommand.pop();
service = splitCommand.join(".");
actionArgs = Array.prototype.splice.call(arguments, 1);
console.log('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
"cordova.exec(null, null, \"" + service + "\", \"" + action + "\"," + JSON.stringify(actionArgs) + ");"
);
return;
} catch (e) {}
} }
// If actionArgs is not provided, default to an empty array // If actionArgs is not provided, default to an empty array
...@@ -976,116 +925,70 @@ function iOSExec() { ...@@ -976,116 +925,70 @@ function iOSExec() {
// effectively clone the command arguments in case they are mutated before // effectively clone the command arguments in case they are mutated before
// the command is executed. // the command is executed.
commandQueue.push(JSON.stringify(command)); commandQueue.push(JSON.stringify(command));
if (bridgeMode === jsToNativeModes.WK_WEBVIEW_BINDING) {
window.webkit.messageHandlers.cordova.postMessage(command);
} else {
// If we're in the context of a stringByEvaluatingJavaScriptFromString call,
// then the queue will be flushed when it returns; no need for a poke.
// Also, if there is already a command in the queue, then we've already
// poked the native side, so there is no reason to do so again.
if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNative();
}
}
}
function pokeNative() { // If we're in the context of a stringByEvaluatingJavaScriptFromString call,
switch (bridgeMode) { // then the queue will be flushed when it returns; no need for a poke.
case jsToNativeModes.XHR_NO_PAYLOAD: // Also, if there is already a command in the queue, then we've already
case jsToNativeModes.XHR_WITH_PAYLOAD: // poked the native side, so there is no reason to do so again.
case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNativeViaXhr(); pokeNative();
break;
default: // iframe-based.
pokeNativeViaIframe();
} }
} }
function pokeNativeViaXhr() { // CB-10106
// This prevents sending an XHR when there is already one being sent. function handleBridgeChange() {
// This should happen only in rare circumstances (refer to unit tests). if (execProxy !== cordovaExec()) {
if (execXhr && execXhr.readyState != 4) { var commandString = commandQueue.shift();
execXhr = null; while(commandString) {
} var command = JSON.parse(commandString);
// Re-using the XHR improves exec() performance by about 10%. var callbackId = command[0];
execXhr = execXhr || new XMLHttpRequest(); var service = command[1];
// Changing this to a GET will make the XHR reach the URIProtocol on 4.2. var action = command[2];
// For some reason it still doesn't work though... var actionArgs = command[3];
// Add a timestamp to the query param to prevent caching. var callbacks = cordova.callbacks[callbackId] || {};
execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);
if (!vcHeaderValue) { execProxy(callbacks.success, callbacks.fail, service, action, actionArgs);
vcHeaderValue = /.*\((.*)\)$/.exec(navigator.userAgent)[1];
} commandString = commandQueue.shift();
execXhr.setRequestHeader('vc', vcHeaderValue); };
execXhr.setRequestHeader('rc', ++requestCount); return true;
if (shouldBundleCommandJson()) { }
execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());
} return false;
execXhr.send(null);
} }
function pokeNativeViaIframe() { function pokeNative() {
// CB-5488 - Don't attempt to create iframe before document.body is available. // CB-5488 - Don't attempt to create iframe before document.body is available.
if (!document.body) { if (!document.body) {
setTimeout(pokeNativeViaIframe); setTimeout(pokeNative);
return; return;
} }
if (bridgeMode === jsToNativeModes.IFRAME_HASH_NO_PAYLOAD || bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
// TODO: This bridge mode doesn't properly support being removed from the DOM (CB-7735) // Check if they've removed it from the DOM, and put it back if so.
if (!execHashIframe) { if (execIframe && execIframe.contentWindow) {
execHashIframe = document.createElement('iframe'); execIframe.contentWindow.location = 'gap://ready';
execHashIframe.style.display = 'none';
document.body.appendChild(execHashIframe);
// Hash changes don't work on about:blank, so switch it to file:///.
execHashIframe.contentWindow.history.replaceState(null, null, 'file:///#');
}
// The delegate method is called only when the hash changes, so toggle it back and forth.
hashToggle = hashToggle ^ 3;
var hashValue = '%0' + hashToggle;
if (bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
hashValue += iOSExec.nativeFetchMessages();
}
execHashIframe.contentWindow.location.hash = hashValue;
} else { } else {
// Check if they've removed it from the DOM, and put it back if so. execIframe = document.createElement('iframe');
if (execIframe && execIframe.contentWindow) { execIframe.style.display = 'none';
execIframe.contentWindow.location = 'gap://ready'; execIframe.src = 'gap://ready';
} else { document.body.appendChild(execIframe);
execIframe = document.createElement('iframe'); }
execIframe.style.display = 'none'; // Use a timer to protect against iframe being unloaded during the poke (CB-7735).
execIframe.src = 'gap://ready'; // This makes the bridge ~ 7% slower, but works around the poke getting lost
document.body.appendChild(execIframe); // when the iframe is removed from the DOM.
} // An onunload listener could be used in the case where the iframe has just been
// Use a timer to protect against iframe being unloaded during the poke (CB-7735). // created, but since unload events fire only once, it doesn't work in the normal
// This makes the bridge ~ 7% slower, but works around the poke getting lost // case of iframe reuse (where unload will have already fired due to the attempted
// when the iframe is removed from the DOM. // navigation of the page).
// An onunload listener could be used in the case where the iframe has just been failSafeTimerId = setTimeout(function() {
// created, but since unload events fire only once, it doesn't work in the normal if (commandQueue.length) {
// case of iframe reuse (where unload will have already fired due to the attempted // CB-10106 - flush the queue on bridge change
// navigation of the page). if (!handleBridgeChange()) {
failSafeTimerId = setTimeout(function() {
if (commandQueue.length) {
pokeNative(); pokeNative();
} }
}, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
}
}
iOSExec.jsToNativeModes = jsToNativeModes;
iOSExec.setJsToNativeBridgeMode = function(mode) {
// Remove the iFrame since it may be no longer required, and its existence
// can trigger browser bugs.
// https://issues.apache.org/jira/browse/CB-593
if (execIframe) {
if (execIframe.parentNode) {
execIframe.parentNode.removeChild(execIframe);
} }
execIframe = null; }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
} }
bridgeMode = mode;
};
iOSExec.nativeFetchMessages = function() { iOSExec.nativeFetchMessages = function() {
// Stop listing for window detatch once native side confirms poke. // Stop listing for window detatch once native side confirms poke.
...@@ -1102,11 +1005,14 @@ iOSExec.nativeFetchMessages = function() { ...@@ -1102,11 +1005,14 @@ iOSExec.nativeFetchMessages = function() {
return json; return json;
}; };
iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) { iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) {
return iOSExec.nativeEvalAndFetch(function() { return iOSExec.nativeEvalAndFetch(function() {
var success = status === 0 || status === 1; var success = status === 0 || status === 1;
var args = convertMessageToArgsNativeToJs(message); var args = convertMessageToArgsNativeToJs(message);
cordova.callbackFromNative(callbackId, success, status, args, keepCallback); function nc2() {
cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
}
setTimeout(nc2, 0);
}); });
}; };
...@@ -1121,7 +1027,31 @@ iOSExec.nativeEvalAndFetch = function(func) { ...@@ -1121,7 +1027,31 @@ iOSExec.nativeEvalAndFetch = function(func) {
} }
}; };
module.exports = iOSExec; // Proxy the exec for bridge changes. See CB-10106
function cordovaExec() {
var cexec = require('cordova/exec');
var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function');
return (cexec_valid && execProxy !== cexec)? cexec : iOSExec;
}
function execProxy() {
cordovaExec().apply(null, arguments);
};
execProxy.nativeFetchMessages = function() {
return cordovaExec().nativeFetchMessages.apply(null, arguments);
};
execProxy.nativeEvalAndFetch = function() {
return cordovaExec().nativeEvalAndFetch.apply(null, arguments);
};
execProxy.nativeCallback = function() {
return cordovaExec().nativeCallback.apply(null, arguments);
};
module.exports = execProxy;
}); });
...@@ -1606,7 +1536,7 @@ exports.reset(); ...@@ -1606,7 +1536,7 @@ exports.reset();
}); });
// file: e:/cordova/cordova-ios/cordova-js-src/platform.js // file: /Users/shaz/Documents/Git/Apache/cordova-ios/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) { define("cordova/platform", function(require, exports, module) {
module.exports = { module.exports = {
......
...@@ -6,48 +6,22 @@ ...@@ -6,48 +6,22 @@
objectVersion = 46; objectVersion = 46;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0D62F8E153E44EF28A7721FA /* Toast+UIView.m in Sources */ = {isa = PBXBuildFile; fileRef = C6AD7DC485A4443E929A7620 /* Toast+UIView.m */; }; 0207DA581B56EA530066E2B4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0207DA571B56EA530066E2B4 /* Images.xcassets */; };
197FB026841240789EACC5D0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EF785F79A8B7472E92024450 /* QuartzCore.framework */; }; 1652DDD58CF34A1498E625ED /* Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 16148B5EDCEF4DF9AFAFFBFD /* Toast.m */; };
1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; }; 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; };
1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; }; 301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; };
302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; }; 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; };
302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; }; 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 302D95F014D2391D003F00A1 /* MainViewController.xib */; };
305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */; }; 33749F5BB3324522B9B72133 /* Toast+UIView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59CDBB99923A4A59BB0BE05C /* Toast+UIView.m */; };
3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */; }; 514390DFFD20442B8CBA57A8 /* CDVDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = CDBA542B40B54B64BBEBFF34 /* CDVDevice.m */; };
3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */; }; 76E4141F02C54632A2958ABB /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ABF4DDFD02E4BA6B0389239 /* QuartzCore.framework */; };
3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */; }; 9D9EA0F32FC14A24A5A164F3 /* AppDelegate+APPAppEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A96642F91D8415EB765C255 /* AppDelegate+APPAppEvent.m */; };
3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */; }; F56317B8B99C49AD91251E94 /* CDVPlugin+APPAppEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BCD18989D8F422D8F489E55 /* CDVPlugin+APPAppEvent.m */; };
3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */; }; BAE8FD12220D4585AA5CD416 /* APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA854E9E4884AC0A56C2D2A /* APPLocalNotification.m */; };
3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = 3088BBBC154F3926009F9C59 /* Default~iphone.png */; }; 92F7EF9D037E4BCC923AC249 /* APPLocalNotificationOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CCE6D58A7E4451E900381D6 /* APPLocalNotificationOptions.m */; };
308D05371370CCF300D202BF /* icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052E1370CCF300D202BF /* icon-72.png */; }; FD4791A0CBDD4750BFD038AA /* UIApplication+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = FC27C30D288A4122A611D054 /* UIApplication+APPLocalNotification.m */; };
308D05381370CCF300D202BF /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D052F1370CCF300D202BF /* icon.png */; }; 582BEB261DE344FDB231BC4C /* UILocalNotification+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = F4DD58B134D64BC79F7266E8 /* UILocalNotification+APPLocalNotification.m */; };
308D05391370CCF300D202BF /* icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 308D05301370CCF300D202BF /* icon@2x.png */; };
30B4F30019D5E07200D9F7D8 /* Default-667h.png in Resources */ = {isa = PBXBuildFile; fileRef = 30B4F2FD19D5E07200D9F7D8 /* Default-667h.png */; };
30B4F30119D5E07200D9F7D8 /* Default-736h.png in Resources */ = {isa = PBXBuildFile; fileRef = 30B4F2FE19D5E07200D9F7D8 /* Default-736h.png */; };
30B4F30219D5E07200D9F7D8 /* Default-Landscape-736h.png in Resources */ = {isa = PBXBuildFile; fileRef = 30B4F2FF19D5E07200D9F7D8 /* Default-Landscape-736h.png */; };
30C1856619D5FC0A00212699 /* icon-60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30C1856519D5FC0A00212699 /* icon-60@3x.png */; };
30FC414916E50CA1004E6F35 /* icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30FC414816E50CA1004E6F35 /* icon-72@2x.png */; };
5B1594DD16A7569C00FEF299 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */; };
7E7966DE1810823500FA85AD /* icon-40.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D41810823500FA85AD /* icon-40.png */; };
7E7966DF1810823500FA85AD /* icon-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D51810823500FA85AD /* icon-40@2x.png */; };
7E7966E01810823500FA85AD /* icon-50.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D61810823500FA85AD /* icon-50.png */; };
7E7966E11810823500FA85AD /* icon-50@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D71810823500FA85AD /* icon-50@2x.png */; };
7E7966E21810823500FA85AD /* icon-60.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D81810823500FA85AD /* icon-60.png */; };
7E7966E31810823500FA85AD /* icon-60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D91810823500FA85AD /* icon-60@2x.png */; };
7E7966E41810823500FA85AD /* icon-76.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DA1810823500FA85AD /* icon-76.png */; };
7E7966E51810823500FA85AD /* icon-76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DB1810823500FA85AD /* icon-76@2x.png */; };
7E7966E61810823500FA85AD /* icon-small.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DC1810823500FA85AD /* icon-small.png */; };
7E7966E71810823500FA85AD /* icon-small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DD1810823500FA85AD /* icon-small@2x.png */; };
C0912AD10EF243F1953F9047 /* Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C291FF26573452996FB3E0E /* Toast.m */; };
CC42D1F09F714070882869AF /* CDVDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = CE7DBE8F25C94EE78486BF2D /* CDVDevice.m */; };
D4A0D8761607E02300AEF8BB /* Default-568h@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */; };
BCDF7572D156489CA23538B0 /* AppDelegate+APPRegisterUserNotificationSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEC131F711411088C92EDA /* AppDelegate+APPRegisterUserNotificationSettings.m */; };
9E367FA486424D39BA150ECD /* APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E97D790684542B885DA3CF7 /* APPLocalNotification.m */; };
AFC7D470F50A47519DF04184 /* APPLocalNotificationOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CC79B3EDF31424E97DD9C43 /* APPLocalNotificationOptions.m */; };
704F898AC24049919D2CFDFF /* UIApplication+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA61515A5B64BA686E32943 /* UIApplication+APPLocalNotification.m */; };
C87EB797F6994CD084F57E41 /* UILocalNotification+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 31E97140586C4586969A25F4 /* UILocalNotification+APPLocalNotification.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
...@@ -68,66 +42,44 @@ ...@@ -68,66 +42,44 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
038EB824BC264EB38A1CC2B4 /* Toast+UIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Toast+UIView.h"; path = "cordova-plugin-x-toast/Toast+UIView.h"; sourceTree = "<group>"; }; 0207DA571B56EA530066E2B4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NotificationExample/Images.xcassets; sourceTree = SOURCE_ROOT; };
0C291FF26573452996FB3E0E /* Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Toast.m; path = "cordova-plugin-x-toast/Toast.m"; sourceTree = "<group>"; }; 12A96A2378B04BD089C1AC18 /* CDVDevice.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = CDVDevice.h; path = "cordova-plugin-device/CDVDevice.h"; sourceTree = "<group>"; };
16148B5EDCEF4DF9AFAFFBFD /* Toast.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = Toast.m; path = "cordova-plugin-x-toast/Toast.m"; sourceTree = "<group>"; };
1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; 1D3623240D0F684500981E51 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; 1D3623250D0F684500981E51 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
1D6058910D05DD3D006BFB54 /* NotificationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NotificationExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1D6058910D05DD3D006BFB54 /* NotificationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NotificationExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CordovaLib.xcodeproj; path = CordovaLib/CordovaLib.xcodeproj; sourceTree = "<group>"; }; 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CordovaLib.xcodeproj; path = CordovaLib/CordovaLib.xcodeproj; sourceTree = "<group>"; };
301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; }; 301BF56E109A69640062928A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = SOURCE_ROOT; };
302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; }; 302D95EE14D2391D003F00A1 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = "<group>"; };
302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; }; 302D95EF14D2391D003F00A1 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = "<group>"; };
302D95F014D2391D003F00A1 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = "<group>"; }; 302D95F014D2391D003F00A1 /* MainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainViewController.xib; sourceTree = "<group>"; };
305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 3047A50F1AB8059700498E2A /* build-debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "build-debug.xcconfig"; path = "cordova/build-debug.xcconfig"; sourceTree = SOURCE_ROOT; };
3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x~ipad.png"; sourceTree = "<group>"; }; 3047A5101AB8059700498E2A /* build-release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "build-release.xcconfig"; path = "cordova/build-release.xcconfig"; sourceTree = SOURCE_ROOT; };
3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = "<group>"; }; 3047A5111AB8059700498E2A /* build.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = build.xcconfig; path = cordova/build.xcconfig; sourceTree = SOURCE_ROOT; };
3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = "<group>"; };
3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = "<group>"; };
3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x~iphone.png"; sourceTree = "<group>"; };
3088BBBC154F3926009F9C59 /* Default~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default~iphone.png"; sourceTree = "<group>"; };
308D052E1370CCF300D202BF /* icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72.png"; sourceTree = "<group>"; };
308D052F1370CCF300D202BF /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
308D05301370CCF300D202BF /* icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon@2x.png"; sourceTree = "<group>"; };
30B4F2FD19D5E07200D9F7D8 /* Default-667h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h.png"; sourceTree = "<group>"; };
30B4F2FE19D5E07200D9F7D8 /* Default-736h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h.png"; sourceTree = "<group>"; };
30B4F2FF19D5E07200D9F7D8 /* Default-Landscape-736h.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-736h.png"; sourceTree = "<group>"; };
30C1856519D5FC0A00212699 /* icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-60@3x.png"; sourceTree = "<group>"; };
30FC414816E50CA1004E6F35 /* icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72@2x.png"; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* NotificationExample-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NotificationExample-Prefix.pch"; sourceTree = "<group>"; }; 32CA4F630368D1EE00C91783 /* NotificationExample-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NotificationExample-Prefix.pch"; sourceTree = "<group>"; };
528A07C507BD4FE3AD9EA09F /* CDVDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDevice.h; path = "cordova-plugin-device/CDVDevice.h"; sourceTree = "<group>"; }; 56566A9952BE44FE85B60CFE /* Toast.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = Toast.h; path = "cordova-plugin-x-toast/Toast.h"; sourceTree = "<group>"; };
5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 59CDBB99923A4A59BB0BE05C /* Toast+UIView.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = "Toast+UIView.m"; path = "cordova-plugin-x-toast/Toast+UIView.m"; sourceTree = "<group>"; };
7E7966D41810823500FA85AD /* icon-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-40.png"; sourceTree = "<group>"; }; 6ABF4DDFD02E4BA6B0389239 /* QuartzCore.framework */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
7E7966D51810823500FA85AD /* icon-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-40@2x.png"; sourceTree = "<group>"; }; 8D1107310486CEB800E47090 /* NotificationExample-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "NotificationExample-Info.plist"; path = "NotificationExample/NotificationExample-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = SOURCE_ROOT; };
7E7966D61810823500FA85AD /* icon-50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-50.png"; sourceTree = "<group>"; }; CDBA542B40B54B64BBEBFF34 /* CDVDevice.m */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.objc; name = CDVDevice.m; path = "cordova-plugin-device/CDVDevice.m"; sourceTree = "<group>"; };
7E7966D71810823500FA85AD /* icon-50@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-50@2x.png"; sourceTree = "<group>"; }; E0B807C73C054EB4B15B2B67 /* Toast+UIView.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Toast+UIView.h"; path = "cordova-plugin-x-toast/Toast+UIView.h"; sourceTree = "<group>"; };
7E7966D81810823500FA85AD /* icon-60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-60.png"; sourceTree = "<group>"; };
7E7966D91810823500FA85AD /* icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-60@2x.png"; sourceTree = "<group>"; };
7E7966DA1810823500FA85AD /* icon-76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-76.png"; sourceTree = "<group>"; };
7E7966DB1810823500FA85AD /* icon-76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-76@2x.png"; sourceTree = "<group>"; };
7E7966DC1810823500FA85AD /* icon-small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-small.png"; sourceTree = "<group>"; };
7E7966DD1810823500FA85AD /* icon-small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-small@2x.png"; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* NotificationExample-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "NotificationExample-Info.plist"; path = "../NotificationExample-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
C6AD7DC485A4443E929A7620 /* Toast+UIView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "Toast+UIView.m"; path = "cordova-plugin-x-toast/Toast+UIView.m"; sourceTree = "<group>"; };
CE7DBE8F25C94EE78486BF2D /* CDVDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVDevice.m; path = "cordova-plugin-device/CDVDevice.m"; sourceTree = "<group>"; };
D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x~iphone.png"; sourceTree = "<group>"; };
E754277EEB384C66B1431A9F /* Toast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Toast.h; path = "cordova-plugin-x-toast/Toast.h"; sourceTree = "<group>"; };
EB87FDF21871DA7A0020F90C /* merges */ = {isa = PBXFileReference; lastKnownFileType = folder; name = merges; path = ../../merges; sourceTree = "<group>"; };
EB87FDF31871DA8E0020F90C /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../../www; sourceTree = "<group>"; }; EB87FDF31871DA8E0020F90C /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../../www; sourceTree = "<group>"; };
EB87FDF41871DAF40020F90C /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = ../../config.xml; sourceTree = "<group>"; }; EB87FDF41871DAF40020F90C /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = ../../config.xml; sourceTree = "<group>"; };
EF785F79A8B7472E92024450 /* QuartzCore.framework */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; ED33DF2A687741AEAF9F8254 /* Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = "<group>"; };
F840E1F0165FE0F500CFE078 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = NotificationExample/config.xml; sourceTree = "<group>"; }; F840E1F0165FE0F500CFE078 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = NotificationExample/config.xml; sourceTree = "<group>"; };
83BEC131F711411088C92EDA /* AppDelegate+APPRegisterUserNotificationSettings.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "AppDelegate+APPRegisterUserNotificationSettings.m"; path = "../../../../plugins/cordova-plugin-registerusernotificationsettings/src/ios/AppDelegate+APPRegisterUserNotificationSettings.m"; sourceTree = "<group>"; fileEncoding = 4; }; 8A96642F91D8415EB765C255 /* AppDelegate+APPAppEvent.m */ = {isa = PBXFileReference; name = "AppDelegate+APPAppEvent.m"; path = "../../../../../cordova-plugin-app-event/src/ios/AppDelegate+APPAppEvent.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
A5FA75D5042F413EA3791296 /* AppDelegate+APPRegisterUserNotificationSettings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "AppDelegate+APPRegisterUserNotificationSettings.h"; path = "../../../../plugins/cordova-plugin-registerusernotificationsettings/src/ios/AppDelegate+APPRegisterUserNotificationSettings.h"; sourceTree = "<group>"; fileEncoding = 4; }; 0BCD18989D8F422D8F489E55 /* CDVPlugin+APPAppEvent.m */ = {isa = PBXFileReference; name = "CDVPlugin+APPAppEvent.m"; path = "../../../../../cordova-plugin-app-event/src/ios/CDVPlugin+APPAppEvent.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
1E97D790684542B885DA3CF7 /* APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "APPLocalNotification.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; }; F1424962B1CF4E1AADE57330 /* AppDelegate+APPAppEvent.h */ = {isa = PBXFileReference; name = "AppDelegate+APPAppEvent.h"; path = "../../../../../cordova-plugin-app-event/src/ios/AppDelegate+APPAppEvent.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
7CC79B3EDF31424E97DD9C43 /* APPLocalNotificationOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "APPLocalNotificationOptions.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotificationOptions.m"; sourceTree = "<group>"; fileEncoding = 4; }; 1099F744EF5B4C64B8B86342 /* CDVPlugin+APPAppEvent.h */ = {isa = PBXFileReference; name = "CDVPlugin+APPAppEvent.h"; path = "../../../../../cordova-plugin-app-event/src/ios/CDVPlugin+APPAppEvent.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
DCA61515A5B64BA686E32943 /* UIApplication+APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+APPLocalNotification.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UIApplication+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; }; 0CA854E9E4884AC0A56C2D2A /* APPLocalNotification.m */ = {isa = PBXFileReference; name = "APPLocalNotification.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
31E97140586C4586969A25F4 /* UILocalNotification+APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "UILocalNotification+APPLocalNotification.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UILocalNotification+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; }; 4CCE6D58A7E4451E900381D6 /* APPLocalNotificationOptions.m */ = {isa = PBXFileReference; name = "APPLocalNotificationOptions.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotificationOptions.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
A36117D2BDF54F4FB9655726 /* APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "APPLocalNotification.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; }; FC27C30D288A4122A611D054 /* UIApplication+APPLocalNotification.m */ = {isa = PBXFileReference; name = "UIApplication+APPLocalNotification.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UIApplication+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
A1794ABC64CB4F149CE892B8 /* APPLocalNotificationOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "APPLocalNotificationOptions.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotificationOptions.h"; sourceTree = "<group>"; fileEncoding = 4; }; F4DD58B134D64BC79F7266E8 /* UILocalNotification+APPLocalNotification.m */ = {isa = PBXFileReference; name = "UILocalNotification+APPLocalNotification.m"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UILocalNotification+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; };
7EA4BEA5695D48A19AEC8E54 /* UIApplication+APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "UIApplication+APPLocalNotification.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UIApplication+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; }; DC2E605769EA4623AAEB5D1E /* APPLocalNotification.h */ = {isa = PBXFileReference; name = "APPLocalNotification.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
3ECEAA4923104469833E2888 /* UILocalNotification+APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "UILocalNotification+APPLocalNotification.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UILocalNotification+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; }; 1E1EF3EF8F0441A69BBB6B9D /* APPLocalNotificationOptions.h */ = {isa = PBXFileReference; name = "APPLocalNotificationOptions.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/APPLocalNotificationOptions.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
289A8FD72DA0463E83787714 /* UIApplication+APPLocalNotification.h */ = {isa = PBXFileReference; name = "UIApplication+APPLocalNotification.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UIApplication+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
C7119118DB2247C0980B80AD /* UILocalNotification+APPLocalNotification.h */ = {isa = PBXFileReference; name = "UILocalNotification+APPLocalNotification.h"; path = "../../../../../cordova-plugin-local-notifications/src/ios/UILocalNotification+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -135,11 +87,8 @@ ...@@ -135,11 +87,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5B1594DD16A7569C00FEF299 /* AssetsLibrary.framework in Frameworks */,
301BF552109A68D80062928A /* libCordova.a in Frameworks */, 301BF552109A68D80062928A /* libCordova.a in Frameworks */,
288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */, 76E4141F02C54632A2958ABB /* QuartzCore.framework in Frameworks */,
305D5FD1115AB8F900A74A75 /* MobileCoreServices.framework in Frameworks */,
197FB026841240789EACC5D0 /* QuartzCore.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -172,7 +121,6 @@ ...@@ -172,7 +121,6 @@
children = ( children = (
EB87FDF41871DAF40020F90C /* config.xml */, EB87FDF41871DAF40020F90C /* config.xml */,
EB87FDF31871DA8E0020F90C /* www */, EB87FDF31871DA8E0020F90C /* www */,
EB87FDF21871DA7A0020F90C /* merges */,
EB87FDF11871DA420020F90C /* Staging */, EB87FDF11871DA420020F90C /* Staging */,
301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */, 301BF52D109A57CC0062928A /* CordovaLib.xcodeproj */,
080E96DDFE201D6D7F000001 /* Classes */, 080E96DDFE201D6D7F000001 /* Classes */,
...@@ -190,6 +138,7 @@ ...@@ -190,6 +138,7 @@
children = ( children = (
32CA4F630368D1EE00C91783 /* NotificationExample-Prefix.pch */, 32CA4F630368D1EE00C91783 /* NotificationExample-Prefix.pch */,
29B97316FDCFA39411CA2CEA /* main.m */, 29B97316FDCFA39411CA2CEA /* main.m */,
ED33DF2A687741AEAF9F8254 /* Bridging-Header.h */,
); );
name = "Other Sources"; name = "Other Sources";
path = NotificationExample; path = NotificationExample;
...@@ -198,8 +147,8 @@ ...@@ -198,8 +147,8 @@
29B97317FDCFA39411CA2CEA /* Resources */ = { 29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
308D052D1370CCF300D202BF /* icons */, 0207DA571B56EA530066E2B4 /* Images.xcassets */,
308D05311370CCF300D202BF /* splash */, 3047A50E1AB8057F00498E2A /* config */,
8D1107310486CEB800E47090 /* NotificationExample-Info.plist */, 8D1107310486CEB800E47090 /* NotificationExample-Info.plist */,
); );
name = Resources; name = Resources;
...@@ -209,10 +158,7 @@ ...@@ -209,10 +158,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = { 29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */, 6ABF4DDFD02E4BA6B0389239 /* QuartzCore.framework */,
288765FC0DF74451002DB57D /* CoreGraphics.framework */,
305D5FD0115AB8F900A74A75 /* MobileCoreServices.framework */,
EF785F79A8B7472E92024450 /* QuartzCore.framework */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -225,68 +171,41 @@ ...@@ -225,68 +171,41 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
307C750510C5A3420062BCA9 /* Plugins */ = { 3047A50E1AB8057F00498E2A /* config */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE7DBE8F25C94EE78486BF2D /* CDVDevice.m */, 3047A50F1AB8059700498E2A /* build-debug.xcconfig */,
528A07C507BD4FE3AD9EA09F /* CDVDevice.h */, 3047A5101AB8059700498E2A /* build-release.xcconfig */,
C6AD7DC485A4443E929A7620 /* Toast+UIView.m */, 3047A5111AB8059700498E2A /* build.xcconfig */,
0C291FF26573452996FB3E0E /* Toast.m */,
038EB824BC264EB38A1CC2B4 /* Toast+UIView.h */,
E754277EEB384C66B1431A9F /* Toast.h */,
83BEC131F711411088C92EDA /* AppDelegate+APPRegisterUserNotificationSettings.m */,
A5FA75D5042F413EA3791296 /* AppDelegate+APPRegisterUserNotificationSettings.h */,
1E97D790684542B885DA3CF7 /* APPLocalNotification.m */,
7CC79B3EDF31424E97DD9C43 /* APPLocalNotificationOptions.m */,
DCA61515A5B64BA686E32943 /* UIApplication+APPLocalNotification.m */,
31E97140586C4586969A25F4 /* UILocalNotification+APPLocalNotification.m */,
A36117D2BDF54F4FB9655726 /* APPLocalNotification.h */,
A1794ABC64CB4F149CE892B8 /* APPLocalNotificationOptions.h */,
7EA4BEA5695D48A19AEC8E54 /* UIApplication+APPLocalNotification.h */,
3ECEAA4923104469833E2888 /* UILocalNotification+APPLocalNotification.h */,
); );
name = Plugins; name = config;
path = NotificationExample/Plugins;
sourceTree = SOURCE_ROOT;
};
308D052D1370CCF300D202BF /* icons */ = {
isa = PBXGroup;
children = (
30C1856519D5FC0A00212699 /* icon-60@3x.png */,
7E7966D41810823500FA85AD /* icon-40.png */,
7E7966D51810823500FA85AD /* icon-40@2x.png */,
7E7966D61810823500FA85AD /* icon-50.png */,
7E7966D71810823500FA85AD /* icon-50@2x.png */,
7E7966D81810823500FA85AD /* icon-60.png */,
7E7966D91810823500FA85AD /* icon-60@2x.png */,
7E7966DA1810823500FA85AD /* icon-76.png */,
7E7966DB1810823500FA85AD /* icon-76@2x.png */,
7E7966DC1810823500FA85AD /* icon-small.png */,
7E7966DD1810823500FA85AD /* icon-small@2x.png */,
30FC414816E50CA1004E6F35 /* icon-72@2x.png */,
308D052E1370CCF300D202BF /* icon-72.png */,
308D052F1370CCF300D202BF /* icon.png */,
308D05301370CCF300D202BF /* icon@2x.png */,
);
path = icons;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
308D05311370CCF300D202BF /* splash */ = { 307C750510C5A3420062BCA9 /* Plugins */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
30B4F2FD19D5E07200D9F7D8 /* Default-667h.png */, CDBA542B40B54B64BBEBFF34 /* CDVDevice.m */,
30B4F2FE19D5E07200D9F7D8 /* Default-736h.png */, 12A96A2378B04BD089C1AC18 /* CDVDevice.h */,
30B4F2FF19D5E07200D9F7D8 /* Default-Landscape-736h.png */, 59CDBB99923A4A59BB0BE05C /* Toast+UIView.m */,
D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */, 16148B5EDCEF4DF9AFAFFBFD /* Toast.m */,
3088BBB7154F3926009F9C59 /* Default-Landscape@2x~ipad.png */, E0B807C73C054EB4B15B2B67 /* Toast+UIView.h */,
3088BBB8154F3926009F9C59 /* Default-Landscape~ipad.png */, 56566A9952BE44FE85B60CFE /* Toast.h */,
3088BBB9154F3926009F9C59 /* Default-Portrait@2x~ipad.png */, 8A96642F91D8415EB765C255 /* AppDelegate+APPAppEvent.m */,
3088BBBA154F3926009F9C59 /* Default-Portrait~ipad.png */, 0BCD18989D8F422D8F489E55 /* CDVPlugin+APPAppEvent.m */,
3088BBBB154F3926009F9C59 /* Default@2x~iphone.png */, F1424962B1CF4E1AADE57330 /* AppDelegate+APPAppEvent.h */,
3088BBBC154F3926009F9C59 /* Default~iphone.png */, 1099F744EF5B4C64B8B86342 /* CDVPlugin+APPAppEvent.h */,
0CA854E9E4884AC0A56C2D2A /* APPLocalNotification.m */,
4CCE6D58A7E4451E900381D6 /* APPLocalNotificationOptions.m */,
FC27C30D288A4122A611D054 /* UIApplication+APPLocalNotification.m */,
F4DD58B134D64BC79F7266E8 /* UILocalNotification+APPLocalNotification.m */,
DC2E605769EA4623AAEB5D1E /* APPLocalNotification.h */,
1E1EF3EF8F0441A69BBB6B9D /* APPLocalNotificationOptions.h */,
289A8FD72DA0463E83787714 /* UIApplication+APPLocalNotification.h */,
C7119118DB2247C0980B80AD /* UILocalNotification+APPLocalNotification.h */,
); );
path = splash; name = Plugins;
sourceTree = "<group>"; path = NotificationExample/Plugins;
sourceTree = SOURCE_ROOT;
}; };
EB87FDF11871DA420020F90C /* Staging */ = { EB87FDF11871DA420020F90C /* Staging */ = {
isa = PBXGroup; isa = PBXGroup;
...@@ -332,14 +251,7 @@ ...@@ -332,14 +251,7 @@
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 1; hasScannedForEncodings = 1;
knownRegions = ( knownRegions = (
English,
Japanese,
French,
German,
en, en,
es,
de,
se,
); );
mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
projectDirPath = ""; projectDirPath = "";
...@@ -371,32 +283,8 @@ ...@@ -371,32 +283,8 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7E7966E41810823500FA85AD /* icon-76.png in Resources */,
7E7966DF1810823500FA85AD /* icon-40@2x.png in Resources */,
308D05371370CCF300D202BF /* icon-72.png in Resources */,
30B4F30119D5E07200D9F7D8 /* Default-736h.png in Resources */,
308D05381370CCF300D202BF /* icon.png in Resources */,
308D05391370CCF300D202BF /* icon@2x.png in Resources */,
302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */, 302D95F214D2391D003F00A1 /* MainViewController.xib in Resources */,
7E7966E01810823500FA85AD /* icon-50.png in Resources */, 0207DA581B56EA530066E2B4 /* Images.xcassets in Resources */,
7E7966E31810823500FA85AD /* icon-60@2x.png in Resources */,
7E7966E61810823500FA85AD /* icon-small.png in Resources */,
3088BBBD154F3926009F9C59 /* Default-Landscape@2x~ipad.png in Resources */,
3088BBBE154F3926009F9C59 /* Default-Landscape~ipad.png in Resources */,
3088BBBF154F3926009F9C59 /* Default-Portrait@2x~ipad.png in Resources */,
7E7966E71810823500FA85AD /* icon-small@2x.png in Resources */,
3088BBC0154F3926009F9C59 /* Default-Portrait~ipad.png in Resources */,
30B4F30019D5E07200D9F7D8 /* Default-667h.png in Resources */,
7E7966DE1810823500FA85AD /* icon-40.png in Resources */,
3088BBC1154F3926009F9C59 /* Default@2x~iphone.png in Resources */,
7E7966E21810823500FA85AD /* icon-60.png in Resources */,
3088BBC2154F3926009F9C59 /* Default~iphone.png in Resources */,
D4A0D8761607E02300AEF8BB /* Default-568h@2x~iphone.png in Resources */,
30B4F30219D5E07200D9F7D8 /* Default-Landscape-736h.png in Resources */,
30C1856619D5FC0A00212699 /* icon-60@3x.png in Resources */,
7E7966E11810823500FA85AD /* icon-50@2x.png in Resources */,
7E7966E51810823500FA85AD /* icon-76@2x.png in Resources */,
30FC414916E50CA1004E6F35 /* icon-72@2x.png in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -428,14 +316,15 @@ ...@@ -428,14 +316,15 @@
1D60589B0D05DD56006BFB54 /* main.m in Sources */, 1D60589B0D05DD56006BFB54 /* main.m in Sources */,
1D3623260D0F684500981E51 /* AppDelegate.m in Sources */, 1D3623260D0F684500981E51 /* AppDelegate.m in Sources */,
302D95F114D2391D003F00A1 /* MainViewController.m in Sources */, 302D95F114D2391D003F00A1 /* MainViewController.m in Sources */,
CC42D1F09F714070882869AF /* CDVDevice.m in Sources */, 514390DFFD20442B8CBA57A8 /* CDVDevice.m in Sources */,
0D62F8E153E44EF28A7721FA /* Toast+UIView.m in Sources */, 33749F5BB3324522B9B72133 /* Toast+UIView.m in Sources */,
C0912AD10EF243F1953F9047 /* Toast.m in Sources */, 1652DDD58CF34A1498E625ED /* Toast.m in Sources */,
BCDF7572D156489CA23538B0 /* AppDelegate+APPRegisterUserNotificationSettings.m in Sources */, 9D9EA0F32FC14A24A5A164F3 /* AppDelegate+APPAppEvent.m in Sources */,
9E367FA486424D39BA150ECD /* APPLocalNotification.m in Sources */, F56317B8B99C49AD91251E94 /* CDVPlugin+APPAppEvent.m in Sources */,
AFC7D470F50A47519DF04184 /* APPLocalNotificationOptions.m in Sources */, BAE8FD12220D4585AA5CD416 /* APPLocalNotification.m in Sources */,
704F898AC24049919D2CFDFF /* UIApplication+APPLocalNotification.m in Sources */, 92F7EF9D037E4BCC923AC249 /* APPLocalNotificationOptions.m in Sources */,
C87EB797F6994CD084F57E41 /* UILocalNotification+APPLocalNotification.m in Sources */, FD4791A0CBDD4750BFD038AA /* UIApplication+APPLocalNotification.m in Sources */,
582BEB261DE344FDB231BC4C /* UILocalNotification+APPLocalNotification.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -452,8 +341,12 @@ ...@@ -452,8 +341,12 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
1D6058940D05DD3E006BFB54 /* Debug */ = { 1D6058940D05DD3E006BFB54 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 3047A50F1AB8059700498E2A /* build-debug.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
...@@ -463,29 +356,20 @@ ...@@ -463,29 +356,20 @@
GCC_THUMB_SUPPORT = NO; GCC_THUMB_SUPPORT = NO;
GCC_VERSION = ""; GCC_VERSION = "";
INFOPLIST_FILE = "NotificationExample/NotificationExample-Info.plist"; INFOPLIST_FILE = "NotificationExample/NotificationExample-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0; LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
OTHER_LDFLAGS = (
"-weak_framework",
CoreFoundation,
"-weak_framework",
UIKit,
"-weak_framework",
AVFoundation,
"-weak_framework",
CoreMedia,
"-weak-lSystem",
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = de.appplant.localnotification.example; PRODUCT_BUNDLE_IDENTIFIER = de.appplant.localnotification.example;
PRODUCT_NAME = NotificationExample; PRODUCT_NAME = NotificationExample;
TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
1D6058950D05DD3E006BFB54 /* Release */ = { 1D6058950D05DD3E006BFB54 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 3047A5101AB8059700498E2A /* build-release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
...@@ -493,28 +377,16 @@ ...@@ -493,28 +377,16 @@
GCC_THUMB_SUPPORT = NO; GCC_THUMB_SUPPORT = NO;
GCC_VERSION = ""; GCC_VERSION = "";
INFOPLIST_FILE = "NotificationExample/NotificationExample-Info.plist"; INFOPLIST_FILE = "NotificationExample/NotificationExample-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
OTHER_LDFLAGS = (
"-weak_framework",
CoreFoundation,
"-weak_framework",
UIKit,
"-weak_framework",
AVFoundation,
"-weak_framework",
CoreMedia,
"-weak-lSystem",
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = de.appplant.localnotification.example; PRODUCT_BUNDLE_IDENTIFIER = de.appplant.localnotification.example;
PRODUCT_NAME = NotificationExample; PRODUCT_NAME = NotificationExample;
TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };
C01FCF4F08A954540054247B /* Debug */ = { C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 3047A5111AB8059700498E2A /* build.xcconfig */;
buildSettings = { buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
...@@ -522,7 +394,6 @@ ...@@ -522,7 +394,6 @@
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = c99; GCC_C_LANGUAGE_STANDARD = c99;
GCC_THUMB_SUPPORT = NO; GCC_THUMB_SUPPORT = NO;
...@@ -532,34 +403,17 @@ ...@@ -532,34 +403,17 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"",
"\"$(OBJROOT)/UninstalledProducts/include\"",
"\"$(BUILT_PRODUCTS_DIR)\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"-weak_framework",
CoreFoundation,
"-weak_framework",
UIKit,
"-weak_framework",
AVFoundation,
"-weak_framework",
CoreMedia,
"-weak-lSystem",
"-ObjC",
);
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
USER_HEADER_SEARCH_PATHS = "";
}; };
name = Debug; name = Debug;
}; };
C01FCF5008A954540054247B /* Release */ = { C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 3047A5111AB8059700498E2A /* build.xcconfig */;
buildSettings = { buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
...@@ -567,7 +421,6 @@ ...@@ -567,7 +421,6 @@
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99; GCC_C_LANGUAGE_STANDARD = c99;
GCC_THUMB_SUPPORT = NO; GCC_THUMB_SUPPORT = NO;
GCC_VERSION = ""; GCC_VERSION = "";
...@@ -576,28 +429,8 @@ ...@@ -576,28 +429,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"\"$(TARGET_BUILD_DIR)/usr/local/lib/include\"",
"\"$(OBJROOT)/UninstalledProducts/include\"",
"\"$(BUILT_PRODUCTS_DIR)\"",
"\"$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include\"",
);
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
OTHER_LDFLAGS = (
"-weak_framework",
CoreFoundation,
"-weak_framework",
UIKit,
"-weak_framework",
AVFoundation,
"-weak_framework",
CoreMedia,
"-weak-lSystem",
"-ObjC",
);
SDKROOT = iphoneos; SDKROOT = iphoneos;
SKIP_INSTALL = NO; SKIP_INSTALL = NO;
USER_HEADER_SEARCH_PATHS = "";
}; };
name = Release; name = Release;
}; };
......
...@@ -25,18 +25,9 @@ ...@@ -25,18 +25,9 @@
// Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved. // Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved.
// //
#import <UIKit/UIKit.h>
#import <Cordova/CDVViewController.h> #import <Cordova/CDVViewController.h>
#import <Cordova/CDVAppDelegate.h>
@interface AppDelegate : NSObject <UIApplicationDelegate>{} @interface AppDelegate : CDVAppDelegate {}
// invoke string is passed to your app on launch, this is only valid if you
// edit NotificationExample-Info.plist to add a protocol
// a simple tutorial can be found here :
// http://iphonedevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html
@property (nonatomic, strong) IBOutlet UIWindow* window;
@property (nonatomic, strong) IBOutlet CDVViewController* viewController;
@end @end
...@@ -28,128 +28,12 @@ ...@@ -28,128 +28,12 @@
#import "AppDelegate.h" #import "AppDelegate.h"
#import "MainViewController.h" #import "MainViewController.h"
#import <Cordova/CDVPlugin.h>
@implementation AppDelegate @implementation AppDelegate
@synthesize window, viewController;
- (id)init
{
/** If you need to do any extra app-specific initialization, you can do it here
* -jm
**/
NSHTTPCookieStorage* cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
[cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
int cacheSizeMemory = 8 * 1024 * 1024; // 8MB
int cacheSizeDisk = 32 * 1024 * 1024; // 32MB
#if __has_feature(objc_arc)
NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"];
#else
NSURLCache* sharedCache = [[[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"] autorelease];
#endif
[NSURLCache setSharedURLCache:sharedCache];
self = [super init];
return self;
}
#pragma mark UIApplicationDelegate implementation
/**
* This is main kick off after the app inits, the views and Settings are setup here. (preferred - iOS4 and up)
*/
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{ {
CGRect screenBounds = [[UIScreen mainScreen] bounds]; self.viewController = [[MainViewController alloc] init];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
#if __has_feature(objc_arc)
self.window = [[UIWindow alloc] initWithFrame:screenBounds];
#else
self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease];
#endif
self.window.autoresizesSubviews = YES;
#if __has_feature(objc_arc)
self.viewController = [[MainViewController alloc] init];
#else
self.viewController = [[[MainViewController alloc] init] autorelease];
#endif
// Set your app's start page by setting the <content src='foo.html' /> tag in config.xml.
// If necessary, uncomment the line below to override it.
// self.viewController.startPage = @"index.html";
// NOTE: To customize the view's frame size (which defaults to full screen), override
// [self.viewController viewWillAppear:] in your view controller.
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
// this happens while we are running ( in the background, or from within our own app )
// only valid if NotificationExample-Info.plist specifies a protocol to handle
- (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation
{
if (!url) {
return NO;
}
// all plugins will get the notification, and their handlers will be called
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
return YES;
}
// repost all remote and local notification using the default NSNotificationCenter so multiple plugins may respond
- (void) application:(UIApplication*)application
didReceiveLocalNotification:(UILocalNotification*)notification
{
// re-post ( broadcast )
[[NSNotificationCenter defaultCenter] postNotificationName:CDVLocalNotification object:notification];
}
#ifndef DISABLE_PUSH_NOTIFICATIONS
- (void) application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
// re-post ( broadcast )
NSString* token = [[[[deviceToken description]
stringByReplacingOccurrencesOfString:@"<" withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""]
stringByReplacingOccurrencesOfString:@" " withString:@""];
[[NSNotificationCenter defaultCenter] postNotificationName:CDVRemoteNotification object:token];
}
- (void) application:(UIApplication*)application
didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
// re-post ( broadcast )
[[NSNotificationCenter defaultCenter] postNotificationName:CDVRemoteNotificationError object:error];
}
#endif
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
#else
- (UIInterfaceOrientationMask)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
#endif
{
// iPhone doesn't support upside down by default, while the iPad does. Override to allow all orientations always, and let the root view controller decide what's allowed (the supported orientations mask gets intersected).
NSUInteger supportedInterfaceOrientations = (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationLandscapeLeft) | (1 << UIInterfaceOrientationLandscapeRight) | (1 << UIInterfaceOrientationPortraitUpsideDown);
return supportedInterfaceOrientations;
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
{
[[NSURLCache sharedURLCache] removeAllCachedResponses];
} }
@end @end
...@@ -99,36 +99,6 @@ ...@@ -99,36 +99,6 @@
} }
*/ */
#pragma mark UIWebDelegate implementation
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
// Black base color for background matches the native apps
theWebView.backgroundColor = [UIColor blackColor];
return [super webViewDidFinishLoad:theWebView];
}
/* Comment out the block below to over-ride */
/*
- (void) webViewDidStartLoad:(UIWebView*)theWebView
{
return [super webViewDidStartLoad:theWebView];
}
- (void) webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
{
return [super webView:theWebView didFailLoadWithError:error];
}
- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
{
return [super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
}
*/
@end @end
@implementation MainCommandDelegate @implementation MainCommandDelegate
......
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"filename" : "icon-small.png",
"scale" : "1x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"filename" : "icon-small@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"filename" : "icon-small@3x.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"filename" : "icon-40@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"filename" : "icon-60@2x.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "57x57",
"filename" : "icon.png",
"scale" : "1x"
},
{
"idiom" : "iphone",
"size" : "57x57",
"filename" : "icon@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"filename" : "icon-60@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"filename" : "icon-60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"filename" : "icon-small.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"filename" : "icon-small@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"filename" : "icon-40.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"filename" : "icon-40@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "50x50",
"filename" : "icon-50.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "50x50",
"filename" : "icon-50@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "72x72",
"filename" : "icon-72.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "72x72",
"filename" : "icon-72@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"filename" : "icon-76.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"filename" : "icon-76@2x.png",
"scale" : "2x"
},
{
"size" : "24x24",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"scale" : "2x",
"role" : "longLook",
"subtype" : "42mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
...@@ -11,43 +11,9 @@ ...@@ -11,43 +11,9 @@
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>icon.png</string> <string>icon.png</string>
<key>CFBundleIcons</key> <key>CFBundleIcons</key>
<dict> <dict/>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>icon-40</string>
<string>icon-small</string>
<string>icon-60</string>
<string>icon.png</string>
<string>icon@2x</string>
<string>icon-72</string>
<string>icon-72@2x</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundleIcons~ipad</key> <key>CFBundleIcons~ipad</key>
<dict> <dict/>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>icon-small</string>
<string>icon-40</string>
<string>icon-50</string>
<string>icon-76</string>
<string>icon-60</string>
<string>icon</string>
<string>icon@2x</string>
<string>icon-72</string>
<string>icon-72@2x</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>de.appplant.localnotification.example</string> <string>de.appplant.localnotification.example</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
...@@ -70,112 +36,9 @@ ...@@ -70,112 +36,9 @@
<true/> <true/>
</dict> </dict>
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string/> <string></string>
<key>NSMainNibFile~ipad</key> <key>NSMainNibFile~ipad</key>
<string/> <string></string>
<key>UILaunchImages</key>
<array>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 480}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{320, 480}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-568h</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-568h</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-667h</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-667h</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-736h</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-Landscape-736h</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-Portrait</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-Landscape</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
</array>
<key>UIRequiresFullScreen</key> <key>UIRequiresFullScreen</key>
<true/> <true/>
</dict> </dict>
......
../../../../../plugins/cordova-plugin-registerusernotificationsettings/src/ios/AppDelegate+APPRegisterUserNotificationSettings.h
\ No newline at end of file
../../../../../plugins/cordova-plugin-registerusernotificationsettings/src/ios/AppDelegate+APPRegisterUserNotificationSettings.m
\ No newline at end of file
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
<preference name="KeyboardDisplayRequiresUserAction" value="true" /> <preference name="KeyboardDisplayRequiresUserAction" value="true" />
<preference name="MediaPlaybackRequiresUserAction" value="false" /> <preference name="MediaPlaybackRequiresUserAction" value="false" />
<preference name="SuppressesIncrementalRendering" value="false" /> <preference name="SuppressesIncrementalRendering" value="false" />
<preference name="SuppressesLongPressGesture" value="false" />
<preference name="Suppresses3DTouchGesture" value="false" />
<preference name="GapBetweenPages" value="0" /> <preference name="GapBetweenPages" value="0" />
<preference name="PageLength" value="0" /> <preference name="PageLength" value="0" />
<preference name="PaginationBreakingMode" value="page" /> <preference name="PaginationBreakingMode" value="page" />
...@@ -14,6 +16,18 @@ ...@@ -14,6 +16,18 @@
<feature name="LocalStorage"> <feature name="LocalStorage">
<param name="ios-package" value="CDVLocalStorage" /> <param name="ios-package" value="CDVLocalStorage" />
</feature> </feature>
<feature name="HandleOpenUrl">
<param name="ios-package" value="CDVHandleOpenURL" />
<param name="onload" value="true" />
</feature>
<feature name="IntentAndNavigationFilter">
<param name="ios-package" value="CDVIntentAndNavigationFilter" />
<param name="onload" value="true" />
</feature>
<feature name="GestureHandler">
<param name="ios-package" value="CDVGestureHandler" />
<param name="onload" value="true" />
</feature>
<feature name="Device"> <feature name="Device">
<param name="ios-package" value="CDVDevice" /> <param name="ios-package" value="CDVDevice" />
</feature> </feature>
......
...@@ -19,18 +19,40 @@ ...@@ -19,18 +19,40 @@
under the License. under the License.
*/ */
var build = require('./lib/build'), var args = process.argv;
args = process.argv; var Api = require('./Api');
var nopt = require('nopt');
// Handle help flag var path = require('path');
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(args[2]) > -1) {
build.help(); // Support basic help commands
} else { if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
build.run(args).done(function() { require('./lib/build').help();
process.exit(0);
}
// Parse arguments
var buildOpts = nopt({
'verbose' : Boolean,
'silent' : Boolean,
'archs': String,
'debug': Boolean,
'release': Boolean,
'device': Boolean,
'emulator': Boolean,
'codeSignIdentity': String,
'codeSignResourceRules': String,
'provisioningProfile': String,
'buildConfig' : String,
'noSign' : Boolean
}, {'-r': '--release'}, args);
// Make buildOptions compatible with PlatformApi build method spec
buildOpts.argv = buildOpts.argv.remain;
new Api().build(buildOpts).done(function() {
console.log('** BUILD SUCCEEDED **'); console.log('** BUILD SUCCEEDED **');
}, function(err) { }, function(err) {
var errorMessage = (err && err.stack) ? err.stack : err; var errorMessage = (err && err.stack) ? err.stack : err;
console.error(errorMessage); console.error(errorMessage);
process.exit(2); process.exit(2);
}); });
}
\ No newline at end of file
...@@ -22,4 +22,7 @@ ...@@ -22,4 +22,7 @@
// //
#include "build.xcconfig" #include "build.xcconfig"
GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1
#include "build-extras.xcconfig" #include "build-extras.xcconfig"
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
// Settings are overridden by configuration-level .xcconfig file (build-release/build-debug). // Settings are overridden by configuration-level .xcconfig file (build-release/build-debug).
// //
HEADER_SEARCH_PATHS = "$(TARGET_BUILD_DIR)/usr/local/lib/include" "$(OBJROOT)/UninstalledProducts/include" "$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include" "$(BUILT_PRODUCTS_DIR)"
IPHONEOS_DEPLOYMENT_TARGET = 8.0
OTHER_LDFLAGS = -ObjC
TARGETED_DEVICE_FAMILY = 1,2
// Type of signing identity used for codesigning, resolves to first match of given type. // Type of signing identity used for codesigning, resolves to first match of given type.
// "iPhone Developer": Development builds (default, local only; iOS Development certificate) or "iPhone Distribution": Distribution builds (Adhoc/In-House/AppStore; iOS Distribution certificate) // "iPhone Developer": Development builds (default, local only; iOS Development certificate) or "iPhone Distribution": Distribution builds (Adhoc/In-House/AppStore; iOS Distribution certificate)
...@@ -30,3 +34,9 @@ CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer ...@@ -30,3 +34,9 @@ CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
// (CB-9721) Set ENABLE_BITCODE to NO in build.xcconfig // (CB-9721) Set ENABLE_BITCODE to NO in build.xcconfig
ENABLE_BITCODE = NO ENABLE_BITCODE = NO
// (CB-9719) Set CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES to YES in build.xcconfig
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES
// (CB-10072)
SWIFT_OBJC_BRIDGING_HEADER = $(PROJECT_DIR)/$(PROJECT_NAME)/Bridging-Header.h
\ No newline at end of file
...@@ -23,10 +23,10 @@ var check_reqs = require('./lib/check_reqs'); ...@@ -23,10 +23,10 @@ var check_reqs = require('./lib/check_reqs');
// check for help flag // check for help flag
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) > -1) { if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) > -1) {
check_reqs.help(); console.log('Usage: check_reqs or node check_reqs');
} else { } else {
check_reqs.run().done(null, function (err) { check_reqs.run().done(null, function (err) {
console.error('Failed to check requirements due to ' + err); console.error('Failed to check requirements due to ' + err);
process.exit(2); process.exit(2);
}); });
} }
\ No newline at end of file
...@@ -19,11 +19,17 @@ ...@@ -19,11 +19,17 @@
under the License. under the License.
*/ */
var clean = require('./lib/clean'); var Api = require('./Api');
var path = require('path');
clean.run(process.argv).done(function () { if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
console.log('Cleans the project directory.');
process.exit(0);
}
new Api().clean({argv: process.argv.slice(2)}).done(function() {
console.log('** CLEAN SUCCEEDED **'); console.log('** CLEAN SUCCEEDED **');
}, function(err) { },function(err) {
console.error(err); console.error(err);
process.exit(2); process.exit(2);
}); });
\ No newline at end of file
...@@ -14,12 +14,6 @@ ...@@ -14,12 +14,6 @@
:: KIND, either express or implied. See the License for the :: KIND, either express or implied. See the License for the
:: specific language governing permissions and limitations :: specific language governing permissions and limitations
:: under the License :: under the License
@ECHO OFF @ECHO OFF
SET script_path="%~dp0clean" ECHO WARN: The 'clean' command is not available for cordova-ios on windows machines.>&2
IF EXIST %script_path% (
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'clean' script in 'cordova' folder, aborting...>&2
EXIT /B 1
)
\ No newline at end of file
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
<preference name="KeyboardDisplayRequiresUserAction" value="true" /> <preference name="KeyboardDisplayRequiresUserAction" value="true" />
<preference name="MediaPlaybackRequiresUserAction" value="false" /> <preference name="MediaPlaybackRequiresUserAction" value="false" />
<preference name="SuppressesIncrementalRendering" value="false" /> <preference name="SuppressesIncrementalRendering" value="false" />
<preference name="SuppressesLongPressGesture" value="false" />
<preference name="Suppresses3DTouchGesture" value="false" />
<preference name="GapBetweenPages" value="0" /> <preference name="GapBetweenPages" value="0" />
<preference name="PageLength" value="0" /> <preference name="PageLength" value="0" />
<preference name="PaginationBreakingMode" value="page" /> <!-- page, column --> <preference name="PaginationBreakingMode" value="page" /> <!-- page, column -->
...@@ -37,4 +39,17 @@ ...@@ -37,4 +39,17 @@
<feature name="LocalStorage"> <feature name="LocalStorage">
<param name="ios-package" value="CDVLocalStorage"/> <param name="ios-package" value="CDVLocalStorage"/>
</feature> </feature>
<feature name="HandleOpenUrl">
<param name="ios-package" value="CDVHandleOpenURL"/>
<param name="onload" value="true"/>
</feature>
<feature name="IntentAndNavigationFilter">
<param name="ios-package" value="CDVIntentAndNavigationFilter"/>
<param name="onload" value="true"/>
</feature>
<feature name="GestureHandler">
<param name="ios-package" value="CDVGestureHandler"/>
<param name="onload" value="true"/>
</feature>
</widget> </widget>
...@@ -20,96 +20,87 @@ ...@@ -20,96 +20,87 @@
/*jshint node: true*/ /*jshint node: true*/
var Q = require('q'), var Q = require('q'),
nopt = require('nopt'),
path = require('path'), path = require('path'),
shell = require('shelljs'), shell = require('shelljs'),
spawn = require('./spawn'), spawn = require('./spawn'),
check_reqs = require('./check_reqs'), check_reqs = require('./check_reqs'),
fs = require('fs'); fs = require('fs');
var events = require('cordova-common').events;
var projectPath = path.join(__dirname, '..', '..'); var projectPath = path.join(__dirname, '..', '..');
var projectName = null; var projectName = null;
module.exports.run = function (argv) { module.exports.run = function (buildOpts) {
var args = nopt({ buildOpts = buildOpts || {};
// "archs": String, // TODO: add support for building different archs
'debug': Boolean, if (buildOpts.debug && buildOpts.release) {
'release': Boolean,
'device': Boolean,
'emulator': Boolean,
'codeSignIdentity': String,
'codeSignResourceRules': String,
'provisioningProfile': String,
'buildConfig' : String
}, {'-r': '--release'}, argv);
if (args.debug && args.release) {
return Q.reject('Only one of "debug"/"release" options should be specified'); return Q.reject('Only one of "debug"/"release" options should be specified');
} }
if (args.device && args.emulator) { if (buildOpts.device && buildOpts.emulator) {
return Q.reject('Only one of "device"/"emulator" options should be specified'); return Q.reject('Only one of "device"/"emulator" options should be specified');
} }
if(args.buildConfig) { if(buildOpts.buildConfig) {
if(!fs.existsSync(args.buildConfig)) { if(!fs.existsSync(buildOpts.buildConfig)) {
return Q.reject('Build config file does not exist:' + args.buildConfig); return Q.reject('Build config file does not exist:' + buildOpts.buildConfig);
} }
console.log('Reading build config file:', path.resolve(args.buildConfig)); events.emit('log','Reading build config file:', path.resolve(buildOpts.buildConfig));
var buildConfig = JSON.parse(fs.readFileSync(args.buildConfig, 'utf-8')); var buildConfig = JSON.parse(fs.readFileSync(buildOpts.buildConfig, 'utf-8'));
if(buildConfig.ios) { if(buildConfig.ios) {
var buildType = args.release ? 'release' : 'debug'; var buildType = buildOpts.release ? 'release' : 'debug';
var config = buildConfig.ios[buildType]; var config = buildConfig.ios[buildType];
if(config) { if(config) {
['codeSignIdentity', 'codeSignResourceRules', 'provisioningProfile'].forEach( ['codeSignIdentity', 'codeSignResourceRules', 'provisioningProfile'].forEach(
function(key) { function(key) {
args[key] = args[key] || config[key]; buildOpts[key] = buildOpts[key] || config[key];
}); });
} }
} }
} }
return check_reqs.run().then(function () { return check_reqs.run().then(function () {
return findXCodeProjectIn(projectPath); return findXCodeProjectIn(projectPath);
}).then(function (name) { }).then(function (name) {
projectName = name; projectName = name;
var extraConfig = ''; var extraConfig = '';
if (args.codeSignIdentity) { if (buildOpts.codeSignIdentity) {
extraConfig += 'CODE_SIGN_IDENTITY = ' + args.codeSignIdentity + '\n'; extraConfig += 'CODE_SIGN_IDENTITY = ' + buildOpts.codeSignIdentity + '\n';
extraConfig += 'CODE_SIGN_IDENTITY[sdk=iphoneos*] = ' + args.codeSignIdentity + '\n'; extraConfig += 'CODE_SIGN_IDENTITY[sdk=iphoneos*] = ' + buildOpts.codeSignIdentity + '\n';
} }
if (args.codeSignResourceRules) { if (buildOpts.codeSignResourceRules) {
extraConfig += 'CODE_SIGN_RESOURCE_RULES_PATH = ' + args.codeSignResourceRules + '\n'; extraConfig += 'CODE_SIGN_RESOURCE_RULES_PATH = ' + buildOpts.codeSignResourceRules + '\n';
} }
if (args.provisioningProfile) { if (buildOpts.provisioningProfile) {
extraConfig += 'PROVISIONING_PROFILE = ' + args.provisioningProfile + '\n'; extraConfig += 'PROVISIONING_PROFILE = ' + buildOpts.provisioningProfile + '\n';
} }
return Q.nfcall(fs.writeFile, path.join(__dirname, '..', 'build-extras.xcconfig'), extraConfig, 'utf-8'); return Q.nfcall(fs.writeFile, path.join(__dirname, '..', 'build-extras.xcconfig'), extraConfig, 'utf-8');
}).then(function () { }).then(function () {
var configuration = args.release ? 'Release' : 'Debug'; var configuration = buildOpts.release ? 'Release' : 'Debug';
console.log('Building project : ' + path.join(projectPath, projectName + '.xcodeproj')); events.emit('log','Building project : ' + path.join(projectPath, projectName + '.xcodeproj'));
console.log('\tConfiguration : ' + configuration); events.emit('log','\tConfiguration : ' + configuration);
console.log('\tPlatform : ' + (args.device ? 'device' : 'emulator')); events.emit('log','\tPlatform : ' + (buildOpts.device ? 'device' : 'emulator'));
var xcodebuildArgs = getXcodeArgs(projectName, projectPath, configuration, args.device); var xcodebuildArgs = getXcodeArgs(projectName, projectPath, configuration, buildOpts.device);
return spawn('xcodebuild', xcodebuildArgs, projectPath); return spawn('xcodebuild', xcodebuildArgs, projectPath);
}).then(function () { }).then(function () {
if (!args.device) { if (!buildOpts.device || buildOpts.noSign) {
return; return;
} }
var buildOutputDir = path.join(projectPath, 'build', 'device'); var buildOutputDir = path.join(projectPath, 'build', 'device');
var pathToApp = path.join(buildOutputDir, projectName + '.app'); var pathToApp = path.join(buildOutputDir, projectName + '.app');
var pathToIpa = path.join(buildOutputDir, projectName + '.ipa'); var pathToIpa = path.join(buildOutputDir, projectName + '.ipa');
var xcRunArgs = ['-sdk', 'iphoneos', 'PackageApplication', var xcRunArgs = ['-sdk', 'iphoneos', 'PackageApplication',
'-v', pathToApp, '-v', pathToApp,
'-o', pathToIpa]; '-o', pathToIpa];
if (args.codeSignIdentity) { if (buildOpts.codeSignIdentity) {
xcRunArgs.concat('--sign', args.codeSignIdentity); xcRunArgs.concat('--sign', buildOpts.codeSignIdentity);
} }
if (args.provisioningProfile) { if (buildOpts.provisioningProfile) {
xcRunArgs.concat('--embed', args.provisioningProfile); xcRunArgs.concat('--embed', buildOpts.provisioningProfile);
} }
return spawn('xcrun', xcRunArgs, projectPath); return spawn('xcrun', xcRunArgs, projectPath);
}); });
...@@ -125,12 +116,12 @@ function findXCodeProjectIn(projectPath) { ...@@ -125,12 +116,12 @@ function findXCodeProjectIn(projectPath) {
var xcodeProjFiles = shell.ls(projectPath).filter(function (name) { var xcodeProjFiles = shell.ls(projectPath).filter(function (name) {
return path.extname(name) === '.xcodeproj'; return path.extname(name) === '.xcodeproj';
}); });
if (xcodeProjFiles.length === 0) { if (xcodeProjFiles.length === 0) {
return Q.reject('No Xcode project found in ' + projectPath); return Q.reject('No Xcode project found in ' + projectPath);
} }
if (xcodeProjFiles.length > 1) { if (xcodeProjFiles.length > 1) {
console.warn('Found multiple .xcodeproj directories in \n' + events.emit('warn','Found multiple .xcodeproj directories in \n' +
projectPath + '\nUsing first one'); projectPath + '\nUsing first one');
} }
...@@ -154,12 +145,12 @@ function getXcodeArgs(projectName, projectPath, configuration, isDevice) { ...@@ -154,12 +145,12 @@ function getXcodeArgs(projectName, projectPath, configuration, isDevice) {
xcodebuildArgs = [ xcodebuildArgs = [
'-xcconfig', path.join(__dirname, '..', 'build-' + configuration.toLowerCase() + '.xcconfig'), '-xcconfig', path.join(__dirname, '..', 'build-' + configuration.toLowerCase() + '.xcconfig'),
'-project', projectName + '.xcodeproj', '-project', projectName + '.xcodeproj',
'ARCHS=armv7 armv7s arm64', 'ARCHS=armv7 arm64',
'-target', projectName, '-target', projectName,
'-configuration', configuration, '-configuration', configuration,
'-sdk', 'iphoneos', '-sdk', 'iphoneos',
'build', 'build',
'VALID_ARCHS=armv7 armv7s arm64', 'VALID_ARCHS=armv7 arm64',
'CONFIGURATION_BUILD_DIR=' + path.join(projectPath, 'build', 'device'), 'CONFIGURATION_BUILD_DIR=' + path.join(projectPath, 'build', 'device'),
'SHARED_PRECOMPS_DIR=' + path.join(projectPath, 'build', 'sharedpch') 'SHARED_PRECOMPS_DIR=' + path.join(projectPath, 'build', 'sharedpch')
]; ];
...@@ -198,6 +189,7 @@ module.exports.help = function help() { ...@@ -198,6 +189,7 @@ module.exports.help = function help() {
console.log(' --codeSignIdentity : Type of signing identity used for code signing.'); console.log(' --codeSignIdentity : Type of signing identity used for code signing.');
console.log(' --codeSignResourceRules : Path to ResourceRules.plist.'); console.log(' --codeSignResourceRules : Path to ResourceRules.plist.');
console.log(' --provisioningProfile : UUID of the profile.'); console.log(' --provisioningProfile : UUID of the profile.');
console.log(' --device --noSign : Builds project without application signing.');
console.log(''); console.log('');
console.log('examples:'); console.log('examples:');
console.log(' build '); console.log(' build ');
......
...@@ -21,16 +21,11 @@ var Q = require('q'), ...@@ -21,16 +21,11 @@ var Q = require('q'),
shell = require('shelljs'), shell = require('shelljs'),
versions = require('./versions'); versions = require('./versions');
var XCODEBUILD_MIN_VERSION = '4.6.0'; var XCODEBUILD_MIN_VERSION = '6.0.0';
var XCODEBUILD_NOT_FOUND_MESSAGE = var XCODEBUILD_NOT_FOUND_MESSAGE =
'Please install version ' + XCODEBUILD_MIN_VERSION + ' or greater from App Store'; 'Please install version ' + XCODEBUILD_MIN_VERSION + ' or greater from App Store';
var IOS_SIM_MIN_VERSION = '3.0.0'; var IOS_DEPLOY_MIN_VERSION = '1.8.0';
var IOS_SIM_NOT_FOUND_MESSAGE =
'Please download, build and install version ' + IOS_SIM_MIN_VERSION + ' or greater' +
' from https://github.com/phonegap/ios-sim into your path, or do \'npm install -g ios-sim\'';
var IOS_DEPLOY_MIN_VERSION = '1.4.0';
var IOS_DEPLOY_NOT_FOUND_MESSAGE = var IOS_DEPLOY_NOT_FOUND_MESSAGE =
'Please download, build and install version ' + IOS_DEPLOY_MIN_VERSION + ' or greater' + 'Please download, build and install version ' + IOS_DEPLOY_MIN_VERSION + ' or greater' +
' from https://github.com/phonegap/ios-deploy into your path, or do \'npm install -g ios-deploy\''; ' from https://github.com/phonegap/ios-deploy into your path, or do \'npm install -g ios-deploy\'';
...@@ -51,14 +46,6 @@ module.exports.check_ios_deploy = function () { ...@@ -51,14 +46,6 @@ module.exports.check_ios_deploy = function () {
return checkTool('ios-deploy', IOS_DEPLOY_MIN_VERSION, IOS_DEPLOY_NOT_FOUND_MESSAGE); return checkTool('ios-deploy', IOS_DEPLOY_MIN_VERSION, IOS_DEPLOY_NOT_FOUND_MESSAGE);
}; };
/**
* Checks if ios-sim util is available
* @return {Promise} Returns a promise either resolved with ios-sim version or rejected
*/
module.exports.check_ios_sim = function () {
return checkTool('ios-sim', IOS_SIM_MIN_VERSION, IOS_SIM_NOT_FOUND_MESSAGE);
};
module.exports.check_os = function () { module.exports.check_os = function () {
// Build iOS apps available for OSX platform only, so we reject on others platforms // Build iOS apps available for OSX platform only, so we reject on others platforms
return process.platform === 'darwin' ? return process.platform === 'darwin' ?
...@@ -66,13 +53,9 @@ module.exports.check_os = function () { ...@@ -66,13 +53,9 @@ module.exports.check_os = function () {
Q.reject('Cordova tooling for iOS requires Apple OS X'); Q.reject('Cordova tooling for iOS requires Apple OS X');
}; };
module.exports.help = function () {
console.log('Usage: check_reqs or node check_reqs');
};
/** /**
* Checks if specific tool is available. * Checks if specific tool is available.
* @param {String} tool Tool name to check. Known tools are 'xcodebuild', 'ios-sim' and 'ios-deploy' * @param {String} tool Tool name to check. Known tools are 'xcodebuild' and 'ios-deploy'
* @param {Number} minVersion Min allowed tool version. * @param {Number} minVersion Min allowed tool version.
* @param {String} message Message that will be used to reject promise. * @param {String} message Message that will be used to reject promise.
* @return {Promise} Returns a promise either resolved with tool version or rejected * @return {Promise} Returns a promise either resolved with tool version or rejected
...@@ -119,8 +102,7 @@ module.exports.check_all = function() { ...@@ -119,8 +102,7 @@ module.exports.check_all = function() {
var requirements = [ var requirements = [
new Requirement('os', 'Apple OS X', true), new Requirement('os', 'Apple OS X', true),
new Requirement('xcode', 'Xcode'), new Requirement('xcode', 'Xcode'),
new Requirement('ios-deploy', 'ios-deploy'), new Requirement('ios-deploy', 'ios-deploy')
new Requirement('ios-sim', 'ios-sim')
]; ];
var result = []; var result = [];
...@@ -129,8 +111,7 @@ module.exports.check_all = function() { ...@@ -129,8 +111,7 @@ module.exports.check_all = function() {
var checkFns = [ var checkFns = [
module.exports.check_os, module.exports.check_os,
module.exports.check_xcodebuild, module.exports.check_xcodebuild,
module.exports.check_ios_deploy, module.exports.check_ios_deploy
module.exports.check_ios_sim
]; ];
// Then execute requirement checks one-by-one // Then execute requirement checks one-by-one
......
...@@ -22,8 +22,7 @@ ...@@ -22,8 +22,7 @@
var Q = require('q'), var Q = require('q'),
path = require('path'), path = require('path'),
shell = require('shelljs'), shell = require('shelljs'),
spawn = require('./spawn'), spawn = require('./spawn');
check_reqs = require('./check_reqs');
var projectPath = path.join(__dirname, '..', '..'); var projectPath = path.join(__dirname, '..', '..');
...@@ -36,9 +35,8 @@ module.exports.run = function() { ...@@ -36,9 +35,8 @@ module.exports.run = function() {
return Q.reject('No Xcode project found in ' + projectPath); return Q.reject('No Xcode project found in ' + projectPath);
} }
return check_reqs.run().then(function() { return spawn('xcodebuild', ['-project', projectName, '-configuration', 'Debug', '-alltargets', 'clean'], projectPath)
return spawn('xcodebuild', ['-project', projectName, '-configuration', 'Debug', '-alltargets', 'clean'], projectPath); .then(function () {
}).then(function () {
return spawn('xcodebuild', ['-project', projectName, '-configuration', 'Release', '-alltargets', 'clean'], projectPath); return spawn('xcodebuild', ['-project', projectName, '-configuration', 'Release', '-alltargets', 'clean'], projectPath);
}).then(function () { }).then(function () {
return shell.rm('-rf', path.join(projectPath, 'build')); return shell.rm('-rf', path.join(projectPath, 'build'));
......
...@@ -32,7 +32,6 @@ var BUILT_PRODUCTS_DIR = process.env.BUILT_PRODUCTS_DIR, ...@@ -32,7 +32,6 @@ var BUILT_PRODUCTS_DIR = process.env.BUILT_PRODUCTS_DIR,
var path = require('path'), var path = require('path'),
fs = require('fs'), fs = require('fs'),
shell = require('shelljs'), shell = require('shelljs'),
glob = require('glob'),
srcDir = 'www', srcDir = 'www',
dstDir = path.join(BUILT_PRODUCTS_DIR, FULL_PRODUCT_NAME), dstDir = path.join(BUILT_PRODUCTS_DIR, FULL_PRODUCT_NAME),
dstWwwDir = path.join(dstDir, 'www'); dstWwwDir = path.join(dstDir, 'www');
...@@ -46,7 +45,7 @@ try { ...@@ -46,7 +45,7 @@ try {
fs.statSync(srcDir); fs.statSync(srcDir);
} catch (e) { } catch (e) {
console.error('Path does not exist: ' + srcDir); console.error('Path does not exist: ' + srcDir);
process.exit(1); process.exit(2);
} }
// Code signing files must be removed or else there are // Code signing files must be removed or else there are
...@@ -57,11 +56,16 @@ shell.rm('-rf', path.join(dstDir, 'PkgInfo')); ...@@ -57,11 +56,16 @@ shell.rm('-rf', path.join(dstDir, 'PkgInfo'));
shell.rm('-rf', path.join(dstDir, 'embedded.mobileprovision')); shell.rm('-rf', path.join(dstDir, 'embedded.mobileprovision'));
// Copy www dir recursively // Copy www dir recursively
var code;
if(!!COPY_HIDDEN) { if(!!COPY_HIDDEN) {
shell.mkdir('-p', dstWwwDir); code = shell.exec('rsync -Lra "' + srcDir + '" "' + dstDir + '"').code;
shell.cp('-r', glob.sync(srcDir + '/**', { dot: true }), dstWwwDir);
} else { } else {
shell.cp('-r', srcDir, dstDir); code = shell.exec('rsync -Lra --exclude="- .*" "' + srcDir + '" "' + dstDir + '"').code;
}
if(code !== 0) {
console.error('Error occured on copying www. Code: ' + code);
process.exit(3);
} }
// Copy the config.xml file. // Copy the config.xml file.
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
/*jshint node: true*/ /*jshint node: true*/
var Q = require('q'), var Q = require('q'),
iossim = require('ios-sim'),
exec = require('child_process').exec, exec = require('child_process').exec,
check_reqs = require('./check_reqs'); check_reqs = require('./check_reqs');
...@@ -30,15 +31,10 @@ var Q = require('q'), ...@@ -30,15 +31,10 @@ var Q = require('q'),
* @return {Promise} Promise fulfilled with list of devices available for simulation * @return {Promise} Promise fulfilled with list of devices available for simulation
*/ */
function listEmulatorImages () { function listEmulatorImages () {
return check_reqs.check_ios_sim().then(function () { return Q.resolve(iossim.getdevicetypes());
return Q.nfcall(exec, 'ios-sim showdevicetypes 2>&1 | ' +
'sed "s/com.apple.CoreSimulator.SimDeviceType.//g"');
}).then(function (stdio) {
// Exec promise resolves with array [stout, stderr], and we need stdout only
return stdio[0].trim().split('\n');
});
} }
exports.run = listEmulatorImages; exports.run = listEmulatorImages;
// Check if module is started as separate script. // Check if module is started as separate script.
......
...@@ -20,29 +20,21 @@ ...@@ -20,29 +20,21 @@
/*jshint node: true*/ /*jshint node: true*/
var Q = require('q'), var Q = require('q'),
nopt = require('nopt'), path = require('path'),
path = require('path'), iossim = require('ios-sim'),
build = require('./build'), build = require('./build'),
spawn = require('./spawn'), spawn = require('./spawn'),
check_reqs = require('./check_reqs'); check_reqs = require('./check_reqs');
var events = require('cordova-common').events;
var cordovaPath = path.join(__dirname, '..'); var cordovaPath = path.join(__dirname, '..');
var projectPath = path.join(__dirname, '..', '..'); var projectPath = path.join(__dirname, '..', '..');
module.exports.run = function (argv) { module.exports.run = function (runOptions) {
// parse args here
// --debug and --release args not parsed here
// but still valid since they can be passed down to build command
var args = nopt({
// "archs": String, // TODO: add support for building different archs
'list': Boolean,
'nobuild': Boolean,
'device': Boolean, 'emulator': Boolean, 'target': String
}, {}, argv);
// Validate args // Validate args
if (args.device && args.emulator) { if (runOptions.device && runOptions.emulator) {
return Q.reject('Only one of "device"/"emulator" options should be specified'); return Q.reject('Only one of "device"/"emulator" options should be specified');
} }
...@@ -50,54 +42,85 @@ module.exports.run = function (argv) { ...@@ -50,54 +42,85 @@ module.exports.run = function (argv) {
// Valid values for "--target" (case sensitive): // Valid values for "--target" (case sensitive):
var validTargets = ['iPhone-4s', 'iPhone-5', 'iPhone-5s', 'iPhone-6-Plus', 'iPhone-6', var validTargets = ['iPhone-4s', 'iPhone-5', 'iPhone-5s', 'iPhone-6-Plus', 'iPhone-6',
'iPad-2', 'iPad-Retina', 'iPad-Air', 'Resizable-iPhone', 'Resizable-iPad']; 'iPad-2', 'iPad-Retina', 'iPad-Air', 'Resizable-iPhone', 'Resizable-iPad'];
if (!(args.device) && args.target && validTargets.indexOf(args.target.split(',')[0]) < 0 ) { if (!(runOptions.device) && runOptions.target && validTargets.indexOf(runOptions.target.split(',')[0]) < 0 ) {
return Q.reject(args.target + ' is not a valid target for emulator'); return Q.reject(runOptions.target + ' is not a valid target for emulator');
} }
// support for CB-8168 `cordova/run --list` // support for CB-8168 `cordova/run --list`
if (args.list) { if (runOptions.list) {
if (args.device) return listDevices(); if (runOptions.device) return listDevices();
if (args.emulator) return listEmulators(); if (runOptions.emulator) return listEmulators();
// if no --device or --emulator flag is specified, list both devices and emulators // if no --device or --emulator flag is specified, list both devices and emulators
return listDevices().then(function () { return listDevices().then(function () {
return listEmulators(); return listEmulators();
}); });
} }
// check for either ios-sim or ios-deploy is available var useDevice = !!runOptions.device;
// depending on arguments provided
var checkTools = args.device ? check_reqs.check_ios_deploy() : check_reqs.check_ios_sim();
return checkTools.then(function () { return require('./list-devices').run()
// if --nobuild isn't specified then build app first .then(function (devices) {
if (!args.nobuild) { if (devices.length > 0 && !(runOptions.emulator)) {
return build.run(argv); useDevice = true;
// we also explicitly set device flag in options as we pass
// those parameters to other api (build as an example)
runOptions.device = true;
return check_reqs.check_ios_deploy();
}
}).then(function () {
if (!runOptions.nobuild) {
return build.run(runOptions);
} else {
return Q.resolve();
} }
}).then(function () { }).then(function () {
return build.findXCodeProjectIn(projectPath); return build.findXCodeProjectIn(projectPath);
}).then(function (projectName) { }).then(function (projectName) {
var appPath = path.join(projectPath, 'build', (args.device ? 'device' : 'emulator'), projectName + '.app'); var appPath = path.join(projectPath, 'build', 'emulator', projectName + '.app');
// select command to run and arguments depending whether // select command to run and arguments depending whether
// we're running on device/emulator // we're running on device/emulator
if (args.device) { if (useDevice) {
return checkDeviceConnected().then(function () { return checkDeviceConnected().then(function () {
return deployToDevice(appPath); appPath = path.join(projectPath, 'build', 'device', projectName + '.app');
// argv.slice(2) removes node and run.js, filterSupportedArgs removes the run.js args
return deployToDevice(appPath, runOptions.target, filterSupportedArgs(runOptions.argv.slice(2)));
}, function () { }, function () {
// if device connection check failed use emulator then // if device connection check failed use emulator then
return deployToSim(appPath, args.target); return deployToSim(appPath, runOptions.target);
}); });
} else { } else {
return deployToSim(appPath, args.target); return deployToSim(appPath, runOptions.target);
} }
}); });
}; };
/** /**
* Filters the args array and removes supported args for the 'run' command.
*
* @return {Array} array with unsupported args for the 'run' command
*/
function filterSupportedArgs(args) {
var filtered = [];
var sargs = ['--device', '--emulator', '--nobuild', '--list', '--target', '--debug', '--release'];
var re = new RegExp(sargs.join('|'));
args.forEach(function(element) {
// supported args not found, we add
// we do a regex search because --target can be "--target=XXX"
if (element.search(re) == -1) {
filtered.push(element);
}
}, this);
return filtered;
}
/**
* Checks if any iOS device is connected * Checks if any iOS device is connected
* @return {Promise} Fullfilled when any device is connected, rejected otherwise * @return {Promise} Fullfilled when any device is connected, rejected otherwise
*/ */
function checkDeviceConnected() { function checkDeviceConnected() {
return spawn('ios-deploy', ['-c']); return spawn('ios-deploy', ['-c', '-t', '1']);
} }
/** /**
...@@ -106,9 +129,13 @@ function checkDeviceConnected() { ...@@ -106,9 +129,13 @@ function checkDeviceConnected() {
* @param {String} appPath Path to application package * @param {String} appPath Path to application package
* @return {Promise} Resolves when deploy succeeds otherwise rejects * @return {Promise} Resolves when deploy succeeds otherwise rejects
*/ */
function deployToDevice(appPath) { function deployToDevice(appPath, target, extraArgs) {
// Deploying to device... // Deploying to device...
return spawn('ios-deploy', ['-d', '-b', appPath]); if (target) {
return spawn('ios-deploy', ['--justlaunch', '-d', '-b', appPath, '-i', target].concat(extraArgs));
} else {
return spawn('ios-deploy', ['--justlaunch', '-d', '-b', appPath].concat(extraArgs));
}
} }
/** /**
...@@ -120,25 +147,36 @@ function deployToDevice(appPath) { ...@@ -120,25 +147,36 @@ function deployToDevice(appPath) {
function deployToSim(appPath, target) { function deployToSim(appPath, target) {
// Select target device for emulator. Default is 'iPhone-6' // Select target device for emulator. Default is 'iPhone-6'
if (!target) { if (!target) {
target = 'iPhone-6'; return require('./list-emulator-images').run()
console.log('No target specified for emulator. Deploying to ' + target + ' simulator'); .then(function (emulators) {
if (emulators.length > 0) {
target = emulators[0];
}
emulators.forEach(function (emulator) {
if (emulator.indexOf('iPhone') === 0) {
target = emulator;
}
});
events.emit('log','No target specified for emulator. Deploying to ' + target + ' simulator');
return startSim(appPath, target);
});
} else {
return startSim(appPath, target);
} }
}
function startSim(appPath, target) {
var logPath = path.join(cordovaPath, 'console.log'); var logPath = path.join(cordovaPath, 'console.log');
var simArgs = ['launch', appPath,
'--devicetypeid', 'com.apple.CoreSimulator.SimDeviceType.' + target, return iossim.launch(appPath, 'com.apple.CoreSimulator.SimDeviceType.' + target, logPath, '--exit');
// We need to redirect simulator output here to use cordova/log command
// TODO: Is there any other way to get emulator's output to use in log command?
'--stderr', logPath, '--stdout', logPath,
'--exit'];
return spawn('ios-sim', simArgs);
} }
function listDevices() { function listDevices() {
return require('./list-devices').run() return require('./list-devices').run()
.then(function (devices) { .then(function (devices) {
console.log('Available iOS Devices:'); events.emit('log','Available iOS Devices:');
devices.forEach(function (device) { devices.forEach(function (device) {
console.log('\t' + device); events.emit('log','\t' + device);
}); });
}); });
} }
...@@ -146,9 +184,9 @@ function listDevices() { ...@@ -146,9 +184,9 @@ function listDevices() {
function listEmulators() { function listEmulators() {
return require('./list-emulator-images').run() return require('./list-emulator-images').run()
.then(function (emulators) { .then(function (emulators) {
console.log('Available iOS Virtual Devices:'); events.emit('log','Available iOS Virtual Devices:');
emulators.forEach(function (emulator) { emulators.forEach(function (emulator) {
console.log('\t' + emulator); events.emit('log','\t' + emulator);
}); });
}); });
} }
......
...@@ -43,7 +43,6 @@ module.exports = function(cmd, args, opt_cwd) { ...@@ -43,7 +43,6 @@ module.exports = function(cmd, args, opt_cwd) {
} }
}); });
} catch(e) { } catch(e) {
console.error('error caught: ' + e);
d.reject(e); d.reject(e);
} }
return d.promise; return d.promise;
......
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
[![Build Status](https://travis-ci.org/isaacs/node-glob.svg?branch=master)](https://travis-ci.org/isaacs/node-glob/) [![Dependency Status](https://david-dm.org/isaacs/node-glob.svg)](https://david-dm.org/isaacs/node-glob) [![devDependency Status](https://david-dm.org/isaacs/node-glob/dev-status.svg)](https://david-dm.org/isaacs/node-glob#info=devDependencies) [![optionalDependency Status](https://david-dm.org/isaacs/node-glob/optional-status.svg)](https://david-dm.org/isaacs/node-glob#info=optionalDependencies)
# Glob
Match files using the patterns the shell uses, like stars and stuff.
This is a glob implementation in JavaScript. It uses the `minimatch`
library to do its matching.
![](oh-my-glob.gif)
## Usage
```javascript
var glob = require("glob")
// options is optional
glob("**/*.js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
})
```
## Glob Primer
"Globs" are the patterns you type when you do stuff like `ls *.js` on
the command line, or put `build/*` in a `.gitignore` file.
Before parsing the path part patterns, braced sections are expanded
into a set. Braced sections start with `{` and end with `}`, with any
number of comma-delimited sections within. Braced sections may contain
slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`.
The following characters have special magic meaning when used in a
path portion:
* `*` Matches 0 or more characters in a single path portion
* `?` Matches 1 character
* `[...]` Matches a range of characters, similar to a RegExp range.
If the first character of the range is `!` or `^` then it matches
any character not in the range.
* `!(pattern|pattern|pattern)` Matches anything that does not match
any of the patterns provided.
* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the
patterns provided.
* `+(pattern|pattern|pattern)` Matches one or more occurrences of the
patterns provided.
* `*(a|b|c)` Matches zero or more occurrences of the patterns provided
* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns
provided
* `**` If a "globstar" is alone in a path portion, then it matches
zero or more directories and subdirectories searching for matches.
It does not crawl symlinked directories.
### Dots
If a file or directory path portion has a `.` as the first character,
then it will not match any glob pattern unless that pattern's
corresponding path part also has a `.` as its first character.
For example, the pattern `a/.*/c` would match the file at `a/.b/c`.
However the pattern `a/*/c` would not, because `*` does not start with
a dot character.
You can make glob treat dots as normal characters by setting
`dot:true` in the options.
### Basename Matching
If you set `matchBase:true` in the options, and the pattern has no
slashes in it, then it will seek for any file anywhere in the tree
with a matching basename. For example, `*.js` would match
`test/simple/basic.js`.
### Negation
The intent for negation would be for a pattern starting with `!` to
match everything that *doesn't* match the supplied pattern. However,
the implementation is weird, and for the time being, this should be
avoided. The behavior is deprecated in version 5, and will be removed
entirely in version 6.
### Empty Sets
If no matching files are found, then an empty array is returned. This
differs from the shell, where the pattern itself is returned. For
example:
$ echo a*s*d*f
a*s*d*f
To get the bash-style behavior, set the `nonull:true` in the options.
### See Also:
* `man sh`
* `man bash` (Search for "Pattern Matching")
* `man 3 fnmatch`
* `man 5 gitignore`
* [minimatch documentation](https://github.com/isaacs/minimatch)
## glob.hasMagic(pattern, [options])
Returns `true` if there are any special characters in the pattern, and
`false` otherwise.
Note that the options affect the results. If `noext:true` is set in
the options object, then `+(a|b)` will not be considered a magic
pattern. If the pattern has a brace expansion, like `a/{b/c,x/y}`
then that is considered magical, unless `nobrace:true` is set in the
options.
## glob(pattern, [options], cb)
* `pattern` {String} Pattern to be matched
* `options` {Object}
* `cb` {Function}
* `err` {Error | null}
* `matches` {Array<String>} filenames found matching the pattern
Perform an asynchronous glob search.
## glob.sync(pattern, [options])
* `pattern` {String} Pattern to be matched
* `options` {Object}
* return: {Array<String>} filenames found matching the pattern
Perform a synchronous glob search.
## Class: glob.Glob
Create a Glob object by instantiating the `glob.Glob` class.
```javascript
var Glob = require("glob").Glob
var mg = new Glob(pattern, options, cb)
```
It's an EventEmitter, and starts walking the filesystem to find matches
immediately.
### new glob.Glob(pattern, [options], [cb])
* `pattern` {String} pattern to search for
* `options` {Object}
* `cb` {Function} Called when an error occurs, or matches are found
* `err` {Error | null}
* `matches` {Array<String>} filenames found matching the pattern
Note that if the `sync` flag is set in the options, then matches will
be immediately available on the `g.found` member.
### Properties
* `minimatch` The minimatch object that the glob uses.
* `options` The options object passed in.
* `aborted` Boolean which is set to true when calling `abort()`. There
is no way at this time to continue a glob search after aborting, but
you can re-use the statCache to avoid having to duplicate syscalls.
* `cache` Convenience object. Each field has the following possible
values:
* `false` - Path does not exist
* `true` - Path exists
* `'DIR'` - Path exists, and is not a directory
* `'FILE'` - Path exists, and is a directory
* `[file, entries, ...]` - Path exists, is a directory, and the
array value is the results of `fs.readdir`
* `statCache` Cache of `fs.stat` results, to prevent statting the same
path multiple times.
* `symlinks` A record of which paths are symbolic links, which is
relevant in resolving `**` patterns.
* `realpathCache` An optional object which is passed to `fs.realpath`
to minimize unnecessary syscalls. It is stored on the instantiated
Glob object, and may be re-used.
### Events
* `end` When the matching is finished, this is emitted with all the
matches found. If the `nonull` option is set, and no match was found,
then the `matches` list contains the original pattern. The matches
are sorted, unless the `nosort` flag is set.
* `match` Every time a match is found, this is emitted with the matched.
* `error` Emitted when an unexpected error is encountered, or whenever
any fs error occurs if `options.strict` is set.
* `abort` When `abort()` is called, this event is raised.
### Methods
* `pause` Temporarily stop the search
* `resume` Resume the search
* `abort` Stop the search forever
### Options
All the options that can be passed to Minimatch can also be passed to
Glob to change pattern matching behavior. Also, some have been added,
or have glob-specific ramifications.
All options are false by default, unless otherwise noted.
All options are added to the Glob object, as well.
If you are running many `glob` operations, you can pass a Glob object
as the `options` argument to a subsequent operation to shortcut some
`stat` and `readdir` calls. At the very least, you may pass in shared
`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that
parallel glob operations will be sped up by sharing information about
the filesystem.
* `cwd` The current working directory in which to search. Defaults
to `process.cwd()`.
* `root` The place where patterns starting with `/` will be mounted
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
systems, and `C:\` or some such on Windows.)
* `dot` Include `.dot` files in normal matches and `globstar` matches.
Note that an explicit dot in a portion of the pattern will always
match dot files.
* `nomount` By default, a pattern starting with a forward-slash will be
"mounted" onto the root setting, so that a valid filesystem path is
returned. Set this flag to disable that behavior.
* `mark` Add a `/` character to directory matches. Note that this
requires additional stat calls.
* `nosort` Don't sort the results.
* `stat` Set to true to stat *all* results. This reduces performance
somewhat, and is completely unnecessary, unless `readdir` is presumed
to be an untrustworthy indicator of file existence.
* `silent` When an unusual error is encountered when attempting to
read a directory, a warning will be printed to stderr. Set the
`silent` option to true to suppress these warnings.
* `strict` When an unusual error is encountered when attempting to
read a directory, the process will just continue on in search of
other matches. Set the `strict` option to raise an error in these
cases.
* `cache` See `cache` property above. Pass in a previously generated
cache object to save some fs calls.
* `statCache` A cache of results of filesystem information, to prevent
unnecessary stat calls. While it should not normally be necessary
to set this, you may pass the statCache from one glob() call to the
options object of another, if you know that the filesystem will not
change between calls. (See "Race Conditions" below.)
* `symlinks` A cache of known symbolic links. You may pass in a
previously generated `symlinks` object to save `lstat` calls when
resolving `**` matches.
* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead.
* `nounique` In some cases, brace-expanded patterns can result in the
same file showing up multiple times in the result set. By default,
this implementation prevents duplicates in the result set. Set this
flag to disable that behavior.
* `nonull` Set to never return an empty set, instead returning a set
containing the pattern itself. This is the default in glob(3).
* `debug` Set to enable debug logging in minimatch and glob.
* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets.
* `noglobstar` Do not match `**` against multiple filenames. (Ie,
treat it as a normal `*` instead.)
* `noext` Do not match `+(a|b)` "extglob" patterns.
* `nocase` Perform a case-insensitive match. Note: on
case-insensitive filesystems, non-magic patterns will match by
default, since `stat` and `readdir` will not raise errors.
* `matchBase` Perform a basename-only match if the pattern does not
contain any slash characters. That is, `*.js` would be treated as
equivalent to `**/*.js`, matching all js files in all directories.
* `nonull` Return the pattern when no matches are found.
* `nodir` Do not match directories, only files. (Note: to match
*only* directories, simply put a `/` at the end of the pattern.)
* `ignore` Add a pattern or an array of patterns to exclude matches.
* `follow` Follow symlinked directories when expanding `**` patterns.
Note that this can result in a lot of duplicate references in the
presence of cyclic links.
* `realpath` Set to true to call `fs.realpath` on all of the results.
In the case of a symlink that cannot be resolved, the full absolute
path to the matched entry is returned (though it will usually be a
broken symlink)
* `nonegate` Suppress deprecated `negate` behavior. (See below.)
Default=true
* `nocomment` Suppress deprecated `comment` behavior. (See below.)
Default=true
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between node-glob and other
implementations, and are intentional.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.3, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
Note that symlinked directories are not crawled as part of a `**`,
though their contents may match against subsequent portions of the
pattern. This prevents infinite loops and duplicates and the like.
If an escaped pattern has no matches, and the `nonull` flag is set,
then glob returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
### Comments and Negation
**Note**: In version 5 of this module, negation and comments are
**disabled** by default. You can explicitly set `nonegate:false` or
`nocomment:false` to re-enable them. They are going away entirely in
version 6.
The intent for negation would be for a pattern starting with `!` to
match everything that *doesn't* match the supplied pattern. However,
the implementation is weird. It is better to use the `ignore` option
to set a pattern or set of patterns to exclude from matches. If you
want the "everything except *x*" type of behavior, you can use `**` as
the main pattern, and set an `ignore` for the things to exclude.
The comments feature is added in minimatch, primarily to more easily
support use cases like ignore files, where a `#` at the start of a
line makes the pattern "empty". However, in the context of a
straightforward filesystem globber, "comments" don't make much sense.
## Windows
**Please only use forward-slashes in glob expressions.**
Though windows uses either `/` or `\` as its path separator, only `/`
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes will always
be interpreted as escape characters, not path separators.
Results from absolute patterns such as `/foo/*` are mounted onto the
root setting using `path.join`. On windows, this will by default result
in `/foo/*` matching `C:\foo\bar.txt`.
## Race Conditions
Glob searching, by its very nature, is susceptible to race conditions,
since it relies on directory walking and such.
As a result, it is possible that a file that exists when glob looks for
it may have been deleted or modified by the time it returns the result.
As part of its internal implementation, this program caches all stat
and readdir calls that it makes, in order to cut down on system
overhead. However, this also makes it even more susceptible to races,
especially if the cache or statCache objects are reused between glob
calls.
Users are thus advised not to use a glob result as a guarantee of
filesystem state in the face of rapid changes. For the vast majority
of operations, this is never a problem.
## Contributing
Any change to behavior (including bugfixes) must come with a test.
Patches that fail tests or reduce performance will be rejected.
```
# to run tests
npm test
# to re-generate test fixtures
npm run test-regen
# to benchmark against bash/zsh
npm run bench
# to profile javascript
npm run prof
```
exports.alphasort = alphasort
exports.alphasorti = alphasorti
exports.setopts = setopts
exports.ownProp = ownProp
exports.makeAbs = makeAbs
exports.finish = finish
exports.mark = mark
exports.isIgnored = isIgnored
exports.childrenIgnored = childrenIgnored
function ownProp (obj, field) {
return Object.prototype.hasOwnProperty.call(obj, field)
}
var path = require("path")
var minimatch = require("minimatch")
var isAbsolute = require("path-is-absolute")
var Minimatch = minimatch.Minimatch
function alphasorti (a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase())
}
function alphasort (a, b) {
return a.localeCompare(b)
}
function setupIgnores (self, options) {
self.ignore = options.ignore || []
if (!Array.isArray(self.ignore))
self.ignore = [self.ignore]
if (self.ignore.length) {
self.ignore = self.ignore.map(ignoreMap)
}
}
function ignoreMap (pattern) {
var gmatcher = null
if (pattern.slice(-3) === '/**') {
var gpattern = pattern.replace(/(\/\*\*)+$/, '')
gmatcher = new Minimatch(gpattern)
}
return {
matcher: new Minimatch(pattern),
gmatcher: gmatcher
}
}
function setopts (self, pattern, options) {
if (!options)
options = {}
// base-matching: just use globstar for that.
if (options.matchBase && -1 === pattern.indexOf("/")) {
if (options.noglobstar) {
throw new Error("base matching requires globstar")
}
pattern = "**/" + pattern
}
self.silent = !!options.silent
self.pattern = pattern
self.strict = options.strict !== false
self.realpath = !!options.realpath
self.realpathCache = options.realpathCache || Object.create(null)
self.follow = !!options.follow
self.dot = !!options.dot
self.mark = !!options.mark
self.nodir = !!options.nodir
if (self.nodir)
self.mark = true
self.sync = !!options.sync
self.nounique = !!options.nounique
self.nonull = !!options.nonull
self.nosort = !!options.nosort
self.nocase = !!options.nocase
self.stat = !!options.stat
self.noprocess = !!options.noprocess
self.maxLength = options.maxLength || Infinity
self.cache = options.cache || Object.create(null)
self.statCache = options.statCache || Object.create(null)
self.symlinks = options.symlinks || Object.create(null)
setupIgnores(self, options)
self.changedCwd = false
var cwd = process.cwd()
if (!ownProp(options, "cwd"))
self.cwd = cwd
else {
self.cwd = options.cwd
self.changedCwd = path.resolve(options.cwd) !== cwd
}
self.root = options.root || path.resolve(self.cwd, "/")
self.root = path.resolve(self.root)
if (process.platform === "win32")
self.root = self.root.replace(/\\/g, "/")
self.nomount = !!options.nomount
// disable comments and negation unless the user explicitly
// passes in false as the option.
options.nonegate = options.nonegate === false ? false : true
options.nocomment = options.nocomment === false ? false : true
deprecationWarning(options)
self.minimatch = new Minimatch(pattern, options)
self.options = self.minimatch.options
}
// TODO(isaacs): remove entirely in v6
// exported to reset in tests
exports.deprecationWarned
function deprecationWarning(options) {
if (!options.nonegate || !options.nocomment) {
if (process.noDeprecation !== true && !exports.deprecationWarned) {
var msg = 'glob WARNING: comments and negation will be disabled in v6'
if (process.throwDeprecation)
throw new Error(msg)
else if (process.traceDeprecation)
console.trace(msg)
else
console.error(msg)
exports.deprecationWarned = true
}
}
}
function finish (self) {
var nou = self.nounique
var all = nou ? [] : Object.create(null)
for (var i = 0, l = self.matches.length; i < l; i ++) {
var matches = self.matches[i]
if (!matches || Object.keys(matches).length === 0) {
if (self.nonull) {
// do like the shell, and spit out the literal glob
var literal = self.minimatch.globSet[i]
if (nou)
all.push(literal)
else
all[literal] = true
}
} else {
// had matches
var m = Object.keys(matches)
if (nou)
all.push.apply(all, m)
else
m.forEach(function (m) {
all[m] = true
})
}
}
if (!nou)
all = Object.keys(all)
if (!self.nosort)
all = all.sort(self.nocase ? alphasorti : alphasort)
// at *some* point we statted all of these
if (self.mark) {
for (var i = 0; i < all.length; i++) {
all[i] = self._mark(all[i])
}
if (self.nodir) {
all = all.filter(function (e) {
return !(/\/$/.test(e))
})
}
}
if (self.ignore.length)
all = all.filter(function(m) {
return !isIgnored(self, m)
})
self.found = all
}
function mark (self, p) {
var abs = makeAbs(self, p)
var c = self.cache[abs]
var m = p
if (c) {
var isDir = c === 'DIR' || Array.isArray(c)
var slash = p.slice(-1) === '/'
if (isDir && !slash)
m += '/'
else if (!isDir && slash)
m = m.slice(0, -1)
if (m !== p) {
var mabs = makeAbs(self, m)
self.statCache[mabs] = self.statCache[abs]
self.cache[mabs] = self.cache[abs]
}
}
return m
}
// lotta situps...
function makeAbs (self, f) {
var abs = f
if (f.charAt(0) === '/') {
abs = path.join(self.root, f)
} else if (isAbsolute(f) || f === '') {
abs = f
} else if (self.changedCwd) {
abs = path.resolve(self.cwd, f)
} else {
abs = path.resolve(f)
}
return abs
}
// Return true, if pattern ends with globstar '**', for the accompanying parent directory.
// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents
function isIgnored (self, path) {
if (!self.ignore.length)
return false
return self.ignore.some(function(item) {
return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path))
})
}
function childrenIgnored (self, path) {
if (!self.ignore.length)
return false
return self.ignore.some(function(item) {
return !!(item.gmatcher && item.gmatcher.match(path))
})
}
// Approach:
//
// 1. Get the minimatch set
// 2. For each pattern in the set, PROCESS(pattern, false)
// 3. Store matches per-set, then uniq them
//
// PROCESS(pattern, inGlobStar)
// Get the first [n] items from pattern that are all strings
// Join these together. This is PREFIX.
// If there is no more remaining, then stat(PREFIX) and
// add to matches if it succeeds. END.
//
// If inGlobStar and PREFIX is symlink and points to dir
// set ENTRIES = []
// else readdir(PREFIX) as ENTRIES
// If fail, END
//
// with ENTRIES
// If pattern[n] is GLOBSTAR
// // handle the case where the globstar match is empty
// // by pruning it out, and testing the resulting pattern
// PROCESS(pattern[0..n] + pattern[n+1 .. $], false)
// // handle other cases.
// for ENTRY in ENTRIES (not dotfiles)
// // attach globstar + tail onto the entry
// // Mark that this entry is a globstar match
// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true)
//
// else // not globstar
// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot)
// Test ENTRY against pattern[n]
// If fails, continue
// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $])
//
// Caveat:
// Cache all stats and readdirs results to minimize syscall. Since all
// we ever care about is existence and directory-ness, we can just keep
// `true` for files, and [children,...] for directories, or `false` for
// things that don't exist.
module.exports = glob
var fs = require('fs')
var minimatch = require('minimatch')
var Minimatch = minimatch.Minimatch
var inherits = require('inherits')
var EE = require('events').EventEmitter
var path = require('path')
var assert = require('assert')
var isAbsolute = require('path-is-absolute')
var globSync = require('./sync.js')
var common = require('./common.js')
var alphasort = common.alphasort
var alphasorti = common.alphasorti
var setopts = common.setopts
var ownProp = common.ownProp
var inflight = require('inflight')
var util = require('util')
var childrenIgnored = common.childrenIgnored
var once = require('once')
function glob (pattern, options, cb) {
if (typeof options === 'function') cb = options, options = {}
if (!options) options = {}
if (options.sync) {
if (cb)
throw new TypeError('callback provided to sync glob')
return globSync(pattern, options)
}
return new Glob(pattern, options, cb)
}
glob.sync = globSync
var GlobSync = glob.GlobSync = globSync.GlobSync
// old api surface
glob.glob = glob
glob.hasMagic = function (pattern, options_) {
var options = util._extend({}, options_)
options.noprocess = true
var g = new Glob(pattern, options)
var set = g.minimatch.set
if (set.length > 1)
return true
for (var j = 0; j < set[0].length; j++) {
if (typeof set[0][j] !== 'string')
return true
}
return false
}
glob.Glob = Glob
inherits(Glob, EE)
function Glob (pattern, options, cb) {
if (typeof options === 'function') {
cb = options
options = null
}
if (options && options.sync) {
if (cb)
throw new TypeError('callback provided to sync glob')
return new GlobSync(pattern, options)
}
if (!(this instanceof Glob))
return new Glob(pattern, options, cb)
setopts(this, pattern, options)
this._didRealPath = false
// process each pattern in the minimatch set
var n = this.minimatch.set.length
// The matches are stored as {<filename>: true,...} so that
// duplicates are automagically pruned.
// Later, we do an Object.keys() on these.
// Keep them as a list so we can fill in when nonull is set.
this.matches = new Array(n)
if (typeof cb === 'function') {
cb = once(cb)
this.on('error', cb)
this.on('end', function (matches) {
cb(null, matches)
})
}
var self = this
var n = this.minimatch.set.length
this._processing = 0
this.matches = new Array(n)
this._emitQueue = []
this._processQueue = []
this.paused = false
if (this.noprocess)
return this
if (n === 0)
return done()
for (var i = 0; i < n; i ++) {
this._process(this.minimatch.set[i], i, false, done)
}
function done () {
--self._processing
if (self._processing <= 0)
self._finish()
}
}
Glob.prototype._finish = function () {
assert(this instanceof Glob)
if (this.aborted)
return
if (this.realpath && !this._didRealpath)
return this._realpath()
common.finish(this)
this.emit('end', this.found)
}
Glob.prototype._realpath = function () {
if (this._didRealpath)
return
this._didRealpath = true
var n = this.matches.length
if (n === 0)
return this._finish()
var self = this
for (var i = 0; i < this.matches.length; i++)
this._realpathSet(i, next)
function next () {
if (--n === 0)
self._finish()
}
}
Glob.prototype._realpathSet = function (index, cb) {
var matchset = this.matches[index]
if (!matchset)
return cb()
var found = Object.keys(matchset)
var self = this
var n = found.length
if (n === 0)
return cb()
var set = this.matches[index] = Object.create(null)
found.forEach(function (p, i) {
// If there's a problem with the stat, then it means that
// one or more of the links in the realpath couldn't be
// resolved. just return the abs value in that case.
p = self._makeAbs(p)
fs.realpath(p, self.realpathCache, function (er, real) {
if (!er)
set[real] = true
else if (er.syscall === 'stat')
set[p] = true
else
self.emit('error', er) // srsly wtf right here
if (--n === 0) {
self.matches[index] = set
cb()
}
})
})
}
Glob.prototype._mark = function (p) {
return common.mark(this, p)
}
Glob.prototype._makeAbs = function (f) {
return common.makeAbs(this, f)
}
Glob.prototype.abort = function () {
this.aborted = true
this.emit('abort')
}
Glob.prototype.pause = function () {
if (!this.paused) {
this.paused = true
this.emit('pause')
}
}
Glob.prototype.resume = function () {
if (this.paused) {
this.emit('resume')
this.paused = false
if (this._emitQueue.length) {
var eq = this._emitQueue.slice(0)
this._emitQueue.length = 0
for (var i = 0; i < eq.length; i ++) {
var e = eq[i]
this._emitMatch(e[0], e[1])
}
}
if (this._processQueue.length) {
var pq = this._processQueue.slice(0)
this._processQueue.length = 0
for (var i = 0; i < pq.length; i ++) {
var p = pq[i]
this._processing--
this._process(p[0], p[1], p[2], p[3])
}
}
}
}
Glob.prototype._process = function (pattern, index, inGlobStar, cb) {
assert(this instanceof Glob)
assert(typeof cb === 'function')
if (this.aborted)
return
this._processing++
if (this.paused) {
this._processQueue.push([pattern, index, inGlobStar, cb])
return
}
//console.error('PROCESS %d', this._processing, pattern)
// Get the first [n] parts of pattern that are all strings.
var n = 0
while (typeof pattern[n] === 'string') {
n ++
}
// now n is the index of the first one that is *not* a string.
// see if there's anything else
var prefix
switch (n) {
// if not, then this is rather simple
case pattern.length:
this._processSimple(pattern.join('/'), index, cb)
return
case 0:
// pattern *starts* with some non-trivial item.
// going to readdir(cwd), but not include the prefix in matches.
prefix = null
break
default:
// pattern has some string bits in the front.
// whatever it starts with, whether that's 'absolute' like /foo/bar,
// or 'relative' like '../baz'
prefix = pattern.slice(0, n).join('/')
break
}
var remain = pattern.slice(n)
// get the list of entries.
var read
if (prefix === null)
read = '.'
else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
if (!prefix || !isAbsolute(prefix))
prefix = '/' + prefix
read = prefix
} else
read = prefix
var abs = this._makeAbs(read)
//if ignored, skip _processing
if (childrenIgnored(this, read))
return cb()
var isGlobStar = remain[0] === minimatch.GLOBSTAR
if (isGlobStar)
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb)
else
this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb)
}
Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) {
var self = this
this._readdir(abs, inGlobStar, function (er, entries) {
return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
})
}
Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
// if the abs isn't a dir, then nothing can match!
if (!entries)
return cb()
// It will only match dot entries if it starts with a dot, or if
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
var pn = remain[0]
var negate = !!this.minimatch.negate
var rawGlob = pn._glob
var dotOk = this.dot || rawGlob.charAt(0) === '.'
var matchedEntries = []
for (var i = 0; i < entries.length; i++) {
var e = entries[i]
if (e.charAt(0) !== '.' || dotOk) {
var m
if (negate && !prefix) {
m = !e.match(pn)
} else {
m = e.match(pn)
}
if (m)
matchedEntries.push(e)
}
}
//console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries)
var len = matchedEntries.length
// If there are no matched entries, then nothing matches.
if (len === 0)
return cb()
// if this is the last remaining pattern bit, then no need for
// an additional stat *unless* the user has specified mark or
// stat explicitly. We know they exist, since readdir returned
// them.
if (remain.length === 1 && !this.mark && !this.stat) {
if (!this.matches[index])
this.matches[index] = Object.create(null)
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
if (prefix) {
if (prefix !== '/')
e = prefix + '/' + e
else
e = prefix + e
}
if (e.charAt(0) === '/' && !this.nomount) {
e = path.join(this.root, e)
}
this._emitMatch(index, e)
}
// This was the last one, and no stats were needed
return cb()
}
// now test all matched entries as stand-ins for that part
// of the pattern.
remain.shift()
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
var newPattern
if (prefix) {
if (prefix !== '/')
e = prefix + '/' + e
else
e = prefix + e
}
this._process([e].concat(remain), index, inGlobStar, cb)
}
cb()
}
Glob.prototype._emitMatch = function (index, e) {
if (this.aborted)
return
if (this.matches[index][e])
return
if (this.paused) {
this._emitQueue.push([index, e])
return
}
var abs = this._makeAbs(e)
if (this.nodir) {
var c = this.cache[abs]
if (c === 'DIR' || Array.isArray(c))
return
}
if (this.mark)
e = this._mark(e)
this.matches[index][e] = true
var st = this.statCache[abs]
if (st)
this.emit('stat', e, st)
this.emit('match', e)
}
Glob.prototype._readdirInGlobStar = function (abs, cb) {
if (this.aborted)
return
// follow all symlinked directories forever
// just proceed as if this is a non-globstar situation
if (this.follow)
return this._readdir(abs, false, cb)
var lstatkey = 'lstat\0' + abs
var self = this
var lstatcb = inflight(lstatkey, lstatcb_)
if (lstatcb)
fs.lstat(abs, lstatcb)
function lstatcb_ (er, lstat) {
if (er)
return cb()
var isSym = lstat.isSymbolicLink()
self.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
if (!isSym && !lstat.isDirectory()) {
self.cache[abs] = 'FILE'
cb()
} else
self._readdir(abs, false, cb)
}
}
Glob.prototype._readdir = function (abs, inGlobStar, cb) {
if (this.aborted)
return
cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb)
if (!cb)
return
//console.error('RD %j %j', +inGlobStar, abs)
if (inGlobStar && !ownProp(this.symlinks, abs))
return this._readdirInGlobStar(abs, cb)
if (ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (!c || c === 'FILE')
return cb()
if (Array.isArray(c))
return cb(null, c)
}
var self = this
fs.readdir(abs, readdirCb(this, abs, cb))
}
function readdirCb (self, abs, cb) {
return function (er, entries) {
if (er)
self._readdirError(abs, er, cb)
else
self._readdirEntries(abs, entries, cb)
}
}
Glob.prototype._readdirEntries = function (abs, entries, cb) {
if (this.aborted)
return
// if we haven't asked to stat everything, then just
// assume that everything in there exists, so we can avoid
// having to stat it a second time.
if (!this.mark && !this.stat) {
for (var i = 0; i < entries.length; i ++) {
var e = entries[i]
if (abs === '/')
e = abs + e
else
e = abs + '/' + e
this.cache[e] = true
}
}
this.cache[abs] = entries
return cb(null, entries)
}
Glob.prototype._readdirError = function (f, er, cb) {
if (this.aborted)
return
// handle errors, and cache the information
switch (er.code) {
case 'ENOTDIR': // totally normal. means it *does* exist.
this.cache[this._makeAbs(f)] = 'FILE'
break
case 'ENOENT': // not terribly unusual
case 'ELOOP':
case 'ENAMETOOLONG':
case 'UNKNOWN':
this.cache[this._makeAbs(f)] = false
break
default: // some unusual error. Treat as failure.
this.cache[this._makeAbs(f)] = false
if (this.strict) {
this.emit('error', er)
// If the error is handled, then we abort
// if not, we threw out of here
this.abort()
}
if (!this.silent)
console.error('glob error', er)
break
}
return cb()
}
Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) {
var self = this
this._readdir(abs, inGlobStar, function (er, entries) {
self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb)
})
}
Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) {
//console.error('pgs2', prefix, remain[0], entries)
// no entries means not a dir, so it can never have matches
// foo.txt/** doesn't match foo.txt
if (!entries)
return cb()
// test without the globstar, and with every child both below
// and replacing the globstar.
var remainWithoutGlobStar = remain.slice(1)
var gspref = prefix ? [ prefix ] : []
var noGlobStar = gspref.concat(remainWithoutGlobStar)
// the noGlobStar pattern exits the inGlobStar state
this._process(noGlobStar, index, false, cb)
var isSym = this.symlinks[abs]
var len = entries.length
// If it's a symlink, and we're in a globstar, then stop
if (isSym && inGlobStar)
return cb()
for (var i = 0; i < len; i++) {
var e = entries[i]
if (e.charAt(0) === '.' && !this.dot)
continue
// these two cases enter the inGlobStar state
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
this._process(instead, index, true, cb)
var below = gspref.concat(entries[i], remain)
this._process(below, index, true, cb)
}
cb()
}
Glob.prototype._processSimple = function (prefix, index, cb) {
// XXX review this. Shouldn't it be doing the mounting etc
// before doing stat? kinda weird?
var self = this
this._stat(prefix, function (er, exists) {
self._processSimple2(prefix, index, er, exists, cb)
})
}
Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) {
//console.error('ps2', prefix, exists)
if (!this.matches[index])
this.matches[index] = Object.create(null)
// If it doesn't exist, then just mark the lack of results
if (!exists)
return cb()
if (prefix && isAbsolute(prefix) && !this.nomount) {
var trail = /[\/\\]$/.test(prefix)
if (prefix.charAt(0) === '/') {
prefix = path.join(this.root, prefix)
} else {
prefix = path.resolve(this.root, prefix)
if (trail)
prefix += '/'
}
}
if (process.platform === 'win32')
prefix = prefix.replace(/\\/g, '/')
// Mark this as a match
this._emitMatch(index, prefix)
cb()
}
// Returns either 'DIR', 'FILE', or false
Glob.prototype._stat = function (f, cb) {
var abs = this._makeAbs(f)
var needDir = f.slice(-1) === '/'
if (f.length > this.maxLength)
return cb()
if (!this.stat && ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (Array.isArray(c))
c = 'DIR'
// It exists, but maybe not how we need it
if (!needDir || c === 'DIR')
return cb(null, c)
if (needDir && c === 'FILE')
return cb()
// otherwise we have to stat, because maybe c=true
// if we know it exists, but not what it is.
}
var exists
var stat = this.statCache[abs]
if (stat !== undefined) {
if (stat === false)
return cb(null, stat)
else {
var type = stat.isDirectory() ? 'DIR' : 'FILE'
if (needDir && type === 'FILE')
return cb()
else
return cb(null, type, stat)
}
}
var self = this
var statcb = inflight('stat\0' + abs, lstatcb_)
if (statcb)
fs.lstat(abs, statcb)
function lstatcb_ (er, lstat) {
if (lstat && lstat.isSymbolicLink()) {
// If it's a symlink, then treat it as the target, unless
// the target does not exist, then treat it as a file.
return fs.stat(abs, function (er, stat) {
if (er)
self._stat2(f, abs, null, lstat, cb)
else
self._stat2(f, abs, er, stat, cb)
})
} else {
self._stat2(f, abs, er, lstat, cb)
}
}
}
Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
if (er) {
this.statCache[abs] = false
return cb()
}
var needDir = f.slice(-1) === '/'
this.statCache[abs] = stat
if (abs.slice(-1) === '/' && !stat.isDirectory())
return cb(null, false, stat)
var c = stat.isDirectory() ? 'DIR' : 'FILE'
this.cache[abs] = this.cache[abs] || c
if (needDir && c !== 'DIR')
return cb()
return cb(null, c, stat)
}
{
"env" : {
"node" : true
},
"rules" : {
"semi": [2, "never"],
"strict": 0,
"quotes": [1, "single", "avoid-escape"],
"no-use-before-define": 0,
"curly": 0,
"no-underscore-dangle": 0,
"no-lonely-if": 1,
"no-unused-vars": [2, {"vars" : "all", "args" : "after-used"}],
"no-mixed-requires": 0,
"space-infix-ops": 0
}
}
The ISC License
Copyright (c) Isaac Z. Schlueter
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# inflight
Add callbacks to requests in flight to avoid async duplication
## USAGE
```javascript
var inflight = require('inflight')
// some request that does some stuff
function req(key, callback) {
// key is any random string. like a url or filename or whatever.
//
// will return either a falsey value, indicating that the
// request for this key is already in flight, or a new callback
// which when called will call all callbacks passed to inflightk
// with the same key
callback = inflight(key, callback)
// If we got a falsey value back, then there's already a req going
if (!callback) return
// this is where you'd fetch the url or whatever
// callback is also once()-ified, so it can safely be assigned
// to multiple events etc. First call wins.
setTimeout(function() {
callback(null, key)
}, 100)
}
// only assigns a single setTimeout
// when it dings, all cbs get called
req('foo', cb1)
req('foo', cb2)
req('foo', cb3)
req('foo', cb4)
```
var wrappy = require('wrappy')
var reqs = Object.create(null)
var once = require('once')
module.exports = wrappy(inflight)
function inflight (key, cb) {
if (reqs[key]) {
reqs[key].push(cb)
return null
} else {
reqs[key] = [cb]
return makeres(key)
}
}
function makeres (key) {
return once(function RES () {
var cbs = reqs[key]
var len = cbs.length
var args = slice(arguments)
for (var i = 0; i < len; i++) {
cbs[i].apply(null, args)
}
if (cbs.length > len) {
// added more in the interim.
// de-zalgo, just in case, but don't call again.
cbs.splice(0, len)
process.nextTick(function () {
RES.apply(null, args)
})
} else {
delete reqs[key]
}
})
}
function slice (args) {
var length = args.length
var array = []
for (var i = 0; i < length; i++) array[i] = args[i]
return array
}
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# wrappy
Callback wrapping utility
## USAGE
```javascript
var wrappy = require("wrappy")
// var wrapper = wrappy(wrapperFunction)
// make sure a cb is called only once
// See also: http://npm.im/once for this specific use case
var once = wrappy(function (cb) {
var called = false
return function () {
if (called) return
called = true
return cb.apply(this, arguments)
}
})
function printBoo () {
console.log('boo')
}
// has some rando property
printBoo.iAmBooPrinter = true
var onlyPrintOnce = once(printBoo)
onlyPrintOnce() // prints 'boo'
onlyPrintOnce() // does nothing
// random property is retained!
assert.equal(onlyPrintOnce.iAmBooPrinter, true)
```
{
"name": "wrappy",
"version": "1.0.1",
"description": "Callback wrapping utility",
"main": "wrappy.js",
"directories": {
"test": "test"
},
"dependencies": {},
"devDependencies": {
"tap": "^0.4.12"
},
"scripts": {
"test": "tap test/*.js"
},
"repository": {
"type": "git",
"url": "https://github.com/npm/wrappy"
},
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/wrappy/issues"
},
"homepage": "https://github.com/npm/wrappy",
"readme": "# wrappy\n\nCallback wrapping utility\n\n## USAGE\n\n```javascript\nvar wrappy = require(\"wrappy\")\n\n// var wrapper = wrappy(wrapperFunction)\n\n// make sure a cb is called only once\n// See also: http://npm.im/once for this specific use case\nvar once = wrappy(function (cb) {\n var called = false\n return function () {\n if (called) return\n called = true\n return cb.apply(this, arguments)\n }\n})\n\nfunction printBoo () {\n console.log('boo')\n}\n// has some rando property\nprintBoo.iAmBooPrinter = true\n\nvar onlyPrintOnce = once(printBoo)\n\nonlyPrintOnce() // prints 'boo'\nonlyPrintOnce() // does nothing\n\n// random property is retained!\nassert.equal(onlyPrintOnce.iAmBooPrinter, true)\n```\n",
"readmeFilename": "README.md",
"_id": "wrappy@1.0.1",
"_shasum": "1e65969965ccbc2db4548c6b84a6f2c5aedd4739",
"_resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz",
"_from": "wrappy@>=1.0.0 <2.0.0"
}
var test = require('tap').test
var wrappy = require('../wrappy.js')
test('basic', function (t) {
function onceifier (cb) {
var called = false
return function () {
if (called) return
called = true
return cb.apply(this, arguments)
}
}
onceifier.iAmOnce = {}
var once = wrappy(onceifier)
t.equal(once.iAmOnce, onceifier.iAmOnce)
var called = 0
function boo () {
t.equal(called, 0)
called++
}
// has some rando property
boo.iAmBoo = true
var onlyPrintOnce = once(boo)
onlyPrintOnce() // prints 'boo'
onlyPrintOnce() // does nothing
t.equal(called, 1)
// random property is retained!
t.equal(onlyPrintOnce.iAmBoo, true)
var logs = []
var logwrap = wrappy(function (msg, cb) {
logs.push(msg + ' wrapping cb')
return function () {
logs.push(msg + ' before cb')
var ret = cb.apply(this, arguments)
logs.push(msg + ' after cb')
}
})
var c = logwrap('foo', function () {
t.same(logs, [ 'foo wrapping cb', 'foo before cb' ])
})
c()
t.same(logs, [ 'foo wrapping cb', 'foo before cb', 'foo after cb' ])
t.end()
})
// Returns a wrapper function that returns a wrapped callback
// The wrapper function should do some stuff, and return a
// presumably different callback function.
// This makes sure that own properties are retained, so that
// decorations and such are not lost along the way.
module.exports = wrappy
function wrappy (fn, cb) {
if (fn && cb) return wrappy(fn)(cb)
if (typeof fn !== 'function')
throw new TypeError('need wrapper function')
Object.keys(fn).forEach(function (k) {
wrapper[k] = fn[k]
})
return wrapper
function wrapper() {
var args = new Array(arguments.length)
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i]
}
var ret = fn.apply(this, args)
var cb = args[args.length-1]
if (typeof ret === 'function' && ret !== cb) {
Object.keys(cb).forEach(function (k) {
ret[k] = cb[k]
})
}
return ret
}
}
{
"name": "inflight",
"version": "1.0.4",
"description": "Add callbacks to requests in flight to avoid async duplication",
"main": "inflight.js",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
},
"devDependencies": {
"tap": "^0.4.10"
},
"scripts": {
"test": "tap test.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/inflight"
},
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"bugs": {
"url": "https://github.com/isaacs/inflight/issues"
},
"homepage": "https://github.com/isaacs/inflight",
"license": "ISC",
"gitHead": "c7b5531d572a867064d4a1da9e013e8910b7d1ba",
"_id": "inflight@1.0.4",
"_shasum": "6cbb4521ebd51ce0ec0a936bfd7657ef7e9b172a",
"_from": "inflight@>=1.0.4 <2.0.0",
"_npmVersion": "2.1.3",
"_nodeVersion": "0.10.32",
"_npmUser": {
"name": "othiym23",
"email": "ogd@aoaioxxysz.net"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
},
{
"name": "othiym23",
"email": "ogd@aoaioxxysz.net"
},
{
"name": "iarna",
"email": "me@re-becca.org"
}
],
"dist": {
"shasum": "6cbb4521ebd51ce0ec0a936bfd7657ef7e9b172a",
"tarball": "http://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz",
"readme": "ERROR: No README data found!"
}
var test = require('tap').test
var inf = require('./inflight.js')
function req (key, cb) {
cb = inf(key, cb)
if (cb) setTimeout(function () {
cb(key)
cb(key)
})
return cb
}
test('basic', function (t) {
var calleda = false
var a = req('key', function (k) {
t.notOk(calleda)
calleda = true
t.equal(k, 'key')
if (calledb) t.end()
})
t.ok(a, 'first returned cb function')
var calledb = false
var b = req('key', function (k) {
t.notOk(calledb)
calledb = true
t.equal(k, 'key')
if (calleda) t.end()
})
t.notOk(b, 'second should get falsey inflight response')
})
test('timing', function (t) {
var expect = [
'method one',
'start one',
'end one',
'two',
'tick',
'three'
]
var i = 0
function log (m) {
t.equal(m, expect[i], m + ' === ' + expect[i])
++i
if (i === expect.length)
t.end()
}
function method (name, cb) {
log('method ' + name)
process.nextTick(cb)
}
var one = inf('foo', function () {
log('start one')
var three = inf('foo', function () {
log('three')
})
if (three) method('three', three)
log('end one')
})
method('one', one)
var two = inf('foo', function () {
log('two')
})
if (two) method('one', two)
process.nextTick(log.bind(null, 'tick'))
})
test('parameters', function (t) {
t.plan(8)
var a = inf('key', function (first, second, third) {
t.equal(first, 1)
t.equal(second, 2)
t.equal(third, 3)
})
t.ok(a, 'first returned cb function')
var b = inf('key', function (first, second, third) {
t.equal(first, 1)
t.equal(second, 2)
t.equal(third, 3)
})
t.notOk(b, 'second should get falsey inflight response')
setTimeout(function () {
a(1, 2, 3)
})
})
The ISC License
Copyright (c) Isaac Z. Schlueter
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
Browser-friendly inheritance fully compatible with standard node.js
[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).
This package exports standard `inherits` from node.js `util` module in
node environment, but also provides alternative browser-friendly
implementation through [browser
field](https://gist.github.com/shtylman/4339901). Alternative
implementation is a literal copy of standard one located in standalone
module to avoid requiring of `util`. It also has a shim for old
browsers with no `Object.create` support.
While keeping you sure you are using standard `inherits`
implementation in node.js environment, it allows bundlers such as
[browserify](https://github.com/substack/node-browserify) to not
include full `util` package to your client code if all you need is
just `inherits` function. It worth, because browser shim for `util`
package is large and `inherits` is often the single function you need
from it.
It's recommended to use this package instead of
`require('util').inherits` for any code that has chances to be used
not only in node.js but in browser too.
## usage
```js
var inherits = require('inherits');
// then use exactly as the standard one
```
## note on version ~1.0
Version ~1.0 had completely different motivation and is not compatible
neither with 2.0 nor with standard node.js `inherits`.
If you are using version ~1.0 and planning to switch to ~2.0, be
careful:
* new version uses `super_` instead of `super` for referencing
superclass
* new version overwrites current prototype while old one preserves any
existing fields on it
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
{
"name": "inherits",
"description": "Browser-friendly inheritance fully compatible with standard node.js inherits()",
"version": "2.0.1",
"keywords": [
"inheritance",
"class",
"klass",
"oop",
"object-oriented",
"inherits",
"browser",
"browserify"
],
"main": "./inherits.js",
"browser": "./inherits_browser.js",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/inherits"
},
"license": "ISC",
"scripts": {
"test": "node test"
},
"readme": "Browser-friendly inheritance fully compatible with standard node.js\n[inherits](http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor).\n\nThis package exports standard `inherits` from node.js `util` module in\nnode environment, but also provides alternative browser-friendly\nimplementation through [browser\nfield](https://gist.github.com/shtylman/4339901). Alternative\nimplementation is a literal copy of standard one located in standalone\nmodule to avoid requiring of `util`. It also has a shim for old\nbrowsers with no `Object.create` support.\n\nWhile keeping you sure you are using standard `inherits`\nimplementation in node.js environment, it allows bundlers such as\n[browserify](https://github.com/substack/node-browserify) to not\ninclude full `util` package to your client code if all you need is\njust `inherits` function. It worth, because browser shim for `util`\npackage is large and `inherits` is often the single function you need\nfrom it.\n\nIt's recommended to use this package instead of\n`require('util').inherits` for any code that has chances to be used\nnot only in node.js but in browser too.\n\n## usage\n\n```js\nvar inherits = require('inherits');\n// then use exactly as the standard one\n```\n\n## note on version ~1.0\n\nVersion ~1.0 had completely different motivation and is not compatible\nneither with 2.0 nor with standard node.js `inherits`.\n\nIf you are using version ~1.0 and planning to switch to ~2.0, be\ncareful:\n\n* new version uses `super_` instead of `super` for referencing\n superclass\n* new version overwrites current prototype while old one preserves any\n existing fields on it\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/isaacs/inherits/issues"
},
"homepage": "https://github.com/isaacs/inherits",
"_id": "inherits@2.0.1",
"_shasum": "b17d08d326b4423e568eff719f91b0b1cbdf69f1",
"_resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
"_from": "inherits@>=2.0.0 <3.0.0"
}
var inherits = require('./inherits.js')
var assert = require('assert')
function test(c) {
assert(c.constructor === Child)
assert(c.constructor.super_ === Parent)
assert(Object.getPrototypeOf(c) === Child.prototype)
assert(Object.getPrototypeOf(Object.getPrototypeOf(c)) === Parent.prototype)
assert(c instanceof Child)
assert(c instanceof Parent)
}
function Child() {
Parent.call(this)
test(this)
}
function Parent() {}
inherits(Child, Parent)
var c = new Child
test(c)
console.log('ok')
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# minimatch
A minimal matching utility.
[![Build Status](https://secure.travis-ci.org/isaacs/minimatch.png)](http://travis-ci.org/isaacs/minimatch)
This is the matching library used internally by npm.
It works by converting glob expressions into JavaScript `RegExp`
objects.
## Usage
```javascript
var minimatch = require("minimatch")
minimatch("bar.foo", "*.foo") // true!
minimatch("bar.foo", "*.bar") // false!
minimatch("bar.foo", "*.+(bar|foo)", { debug: true }) // true, and noisy!
```
## Features
Supports these glob features:
* Brace Expansion
* Extended glob matching
* "Globstar" `**` matching
See:
* `man sh`
* `man bash`
* `man 3 fnmatch`
* `man 5 gitignore`
## Minimatch Class
Create a minimatch object by instanting the `minimatch.Minimatch` class.
```javascript
var Minimatch = require("minimatch").Minimatch
var mm = new Minimatch(pattern, options)
```
### Properties
* `pattern` The original pattern the minimatch object represents.
* `options` The options supplied to the constructor.
* `set` A 2-dimensional array of regexp or string expressions.
Each row in the
array corresponds to a brace-expanded pattern. Each item in the row
corresponds to a single path-part. For example, the pattern
`{a,b/c}/d` would expand to a set of patterns like:
[ [ a, d ]
, [ b, c, d ] ]
If a portion of the pattern doesn't have any "magic" in it
(that is, it's something like `"foo"` rather than `fo*o?`), then it
will be left as a string rather than converted to a regular
expression.
* `regexp` Created by the `makeRe` method. A single regular expression
expressing the entire pattern. This is useful in cases where you wish
to use the pattern somewhat like `fnmatch(3)` with `FNM_PATH` enabled.
* `negate` True if the pattern is negated.
* `comment` True if the pattern is a comment.
* `empty` True if the pattern is `""`.
### Methods
* `makeRe` Generate the `regexp` member if necessary, and return it.
Will return `false` if the pattern is invalid.
* `match(fname)` Return true if the filename matches the pattern, or
false otherwise.
* `matchOne(fileArray, patternArray, partial)` Take a `/`-split
filename, and match it against a single row in the `regExpSet`. This
method is mainly for internal use, but is exposed so that it can be
used by a glob-walker that needs to avoid excessive filesystem calls.
All other methods are internal, and will be called as necessary.
## Functions
The top-level exported function has a `cache` property, which is an LRU
cache set to store 100 items. So, calling these methods repeatedly
with the same pattern and options will use the same Minimatch object,
saving the cost of parsing it multiple times.
### minimatch(path, pattern, options)
Main export. Tests a path against the pattern using the options.
```javascript
var isJS = minimatch(file, "*.js", { matchBase: true })
```
### minimatch.filter(pattern, options)
Returns a function that tests its
supplied argument, suitable for use with `Array.filter`. Example:
```javascript
var javascripts = fileList.filter(minimatch.filter("*.js", {matchBase: true}))
```
### minimatch.match(list, pattern, options)
Match against the list of
files, in the style of fnmatch or glob. If nothing is matched, and
options.nonull is set, then return a list containing the pattern itself.
```javascript
var javascripts = minimatch.match(fileList, "*.js", {matchBase: true}))
```
### minimatch.makeRe(pattern, options)
Make a regular expression object from the pattern.
## Options
All options are `false` by default.
### debug
Dump a ton of stuff to stderr.
### nobrace
Do not expand `{a,b}` and `{1..3}` brace sets.
### noglobstar
Disable `**` matching against multiple folder names.
### dot
Allow patterns to match filenames starting with a period, even if
the pattern does not explicitly have a period in that spot.
Note that by default, `a/**/b` will **not** match `a/.d/b`, unless `dot`
is set.
### noext
Disable "extglob" style patterns like `+(a|b)`.
### nocase
Perform a case-insensitive match.
### nonull
When a match is not found by `minimatch.match`, return a list containing
the pattern itself if this option is set. When not set, an empty list
is returned if there are no matches.
### matchBase
If set, then patterns without slashes will be matched
against the basename of the path if it contains slashes. For example,
`a?b` would match the path `/xyz/123/acb`, but not `/xyz/acb/123`.
### nocomment
Suppress the behavior of treating `#` at the start of a pattern as a
comment.
### nonegate
Suppress the behavior of treating a leading `!` character as negation.
### flipNegate
Returns from negate expressions the same as if they were not negated.
(Ie, true on a hit, false on a miss.)
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between minimatch and other
implementations, and are intentional.
If the pattern starts with a `!` character, then it is negated. Set the
`nonegate` flag to suppress this behavior, and treat leading `!`
characters normally. This is perhaps relevant if you wish to start the
pattern with a negative extglob pattern like `!(a|B)`. Multiple `!`
characters at the start of a pattern will negate the pattern multiple
times.
If a pattern starts with `#`, then it is treated as a comment, and
will not match anything. Use `\#` to match a literal `#` at the
start of a line, or set the `nocomment` flag to suppress this behavior.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.1, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
If an escaped pattern has no matches, and the `nonull` flag is set,
then minimatch.match returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`minimatch.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = minimatch
minimatch.Minimatch = Minimatch
var path = { sep: '/' }
try {
path = require('path')
} catch (er) {}
var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
var expand = require('brace-expansion')
// any single thing other than /
// don't need to escape / when using new RegExp()
var qmark = '[^/]'
// * => any number of characters
var star = qmark + '*?'
// ** when dots are allowed. Anything goes, except .. and .
// not (^ or / followed by one or two dots followed by $ or /),
// followed by anything, any number of times.
var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
// not a ^ or / followed by a dot,
// followed by anything, any number of times.
var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
// characters that need to be escaped in RegExp.
var reSpecials = charSet('().*{}+?[]^$\\!')
// "abc" -> { a:true, b:true, c:true }
function charSet (s) {
return s.split('').reduce(function (set, c) {
set[c] = true
return set
}, {})
}
// normalizes slashes.
var slashSplit = /\/+/
minimatch.filter = filter
function filter (pattern, options) {
options = options || {}
return function (p, i, list) {
return minimatch(p, pattern, options)
}
}
function ext (a, b) {
a = a || {}
b = b || {}
var t = {}
Object.keys(b).forEach(function (k) {
t[k] = b[k]
})
Object.keys(a).forEach(function (k) {
t[k] = a[k]
})
return t
}
minimatch.defaults = function (def) {
if (!def || !Object.keys(def).length) return minimatch
var orig = minimatch
var m = function minimatch (p, pattern, options) {
return orig.minimatch(p, pattern, ext(def, options))
}
m.Minimatch = function Minimatch (pattern, options) {
return new orig.Minimatch(pattern, ext(def, options))
}
return m
}
Minimatch.defaults = function (def) {
if (!def || !Object.keys(def).length) return Minimatch
return minimatch.defaults(def).Minimatch
}
function minimatch (p, pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('glob pattern string required')
}
if (!options) options = {}
// shortcut: comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
return false
}
// "" only matches ""
if (pattern.trim() === '') return p === ''
return new Minimatch(pattern, options).match(p)
}
function Minimatch (pattern, options) {
if (!(this instanceof Minimatch)) {
return new Minimatch(pattern, options)
}
if (typeof pattern !== 'string') {
throw new TypeError('glob pattern string required')
}
if (!options) options = {}
pattern = pattern.trim()
// windows support: need to use /, not \
if (path.sep !== '/') {
pattern = pattern.split(path.sep).join('/')
}
this.options = options
this.set = []
this.pattern = pattern
this.regexp = null
this.negate = false
this.comment = false
this.empty = false
// make the set of regexps etc.
this.make()
}
Minimatch.prototype.debug = function () {}
Minimatch.prototype.make = make
function make () {
// don't do it more than once.
if (this._made) return
var pattern = this.pattern
var options = this.options
// empty patterns and comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
this.comment = true
return
}
if (!pattern) {
this.empty = true
return
}
// step 1: figure out negation, etc.
this.parseNegate()
// step 2: expand braces
var set = this.globSet = this.braceExpand()
if (options.debug) this.debug = console.error
this.debug(this.pattern, set)
// step 3: now we have a set, so turn each one into a series of path-portion
// matching patterns.
// These will be regexps, except in the case of "**", which is
// set to the GLOBSTAR object for globstar behavior,
// and will not contain any / characters
set = this.globParts = set.map(function (s) {
return s.split(slashSplit)
})
this.debug(this.pattern, set)
// glob --> regexps
set = set.map(function (s, si, set) {
return s.map(this.parse, this)
}, this)
this.debug(this.pattern, set)
// filter out everything that didn't compile properly.
set = set.filter(function (s) {
return s.indexOf(false) === -1
})
this.debug(this.pattern, set)
this.set = set
}
Minimatch.prototype.parseNegate = parseNegate
function parseNegate () {
var pattern = this.pattern
var negate = false
var options = this.options
var negateOffset = 0
if (options.nonegate) return
for (var i = 0, l = pattern.length
; i < l && pattern.charAt(i) === '!'
; i++) {
negate = !negate
negateOffset++
}
if (negateOffset) this.pattern = pattern.substr(negateOffset)
this.negate = negate
}
// Brace expansion:
// a{b,c}d -> abd acd
// a{b,}c -> abc ac
// a{0..3}d -> a0d a1d a2d a3d
// a{b,c{d,e}f}g -> abg acdfg acefg
// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
//
// Invalid sets are not expanded.
// a{2..}b -> a{2..}b
// a{b}c -> a{b}c
minimatch.braceExpand = function (pattern, options) {
return braceExpand(pattern, options)
}
Minimatch.prototype.braceExpand = braceExpand
function braceExpand (pattern, options) {
if (!options) {
if (this instanceof Minimatch) {
options = this.options
} else {
options = {}
}
}
pattern = typeof pattern === 'undefined'
? this.pattern : pattern
if (typeof pattern === 'undefined') {
throw new Error('undefined pattern')
}
if (options.nobrace ||
!pattern.match(/\{.*\}/)) {
// shortcut. no need to expand.
return [pattern]
}
return expand(pattern)
}
// parse a component of the expanded set.
// At this point, no pattern may contain "/" in it
// so we're going to return a 2d array, where each entry is the full
// pattern, split on '/', and then turned into a regular expression.
// A regexp is made at the end which joins each array with an
// escaped /, and another full one which joins each regexp with |.
//
// Following the lead of Bash 4.1, note that "**" only has special meaning
// when it is the *only* thing in a path portion. Otherwise, any series
// of * is equivalent to a single *. Globstar behavior is enabled by
// default, and can be disabled by setting options.noglobstar.
Minimatch.prototype.parse = parse
var SUBPARSE = {}
function parse (pattern, isSub) {
var options = this.options
// shortcuts
if (!options.noglobstar && pattern === '**') return GLOBSTAR
if (pattern === '') return ''
var re = ''
var hasMagic = !!options.nocase
var escaping = false
// ? => one single character
var patternListStack = []
var plType
var stateChar
var inClass = false
var reClassStart = -1
var classStart = -1
// . and .. never match anything that doesn't start with .,
// even when options.dot is set.
var patternStart = pattern.charAt(0) === '.' ? '' // anything
// not (start or / followed by . or .. followed by / or end)
: options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
: '(?!\\.)'
var self = this
function clearStateChar () {
if (stateChar) {
// we had some state-tracking character
// that wasn't consumed by this pass.
switch (stateChar) {
case '*':
re += star
hasMagic = true
break
case '?':
re += qmark
hasMagic = true
break
default:
re += '\\' + stateChar
break
}
self.debug('clearStateChar %j %j', stateChar, re)
stateChar = false
}
}
for (var i = 0, len = pattern.length, c
; (i < len) && (c = pattern.charAt(i))
; i++) {
this.debug('%s\t%s %s %j', pattern, i, re, c)
// skip over any that are escaped.
if (escaping && reSpecials[c]) {
re += '\\' + c
escaping = false
continue
}
switch (c) {
case '/':
// completely not allowed, even escaped.
// Should already be path-split by now.
return false
case '\\':
clearStateChar()
escaping = true
continue
// the various stateChar values
// for the "extglob" stuff.
case '?':
case '*':
case '+':
case '@':
case '!':
this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
// all of those are literals inside a class, except that
// the glob [!a] means [^a] in regexp
if (inClass) {
this.debug(' in class')
if (c === '!' && i === classStart + 1) c = '^'
re += c
continue
}
// if we already have a stateChar, then it means
// that there was something like ** or +? in there.
// Handle the stateChar, then proceed with this one.
self.debug('call clearStateChar %j', stateChar)
clearStateChar()
stateChar = c
// if extglob is disabled, then +(asdf|foo) isn't a thing.
// just clear the statechar *now*, rather than even diving into
// the patternList stuff.
if (options.noext) clearStateChar()
continue
case '(':
if (inClass) {
re += '('
continue
}
if (!stateChar) {
re += '\\('
continue
}
plType = stateChar
patternListStack.push({ type: plType, start: i - 1, reStart: re.length })
// negation is (?:(?!js)[^/]*)
re += stateChar === '!' ? '(?:(?!' : '(?:'
this.debug('plType %j %j', stateChar, re)
stateChar = false
continue
case ')':
if (inClass || !patternListStack.length) {
re += '\\)'
continue
}
clearStateChar()
hasMagic = true
re += ')'
plType = patternListStack.pop().type
// negation is (?:(?!js)[^/]*)
// The others are (?:<pattern>)<type>
switch (plType) {
case '!':
re += '[^/]*?)'
break
case '?':
case '+':
case '*':
re += plType
break
case '@': break // the default anyway
}
continue
case '|':
if (inClass || !patternListStack.length || escaping) {
re += '\\|'
escaping = false
continue
}
clearStateChar()
re += '|'
continue
// these are mostly the same in regexp and glob
case '[':
// swallow any state-tracking char before the [
clearStateChar()
if (inClass) {
re += '\\' + c
continue
}
inClass = true
classStart = i
reClassStart = re.length
re += c
continue
case ']':
// a right bracket shall lose its special
// meaning and represent itself in
// a bracket expression if it occurs
// first in the list. -- POSIX.2 2.8.3.2
if (i === classStart + 1 || !inClass) {
re += '\\' + c
escaping = false
continue
}
// handle the case where we left a class open.
// "[z-a]" is valid, equivalent to "\[z-a\]"
if (inClass) {
// split where the last [ was, make sure we don't have
// an invalid re. if so, re-walk the contents of the
// would-be class to re-translate any characters that
// were passed through as-is
// TODO: It would probably be faster to determine this
// without a try/catch and a new RegExp, but it's tricky
// to do safely. For now, this is safe and works.
var cs = pattern.substring(classStart + 1, i)
try {
RegExp('[' + cs + ']')
} catch (er) {
// not a valid class!
var sp = this.parse(cs, SUBPARSE)
re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
hasMagic = hasMagic || sp[1]
inClass = false
continue
}
}
// finish up the class.
hasMagic = true
inClass = false
re += c
continue
default:
// swallow any state char that wasn't consumed
clearStateChar()
if (escaping) {
// no need
escaping = false
} else if (reSpecials[c]
&& !(c === '^' && inClass)) {
re += '\\'
}
re += c
} // switch
} // for
// handle the case where we left a class open.
// "[abc" is valid, equivalent to "\[abc"
if (inClass) {
// split where the last [ was, and escape it
// this is a huge pita. We now have to re-walk
// the contents of the would-be class to re-translate
// any characters that were passed through as-is
cs = pattern.substr(classStart + 1)
sp = this.parse(cs, SUBPARSE)
re = re.substr(0, reClassStart) + '\\[' + sp[0]
hasMagic = hasMagic || sp[1]
}
// handle the case where we had a +( thing at the *end*
// of the pattern.
// each pattern list stack adds 3 chars, and we need to go through
// and escape any | chars that were passed through as-is for the regexp.
// Go through and escape them, taking care not to double-escape any
// | chars that were already escaped.
for (var pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
var tail = re.slice(pl.reStart + 3)
// maybe some even number of \, then maybe 1 \, followed by a |
tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) {
if (!$2) {
// the | isn't already escaped, so escape it.
$2 = '\\'
}
// need to escape all those slashes *again*, without escaping the
// one that we need for escaping the | character. As it works out,
// escaping an even number of slashes can be done by simply repeating
// it exactly after itself. That's why this trick works.
//
// I am sorry that you have to see this.
return $1 + $1 + $2 + '|'
})
this.debug('tail=%j\n %s', tail, tail)
var t = pl.type === '*' ? star
: pl.type === '?' ? qmark
: '\\' + pl.type
hasMagic = true
re = re.slice(0, pl.reStart) + t + '\\(' + tail
}
// handle trailing things that only matter at the very end.
clearStateChar()
if (escaping) {
// trailing \\
re += '\\\\'
}
// only need to apply the nodot start if the re starts with
// something that could conceivably capture a dot
var addPatternStart = false
switch (re.charAt(0)) {
case '.':
case '[':
case '(': addPatternStart = true
}
// if the re is not "" at this point, then we need to make sure
// it doesn't match against an empty path part.
// Otherwise a/* will match a/, which it should not.
if (re !== '' && hasMagic) re = '(?=.)' + re
if (addPatternStart) re = patternStart + re
// parsing just a piece of a larger pattern.
if (isSub === SUBPARSE) {
return [re, hasMagic]
}
// skip the regexp for non-magical patterns
// unescape anything in it, though, so that it'll be
// an exact match against a file etc.
if (!hasMagic) {
return globUnescape(pattern)
}
var flags = options.nocase ? 'i' : ''
var regExp = new RegExp('^' + re + '$', flags)
regExp._glob = pattern
regExp._src = re
return regExp
}
minimatch.makeRe = function (pattern, options) {
return new Minimatch(pattern, options || {}).makeRe()
}
Minimatch.prototype.makeRe = makeRe
function makeRe () {
if (this.regexp || this.regexp === false) return this.regexp
// at this point, this.set is a 2d array of partial
// pattern strings, or "**".
//
// It's better to use .match(). This function shouldn't
// be used, really, but it's pretty convenient sometimes,
// when you just want to work with a regex.
var set = this.set
if (!set.length) {
this.regexp = false
return this.regexp
}
var options = this.options
var twoStar = options.noglobstar ? star
: options.dot ? twoStarDot
: twoStarNoDot
var flags = options.nocase ? 'i' : ''
var re = set.map(function (pattern) {
return pattern.map(function (p) {
return (p === GLOBSTAR) ? twoStar
: (typeof p === 'string') ? regExpEscape(p)
: p._src
}).join('\\\/')
}).join('|')
// must match entire pattern
// ending in a * or ** will make it less strict.
re = '^(?:' + re + ')$'
// can match anything, as long as it's not this.
if (this.negate) re = '^(?!' + re + ').*$'
try {
this.regexp = new RegExp(re, flags)
} catch (ex) {
this.regexp = false
}
return this.regexp
}
minimatch.match = function (list, pattern, options) {
options = options || {}
var mm = new Minimatch(pattern, options)
list = list.filter(function (f) {
return mm.match(f)
})
if (mm.options.nonull && !list.length) {
list.push(pattern)
}
return list
}
Minimatch.prototype.match = match
function match (f, partial) {
this.debug('match', f, this.pattern)
// short-circuit in the case of busted things.
// comments, etc.
if (this.comment) return false
if (this.empty) return f === ''
if (f === '/' && partial) return true
var options = this.options
// windows: need to use /, not \
if (path.sep !== '/') {
f = f.split(path.sep).join('/')
}
// treat the test path as a set of pathparts.
f = f.split(slashSplit)
this.debug(this.pattern, 'split', f)
// just ONE of the pattern sets in this.set needs to match
// in order for it to be valid. If negating, then just one
// match means that we have failed.
// Either way, return on the first hit.
var set = this.set
this.debug(this.pattern, 'set', set)
// Find the basename of the path by looking for the last non-empty segment
var filename
var i
for (i = f.length - 1; i >= 0; i--) {
filename = f[i]
if (filename) break
}
for (i = 0; i < set.length; i++) {
var pattern = set[i]
var file = f
if (options.matchBase && pattern.length === 1) {
file = [filename]
}
var hit = this.matchOne(file, pattern, partial)
if (hit) {
if (options.flipNegate) return true
return !this.negate
}
}
// didn't get any hits. this is success if it's a negative
// pattern, failure otherwise.
if (options.flipNegate) return false
return this.negate
}
// set partial to true to test if, for example,
// "/a/b" matches the start of "/*/b/*/d"
// Partial means, if you run out of file before you run
// out of pattern, then that's fine, as long as all
// the parts match.
Minimatch.prototype.matchOne = function (file, pattern, partial) {
var options = this.options
this.debug('matchOne',
{ 'this': this, file: file, pattern: pattern })
this.debug('matchOne', file.length, pattern.length)
for (var fi = 0,
pi = 0,
fl = file.length,
pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++) {
this.debug('matchOne loop')
var p = pattern[pi]
var f = file[fi]
this.debug(pattern, p, f)
// should be impossible.
// some invalid regexp stuff in the set.
if (p === false) return false
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f])
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi
var pr = pi + 1
if (pr === pl) {
this.debug('** at the end')
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' || file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.')) return false
}
return true
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr]
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee)
// found a match.
return true
} else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' || swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr)
break
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue')
fr++
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
// If there's more *pattern* left, then
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
if (fr === fl) return true
}
return false
}
// something other than **
// non-magic patterns just have to match exactly
// patterns with magic have been turned into regexps.
var hit
if (typeof p === 'string') {
if (options.nocase) {
hit = f.toLowerCase() === p.toLowerCase()
} else {
hit = f === p
}
this.debug('string match', p, f, hit)
} else {
hit = f.match(p)
this.debug('pattern match', p, f, hit)
}
if (!hit) return false
}
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time.
// an exact hit!
return true
} else if (fi === fl) {
// ran out of file, but still had pattern left.
// this is ok if we're doing the match as part of
// a glob fs traversal.
return partial
} else if (pi === pl) {
// ran out of pattern, still have file left.
// this is only acceptable if we're on the very last
// empty segment of a file with a trailing slash.
// a/* should match a/b/
var emptyFileEnd = (fi === fl - 1) && (file[fi] === '')
return emptyFileEnd
}
// should be unreachable.
throw new Error('wtf?')
}
// replace stuff like \* with *
function globUnescape (s) {
return s.replace(/\\(.)/g, '$1')
}
function regExpEscape (s) {
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
}
},{"brace-expansion":2,"path":undefined}],2:[function(require,module,exports){
var concatMap = require('concat-map');
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
var expansions = expand(escapeBraces(str));
return expansions.filter(identity).map(unescapeBraces);
}
function identity(e) {
return e;
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m || /\$$/.test(m.pre)) return [str];
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = /^(.*,)+(.+)?$/.test(m.body);
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0]).map(embrace);
if (n.length === 1) {
var post = m.post.length
? expand(m.post)
: [''];
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post)
: [''];
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = concatMap(n, function(el) { return expand(el) });
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
expansions.push([pre, N[j], post[k]].join(''))
}
}
return expansions;
}
},{"balanced-match":3,"concat-map":4}],3:[function(require,module,exports){
module.exports = balanced;
function balanced(a, b, str) {
var bal = 0;
var m = {};
var ended = false;
for (var i = 0; i < str.length; i++) {
if (a == str.substr(i, a.length)) {
if (!('start' in m)) m.start = i;
bal++;
}
else if (b == str.substr(i, b.length) && 'start' in m) {
ended = true;
bal--;
if (!bal) {
m.end = i;
m.pre = str.substr(0, m.start);
m.body = (m.end - m.start > 1)
? str.substring(m.start + a.length, m.end)
: '';
m.post = str.slice(m.end + b.length);
return m;
}
}
}
// if we opened more than we closed, find the one we closed
if (bal && ended) {
var start = m.start + a.length;
m = balanced(a, b, str.substr(start));
if (m) {
m.start += start;
m.end += start;
m.pre = str.slice(0, start) + m.pre;
}
return m;
}
}
},{}],4:[function(require,module,exports){
module.exports = function (xs, fn) {
var res = [];
for (var i = 0; i < xs.length; i++) {
var x = fn(xs[i], i);
if (Array.isArray(x)) res.push.apply(res, x);
else res.push(x);
}
return res;
};
},{}]},{},[1]);
module.exports = minimatch
minimatch.Minimatch = Minimatch
var path = { sep: '/' }
try {
path = require('path')
} catch (er) {}
var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {}
var expand = require('brace-expansion')
// any single thing other than /
// don't need to escape / when using new RegExp()
var qmark = '[^/]'
// * => any number of characters
var star = qmark + '*?'
// ** when dots are allowed. Anything goes, except .. and .
// not (^ or / followed by one or two dots followed by $ or /),
// followed by anything, any number of times.
var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?'
// not a ^ or / followed by a dot,
// followed by anything, any number of times.
var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?'
// characters that need to be escaped in RegExp.
var reSpecials = charSet('().*{}+?[]^$\\!')
// "abc" -> { a:true, b:true, c:true }
function charSet (s) {
return s.split('').reduce(function (set, c) {
set[c] = true
return set
}, {})
}
// normalizes slashes.
var slashSplit = /\/+/
minimatch.filter = filter
function filter (pattern, options) {
options = options || {}
return function (p, i, list) {
return minimatch(p, pattern, options)
}
}
function ext (a, b) {
a = a || {}
b = b || {}
var t = {}
Object.keys(b).forEach(function (k) {
t[k] = b[k]
})
Object.keys(a).forEach(function (k) {
t[k] = a[k]
})
return t
}
minimatch.defaults = function (def) {
if (!def || !Object.keys(def).length) return minimatch
var orig = minimatch
var m = function minimatch (p, pattern, options) {
return orig.minimatch(p, pattern, ext(def, options))
}
m.Minimatch = function Minimatch (pattern, options) {
return new orig.Minimatch(pattern, ext(def, options))
}
return m
}
Minimatch.defaults = function (def) {
if (!def || !Object.keys(def).length) return Minimatch
return minimatch.defaults(def).Minimatch
}
function minimatch (p, pattern, options) {
if (typeof pattern !== 'string') {
throw new TypeError('glob pattern string required')
}
if (!options) options = {}
// shortcut: comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
return false
}
// "" only matches ""
if (pattern.trim() === '') return p === ''
return new Minimatch(pattern, options).match(p)
}
function Minimatch (pattern, options) {
if (!(this instanceof Minimatch)) {
return new Minimatch(pattern, options)
}
if (typeof pattern !== 'string') {
throw new TypeError('glob pattern string required')
}
if (!options) options = {}
pattern = pattern.trim()
// windows support: need to use /, not \
if (path.sep !== '/') {
pattern = pattern.split(path.sep).join('/')
}
this.options = options
this.set = []
this.pattern = pattern
this.regexp = null
this.negate = false
this.comment = false
this.empty = false
// make the set of regexps etc.
this.make()
}
Minimatch.prototype.debug = function () {}
Minimatch.prototype.make = make
function make () {
// don't do it more than once.
if (this._made) return
var pattern = this.pattern
var options = this.options
// empty patterns and comments match nothing.
if (!options.nocomment && pattern.charAt(0) === '#') {
this.comment = true
return
}
if (!pattern) {
this.empty = true
return
}
// step 1: figure out negation, etc.
this.parseNegate()
// step 2: expand braces
var set = this.globSet = this.braceExpand()
if (options.debug) this.debug = console.error
this.debug(this.pattern, set)
// step 3: now we have a set, so turn each one into a series of path-portion
// matching patterns.
// These will be regexps, except in the case of "**", which is
// set to the GLOBSTAR object for globstar behavior,
// and will not contain any / characters
set = this.globParts = set.map(function (s) {
return s.split(slashSplit)
})
this.debug(this.pattern, set)
// glob --> regexps
set = set.map(function (s, si, set) {
return s.map(this.parse, this)
}, this)
this.debug(this.pattern, set)
// filter out everything that didn't compile properly.
set = set.filter(function (s) {
return s.indexOf(false) === -1
})
this.debug(this.pattern, set)
this.set = set
}
Minimatch.prototype.parseNegate = parseNegate
function parseNegate () {
var pattern = this.pattern
var negate = false
var options = this.options
var negateOffset = 0
if (options.nonegate) return
for (var i = 0, l = pattern.length
; i < l && pattern.charAt(i) === '!'
; i++) {
negate = !negate
negateOffset++
}
if (negateOffset) this.pattern = pattern.substr(negateOffset)
this.negate = negate
}
// Brace expansion:
// a{b,c}d -> abd acd
// a{b,}c -> abc ac
// a{0..3}d -> a0d a1d a2d a3d
// a{b,c{d,e}f}g -> abg acdfg acefg
// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg
//
// Invalid sets are not expanded.
// a{2..}b -> a{2..}b
// a{b}c -> a{b}c
minimatch.braceExpand = function (pattern, options) {
return braceExpand(pattern, options)
}
Minimatch.prototype.braceExpand = braceExpand
function braceExpand (pattern, options) {
if (!options) {
if (this instanceof Minimatch) {
options = this.options
} else {
options = {}
}
}
pattern = typeof pattern === 'undefined'
? this.pattern : pattern
if (typeof pattern === 'undefined') {
throw new Error('undefined pattern')
}
if (options.nobrace ||
!pattern.match(/\{.*\}/)) {
// shortcut. no need to expand.
return [pattern]
}
return expand(pattern)
}
// parse a component of the expanded set.
// At this point, no pattern may contain "/" in it
// so we're going to return a 2d array, where each entry is the full
// pattern, split on '/', and then turned into a regular expression.
// A regexp is made at the end which joins each array with an
// escaped /, and another full one which joins each regexp with |.
//
// Following the lead of Bash 4.1, note that "**" only has special meaning
// when it is the *only* thing in a path portion. Otherwise, any series
// of * is equivalent to a single *. Globstar behavior is enabled by
// default, and can be disabled by setting options.noglobstar.
Minimatch.prototype.parse = parse
var SUBPARSE = {}
function parse (pattern, isSub) {
var options = this.options
// shortcuts
if (!options.noglobstar && pattern === '**') return GLOBSTAR
if (pattern === '') return ''
var re = ''
var hasMagic = !!options.nocase
var escaping = false
// ? => one single character
var patternListStack = []
var plType
var stateChar
var inClass = false
var reClassStart = -1
var classStart = -1
// . and .. never match anything that doesn't start with .,
// even when options.dot is set.
var patternStart = pattern.charAt(0) === '.' ? '' // anything
// not (start or / followed by . or .. followed by / or end)
: options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))'
: '(?!\\.)'
var self = this
function clearStateChar () {
if (stateChar) {
// we had some state-tracking character
// that wasn't consumed by this pass.
switch (stateChar) {
case '*':
re += star
hasMagic = true
break
case '?':
re += qmark
hasMagic = true
break
default:
re += '\\' + stateChar
break
}
self.debug('clearStateChar %j %j', stateChar, re)
stateChar = false
}
}
for (var i = 0, len = pattern.length, c
; (i < len) && (c = pattern.charAt(i))
; i++) {
this.debug('%s\t%s %s %j', pattern, i, re, c)
// skip over any that are escaped.
if (escaping && reSpecials[c]) {
re += '\\' + c
escaping = false
continue
}
switch (c) {
case '/':
// completely not allowed, even escaped.
// Should already be path-split by now.
return false
case '\\':
clearStateChar()
escaping = true
continue
// the various stateChar values
// for the "extglob" stuff.
case '?':
case '*':
case '+':
case '@':
case '!':
this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c)
// all of those are literals inside a class, except that
// the glob [!a] means [^a] in regexp
if (inClass) {
this.debug(' in class')
if (c === '!' && i === classStart + 1) c = '^'
re += c
continue
}
// if we already have a stateChar, then it means
// that there was something like ** or +? in there.
// Handle the stateChar, then proceed with this one.
self.debug('call clearStateChar %j', stateChar)
clearStateChar()
stateChar = c
// if extglob is disabled, then +(asdf|foo) isn't a thing.
// just clear the statechar *now*, rather than even diving into
// the patternList stuff.
if (options.noext) clearStateChar()
continue
case '(':
if (inClass) {
re += '('
continue
}
if (!stateChar) {
re += '\\('
continue
}
plType = stateChar
patternListStack.push({ type: plType, start: i - 1, reStart: re.length })
// negation is (?:(?!js)[^/]*)
re += stateChar === '!' ? '(?:(?!' : '(?:'
this.debug('plType %j %j', stateChar, re)
stateChar = false
continue
case ')':
if (inClass || !patternListStack.length) {
re += '\\)'
continue
}
clearStateChar()
hasMagic = true
re += ')'
plType = patternListStack.pop().type
// negation is (?:(?!js)[^/]*)
// The others are (?:<pattern>)<type>
switch (plType) {
case '!':
re += '[^/]*?)'
break
case '?':
case '+':
case '*':
re += plType
break
case '@': break // the default anyway
}
continue
case '|':
if (inClass || !patternListStack.length || escaping) {
re += '\\|'
escaping = false
continue
}
clearStateChar()
re += '|'
continue
// these are mostly the same in regexp and glob
case '[':
// swallow any state-tracking char before the [
clearStateChar()
if (inClass) {
re += '\\' + c
continue
}
inClass = true
classStart = i
reClassStart = re.length
re += c
continue
case ']':
// a right bracket shall lose its special
// meaning and represent itself in
// a bracket expression if it occurs
// first in the list. -- POSIX.2 2.8.3.2
if (i === classStart + 1 || !inClass) {
re += '\\' + c
escaping = false
continue
}
// handle the case where we left a class open.
// "[z-a]" is valid, equivalent to "\[z-a\]"
if (inClass) {
// split where the last [ was, make sure we don't have
// an invalid re. if so, re-walk the contents of the
// would-be class to re-translate any characters that
// were passed through as-is
// TODO: It would probably be faster to determine this
// without a try/catch and a new RegExp, but it's tricky
// to do safely. For now, this is safe and works.
var cs = pattern.substring(classStart + 1, i)
try {
RegExp('[' + cs + ']')
} catch (er) {
// not a valid class!
var sp = this.parse(cs, SUBPARSE)
re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]'
hasMagic = hasMagic || sp[1]
inClass = false
continue
}
}
// finish up the class.
hasMagic = true
inClass = false
re += c
continue
default:
// swallow any state char that wasn't consumed
clearStateChar()
if (escaping) {
// no need
escaping = false
} else if (reSpecials[c]
&& !(c === '^' && inClass)) {
re += '\\'
}
re += c
} // switch
} // for
// handle the case where we left a class open.
// "[abc" is valid, equivalent to "\[abc"
if (inClass) {
// split where the last [ was, and escape it
// this is a huge pita. We now have to re-walk
// the contents of the would-be class to re-translate
// any characters that were passed through as-is
cs = pattern.substr(classStart + 1)
sp = this.parse(cs, SUBPARSE)
re = re.substr(0, reClassStart) + '\\[' + sp[0]
hasMagic = hasMagic || sp[1]
}
// handle the case where we had a +( thing at the *end*
// of the pattern.
// each pattern list stack adds 3 chars, and we need to go through
// and escape any | chars that were passed through as-is for the regexp.
// Go through and escape them, taking care not to double-escape any
// | chars that were already escaped.
for (var pl = patternListStack.pop(); pl; pl = patternListStack.pop()) {
var tail = re.slice(pl.reStart + 3)
// maybe some even number of \, then maybe 1 \, followed by a |
tail = tail.replace(/((?:\\{2})*)(\\?)\|/g, function (_, $1, $2) {
if (!$2) {
// the | isn't already escaped, so escape it.
$2 = '\\'
}
// need to escape all those slashes *again*, without escaping the
// one that we need for escaping the | character. As it works out,
// escaping an even number of slashes can be done by simply repeating
// it exactly after itself. That's why this trick works.
//
// I am sorry that you have to see this.
return $1 + $1 + $2 + '|'
})
this.debug('tail=%j\n %s', tail, tail)
var t = pl.type === '*' ? star
: pl.type === '?' ? qmark
: '\\' + pl.type
hasMagic = true
re = re.slice(0, pl.reStart) + t + '\\(' + tail
}
// handle trailing things that only matter at the very end.
clearStateChar()
if (escaping) {
// trailing \\
re += '\\\\'
}
// only need to apply the nodot start if the re starts with
// something that could conceivably capture a dot
var addPatternStart = false
switch (re.charAt(0)) {
case '.':
case '[':
case '(': addPatternStart = true
}
// if the re is not "" at this point, then we need to make sure
// it doesn't match against an empty path part.
// Otherwise a/* will match a/, which it should not.
if (re !== '' && hasMagic) re = '(?=.)' + re
if (addPatternStart) re = patternStart + re
// parsing just a piece of a larger pattern.
if (isSub === SUBPARSE) {
return [re, hasMagic]
}
// skip the regexp for non-magical patterns
// unescape anything in it, though, so that it'll be
// an exact match against a file etc.
if (!hasMagic) {
return globUnescape(pattern)
}
var flags = options.nocase ? 'i' : ''
var regExp = new RegExp('^' + re + '$', flags)
regExp._glob = pattern
regExp._src = re
return regExp
}
minimatch.makeRe = function (pattern, options) {
return new Minimatch(pattern, options || {}).makeRe()
}
Minimatch.prototype.makeRe = makeRe
function makeRe () {
if (this.regexp || this.regexp === false) return this.regexp
// at this point, this.set is a 2d array of partial
// pattern strings, or "**".
//
// It's better to use .match(). This function shouldn't
// be used, really, but it's pretty convenient sometimes,
// when you just want to work with a regex.
var set = this.set
if (!set.length) {
this.regexp = false
return this.regexp
}
var options = this.options
var twoStar = options.noglobstar ? star
: options.dot ? twoStarDot
: twoStarNoDot
var flags = options.nocase ? 'i' : ''
var re = set.map(function (pattern) {
return pattern.map(function (p) {
return (p === GLOBSTAR) ? twoStar
: (typeof p === 'string') ? regExpEscape(p)
: p._src
}).join('\\\/')
}).join('|')
// must match entire pattern
// ending in a * or ** will make it less strict.
re = '^(?:' + re + ')$'
// can match anything, as long as it's not this.
if (this.negate) re = '^(?!' + re + ').*$'
try {
this.regexp = new RegExp(re, flags)
} catch (ex) {
this.regexp = false
}
return this.regexp
}
minimatch.match = function (list, pattern, options) {
options = options || {}
var mm = new Minimatch(pattern, options)
list = list.filter(function (f) {
return mm.match(f)
})
if (mm.options.nonull && !list.length) {
list.push(pattern)
}
return list
}
Minimatch.prototype.match = match
function match (f, partial) {
this.debug('match', f, this.pattern)
// short-circuit in the case of busted things.
// comments, etc.
if (this.comment) return false
if (this.empty) return f === ''
if (f === '/' && partial) return true
var options = this.options
// windows: need to use /, not \
if (path.sep !== '/') {
f = f.split(path.sep).join('/')
}
// treat the test path as a set of pathparts.
f = f.split(slashSplit)
this.debug(this.pattern, 'split', f)
// just ONE of the pattern sets in this.set needs to match
// in order for it to be valid. If negating, then just one
// match means that we have failed.
// Either way, return on the first hit.
var set = this.set
this.debug(this.pattern, 'set', set)
// Find the basename of the path by looking for the last non-empty segment
var filename
var i
for (i = f.length - 1; i >= 0; i--) {
filename = f[i]
if (filename) break
}
for (i = 0; i < set.length; i++) {
var pattern = set[i]
var file = f
if (options.matchBase && pattern.length === 1) {
file = [filename]
}
var hit = this.matchOne(file, pattern, partial)
if (hit) {
if (options.flipNegate) return true
return !this.negate
}
}
// didn't get any hits. this is success if it's a negative
// pattern, failure otherwise.
if (options.flipNegate) return false
return this.negate
}
// set partial to true to test if, for example,
// "/a/b" matches the start of "/*/b/*/d"
// Partial means, if you run out of file before you run
// out of pattern, then that's fine, as long as all
// the parts match.
Minimatch.prototype.matchOne = function (file, pattern, partial) {
var options = this.options
this.debug('matchOne',
{ 'this': this, file: file, pattern: pattern })
this.debug('matchOne', file.length, pattern.length)
for (var fi = 0,
pi = 0,
fl = file.length,
pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++) {
this.debug('matchOne loop')
var p = pattern[pi]
var f = file[fi]
this.debug(pattern, p, f)
// should be impossible.
// some invalid regexp stuff in the set.
if (p === false) return false
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f])
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi
var pr = pi + 1
if (pr === pl) {
this.debug('** at the end')
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' || file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.')) return false
}
return true
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr]
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee)
// found a match.
return true
} else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' || swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr)
break
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue')
fr++
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
// If there's more *pattern* left, then
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
if (fr === fl) return true
}
return false
}
// something other than **
// non-magic patterns just have to match exactly
// patterns with magic have been turned into regexps.
var hit
if (typeof p === 'string') {
if (options.nocase) {
hit = f.toLowerCase() === p.toLowerCase()
} else {
hit = f === p
}
this.debug('string match', p, f, hit)
} else {
hit = f.match(p)
this.debug('pattern match', p, f, hit)
}
if (!hit) return false
}
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time.
// an exact hit!
return true
} else if (fi === fl) {
// ran out of file, but still had pattern left.
// this is ok if we're doing the match as part of
// a glob fs traversal.
return partial
} else if (pi === pl) {
// ran out of pattern, still have file left.
// this is only acceptable if we're on the very last
// empty segment of a file with a trailing slash.
// a/* should match a/b/
var emptyFileEnd = (fi === fl - 1) && (file[fi] === '')
return emptyFileEnd
}
// should be unreachable.
throw new Error('wtf?')
}
// replace stuff like \* with *
function globUnescape (s) {
return s.replace(/\\(.)/g, '$1')
}
function regExpEscape (s) {
return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
}
# brace-expansion
[Brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html),
as known from sh/bash, in JavaScript.
[![build status](https://secure.travis-ci.org/juliangruber/brace-expansion.svg)](http://travis-ci.org/juliangruber/brace-expansion)
[![testling badge](https://ci.testling.com/juliangruber/brace-expansion.png)](https://ci.testling.com/juliangruber/brace-expansion)
## Example
```js
var expand = require('brace-expansion');
expand('file-{a,b,c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('-v{,,}')
// => ['-v', '-v', '-v']
expand('file{0..2}.jpg')
// => ['file0.jpg', 'file1.jpg', 'file2.jpg']
expand('file-{a..c}.jpg')
// => ['file-a.jpg', 'file-b.jpg', 'file-c.jpg']
expand('file{2..0}.jpg')
// => ['file2.jpg', 'file1.jpg', 'file0.jpg']
expand('file{0..4..2}.jpg')
// => ['file0.jpg', 'file2.jpg', 'file4.jpg']
expand('file-{a..e..2}.jpg')
// => ['file-a.jpg', 'file-c.jpg', 'file-e.jpg']
expand('file{00..10..5}.jpg')
// => ['file00.jpg', 'file05.jpg', 'file10.jpg']
expand('{{A..C},{a..c}}')
// => ['A', 'B', 'C', 'a', 'b', 'c']
expand('ppp{,config,oe{,conf}}')
// => ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']
```
## API
```js
var expand = require('brace-expansion');
```
### var expanded = expand(str)
Return an array of all possible and valid expansions of `str`. If none are
found, `[str]` is returned.
Valid expansions are:
```js
/^(.*,)+(.+)?$/
// {a,b,...}
```
A comma seperated list of options, like `{a,b}` or `{a,{b,c}}` or `{,a,}`.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
A numeric sequence from `x` to `y` inclusive, with optional increment.
If `x` or `y` start with a leading `0`, all the numbers will be padded
to have equal length. Negative numbers and backwards iteration work too.
```js
/^-?\d+\.\.-?\d+(\.\.-?\d+)?$/
// {x..y[..incr]}
```
An alphabetic sequence from `x` to `y` inclusive, with optional increment.
`x` and `y` must be exactly one character, and if given, `incr` must be a
number.
For compatibility reasons, the string `${` is not eligible for brace expansion.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install brace-expansion
```
## Contributors
- [Julian Gruber](https://github.com/juliangruber)
- [Isaac Z. Schlueter](https://github.com/isaacs)
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
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.
var expand = require('./');
console.log(expand('http://any.org/archive{1996..1999}/vol{1..4}/part{a,b,c}.html'));
console.log(expand('http://www.numericals.com/file{1..100..10}.txt'));
console.log(expand('http://www.letters.com/file{a..z..2}.txt'));
console.log(expand('mkdir /usr/local/src/bash/{old,new,dist,bugs}'));
console.log(expand('chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}'));
var concatMap = require('concat-map');
var balanced = require('balanced-match');
module.exports = expandTop;
var escSlash = '\0SLASH'+Math.random()+'\0';
var escOpen = '\0OPEN'+Math.random()+'\0';
var escClose = '\0CLOSE'+Math.random()+'\0';
var escComma = '\0COMMA'+Math.random()+'\0';
var escPeriod = '\0PERIOD'+Math.random()+'\0';
function numeric(str) {
return parseInt(str, 10) == str
? parseInt(str, 10)
: str.charCodeAt(0);
}
function escapeBraces(str) {
return str.split('\\\\').join(escSlash)
.split('\\{').join(escOpen)
.split('\\}').join(escClose)
.split('\\,').join(escComma)
.split('\\.').join(escPeriod);
}
function unescapeBraces(str) {
return str.split(escSlash).join('\\')
.split(escOpen).join('{')
.split(escClose).join('}')
.split(escComma).join(',')
.split(escPeriod).join('.');
}
// Basically just str.split(","), but handling cases
// where we have nested braced sections, which should be
// treated as individual members, like {a,{b,c},d}
function parseCommaParts(str) {
if (!str)
return [''];
var parts = [];
var m = balanced('{', '}', str);
if (!m)
return str.split(',');
var pre = m.pre;
var body = m.body;
var post = m.post;
var p = pre.split(',');
p[p.length-1] += '{' + body + '}';
var postParts = parseCommaParts(post);
if (post.length) {
p[p.length-1] += postParts.shift();
p.push.apply(p, postParts);
}
parts.push.apply(parts, p);
return parts;
}
function expandTop(str) {
if (!str)
return [];
return expand(escapeBraces(str), true).map(unescapeBraces);
}
function identity(e) {
return e;
}
function embrace(str) {
return '{' + str + '}';
}
function isPadded(el) {
return /^-?0\d/.test(el);
}
function lte(i, y) {
return i <= y;
}
function gte(i, y) {
return i >= y;
}
function expand(str, isTop) {
var expansions = [];
var m = balanced('{', '}', str);
if (!m || /\$$/.test(m.pre)) return [str];
var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
var isSequence = isNumericSequence || isAlphaSequence;
var isOptions = /^(.*,)+(.+)?$/.test(m.body);
if (!isSequence && !isOptions) {
// {a},b}
if (m.post.match(/,.*}/)) {
str = m.pre + '{' + m.body + escClose + m.post;
return expand(str);
}
return [str];
}
var n;
if (isSequence) {
n = m.body.split(/\.\./);
} else {
n = parseCommaParts(m.body);
if (n.length === 1) {
// x{{a,b}}y ==> x{a}y x{b}y
n = expand(n[0], false).map(embrace);
if (n.length === 1) {
var post = m.post.length
? expand(m.post, false)
: [''];
return post.map(function(p) {
return m.pre + n[0] + p;
});
}
}
}
// at this point, n is the parts, and we know it's not a comma set
// with a single entry.
// no need to expand pre, since it is guaranteed to be free of brace-sets
var pre = m.pre;
var post = m.post.length
? expand(m.post, false)
: [''];
var N;
if (isSequence) {
var x = numeric(n[0]);
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
? Math.abs(numeric(n[2]))
: 1;
var test = lte;
var reverse = y < x;
if (reverse) {
incr *= -1;
test = gte;
}
var pad = n.some(isPadded);
N = [];
for (var i = x; test(i, y); i += incr) {
var c;
if (isAlphaSequence) {
c = String.fromCharCode(i);
if (c === '\\')
c = '';
} else {
c = String(i);
if (pad) {
var need = width - c.length;
if (need > 0) {
var z = new Array(need + 1).join('0');
if (i < 0)
c = '-' + z + c.slice(1);
else
c = z + c;
}
}
}
N.push(c);
}
} else {
N = concatMap(n, function(el) { return expand(el, false) });
}
for (var j = 0; j < N.length; j++) {
for (var k = 0; k < post.length; k++) {
var expansion = pre + N[j] + post[k];
if (!isTop || isSequence || expansion)
expansions.push(expansion);
}
}
return expansions;
}
# balanced-match
Match balanced string pairs, like `{` and `}` or `<b>` and `</b>`.
[![build status](https://secure.travis-ci.org/juliangruber/balanced-match.svg)](http://travis-ci.org/juliangruber/balanced-match)
[![downloads](https://img.shields.io/npm/dm/balanced-match.svg)](https://www.npmjs.org/package/balanced-match)
[![testling badge](https://ci.testling.com/juliangruber/balanced-match.png)](https://ci.testling.com/juliangruber/balanced-match)
## Example
Get the first matching pair of braces:
```js
var balanced = require('balanced-match');
console.log(balanced('{', '}', 'pre{in{nested}}post'));
console.log(balanced('{', '}', 'pre{first}between{second}post'));
```
The matches are:
```bash
$ node example.js
{ start: 3, end: 14, pre: 'pre', body: 'in{nested}', post: 'post' }
{ start: 3,
end: 9,
pre: 'pre',
body: 'first',
post: 'between{second}post' }
```
## API
### var m = balanced(a, b, str)
For the first non-nested matching pair of `a` and `b` in `str`, return an
object with those keys:
* **start** the index of the first match of `a`
* **end** the index of the matching `b`
* **pre** the preamble, `a` and `b` not included
* **body** the match, `a` and `b` not included
* **post** the postscript, `a` and `b` not included
If there's no match, `undefined` will be returned.
If the `str` contains more `a` than `b` / there are unmatched pairs, the first match that was closed will be used. For example, `{{a}` will match `['{', 'a', '']`.
## Installation
With [npm](https://npmjs.org) do:
```bash
npm install balanced-match
```
## License
(MIT)
Copyright (c) 2013 Julian Gruber &lt;julian@juliangruber.com&gt;
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.
var balanced = require('./');
console.log(balanced('{', '}', 'pre{in{nested}}post'));
console.log(balanced('{', '}', 'pre{first}between{second}post'));
module.exports = balanced;
function balanced(a, b, str) {
var bal = 0;
var m = {};
var ended = false;
for (var i = 0; i < str.length; i++) {
if (a == str.substr(i, a.length)) {
if (!('start' in m)) m.start = i;
bal++;
}
else if (b == str.substr(i, b.length) && 'start' in m) {
ended = true;
bal--;
if (!bal) {
m.end = i;
m.pre = str.substr(0, m.start);
m.body = (m.end - m.start > 1)
? str.substring(m.start + a.length, m.end)
: '';
m.post = str.slice(m.end + b.length);
return m;
}
}
}
// if we opened more than we closed, find the one we closed
if (bal && ended) {
var start = m.start + a.length;
m = balanced(a, b, str.substr(start));
if (m) {
m.start += start;
m.end += start;
m.pre = str.slice(0, start) + m.pre;
}
return m;
}
}
{
"name": "balanced-match",
"description": "Match balanced character pairs, like \"{\" and \"}\"",
"version": "0.2.0",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/balanced-match.git"
},
"homepage": "https://github.com/juliangruber/balanced-match",
"main": "index.js",
"scripts": {
"test": "make test"
},
"dependencies": {},
"devDependencies": {
"tape": "~1.1.1"
},
"keywords": [
"match",
"regexp",
"test",
"balanced",
"parse"
],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"gitHead": "ba40ed78e7114a4a67c51da768a100184dead39c",
"bugs": {
"url": "https://github.com/juliangruber/balanced-match/issues"
},
"_id": "balanced-match@0.2.0",
"_shasum": "38f6730c03aab6d5edbb52bd934885e756d71674",
"_from": "balanced-match@>=0.2.0 <0.3.0",
"_npmVersion": "2.1.8",
"_nodeVersion": "0.10.32",
"_npmUser": {
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
"maintainers": [
{
"name": "juliangruber",
"email": "julian@juliangruber.com"
}
],
"dist": {
"shasum": "38f6730c03aab6d5edbb52bd934885e756d71674",
"tarball": "http://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz",
"readme": "ERROR: No README data found!"
}
var test = require('tape');
var balanced = require('..');
test('balanced', function(t) {
t.deepEqual(balanced('{', '}', 'pre{in{nest}}post'), {
start: 3,
end: 12,
pre: 'pre',
body: 'in{nest}',
post: 'post'
});
t.deepEqual(balanced('{', '}', '{{{{{{{{{in}post'), {
start: 8,
end: 11,
pre: '{{{{{{{{',
body: 'in',
post: 'post'
});
t.deepEqual(balanced('{', '}', 'pre{body{in}post'), {
start: 8,
end: 11,
pre: 'pre{body',
body: 'in',
post: 'post'
});
t.deepEqual(balanced('{', '}', 'pre}{in{nest}}post'), {
start: 4,
end: 13,
pre: 'pre}',
body: 'in{nest}',
post: 'post'
});
t.deepEqual(balanced('{', '}', 'pre{body}between{body2}post'), {
start: 3,
end: 8,
pre: 'pre',
body: 'body',
post: 'between{body2}post'
});
t.notOk(balanced('{', '}', 'nope'), 'should be notOk');
t.deepEqual(balanced('<b>', '</b>', 'pre<b>in<b>nest</b></b>post'), {
start: 3,
end: 19,
pre: 'pre',
body: 'in<b>nest</b>',
post: 'post'
});
t.deepEqual(balanced('<b>', '</b>', 'pre</b><b>in<b>nest</b></b>post'), {
start: 7,
end: 23,
pre: 'pre</b>',
body: 'in<b>nest</b>',
post: 'post'
});
t.end();
});
This software is released under the MIT license:
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.
concat-map
==========
Concatenative mapdashery.
[![browser support](http://ci.testling.com/substack/node-concat-map.png)](http://ci.testling.com/substack/node-concat-map)
[![build status](https://secure.travis-ci.org/substack/node-concat-map.png)](http://travis-ci.org/substack/node-concat-map)
example
=======
``` js
var concatMap = require('concat-map');
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ys = concatMap(xs, function (x) {
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
console.dir(ys);
```
***
```
[ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]
```
methods
=======
``` js
var concatMap = require('concat-map')
```
concatMap(xs, fn)
-----------------
Return an array of concatenated elements by calling `fn(x, i)` for each element
`x` and each index `i` in the array `xs`.
When `fn(x, i)` returns an array, its result will be concatenated with the
result array. If `fn(x, i)` returns anything else, that value will be pushed
onto the end of the result array.
install
=======
With [npm](http://npmjs.org) do:
```
npm install concat-map
```
license
=======
MIT
notes
=====
This module was written while sitting high above the ground in a tree.
var concatMap = require('../');
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ys = concatMap(xs, function (x) {
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
console.dir(ys);
module.exports = function (xs, fn) {
var res = [];
for (var i = 0; i < xs.length; i++) {
var x = fn(xs[i], i);
if (isArray(x)) res.push.apply(res, x);
else res.push(x);
}
return res;
};
var isArray = Array.isArray || function (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
{
"name": "concat-map",
"description": "concatenative mapdashery",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "git://github.com/substack/node-concat-map.git"
},
"main": "index.js",
"keywords": [
"concat",
"concatMap",
"map",
"functional",
"higher-order"
],
"directories": {
"example": "example",
"test": "test"
},
"scripts": {
"test": "tape test/*.js"
},
"devDependencies": {
"tape": "~2.4.0"
},
"license": "MIT",
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"testling": {
"files": "test/*.js",
"browsers": {
"ie": [
6,
7,
8,
9
],
"ff": [
3.5,
10,
15
],
"chrome": [
10,
22
],
"safari": [
5.1
],
"opera": [
12
]
}
},
"bugs": {
"url": "https://github.com/substack/node-concat-map/issues"
},
"homepage": "https://github.com/substack/node-concat-map",
"_id": "concat-map@0.0.1",
"dist": {
"shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b",
"tarball": "http://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
},
"_from": "concat-map@0.0.1",
"_npmVersion": "1.3.21",
"_npmUser": {
"name": "substack",
"email": "mail@substack.net"
},
"maintainers": [
{
"name": "substack",
"email": "mail@substack.net"
}
],
"_shasum": "d8a96bd77fd68df7793a73036a3ba0d5405d477b",
"_resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"readme": "ERROR: No README data found!"
}
var concatMap = require('../');
var test = require('tape');
test('empty or not', function (t) {
var xs = [ 1, 2, 3, 4, 5, 6 ];
var ixes = [];
var ys = concatMap(xs, function (x, ix) {
ixes.push(ix);
return x % 2 ? [ x - 0.1, x, x + 0.1 ] : [];
});
t.same(ys, [ 0.9, 1, 1.1, 2.9, 3, 3.1, 4.9, 5, 5.1 ]);
t.same(ixes, [ 0, 1, 2, 3, 4, 5 ]);
t.end();
});
test('always something', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function (x) {
return x === 'b' ? [ 'B', 'B', 'B' ] : [ x ];
});
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
t.end();
});
test('scalars', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function (x) {
return x === 'b' ? [ 'B', 'B', 'B' ] : x;
});
t.same(ys, [ 'a', 'B', 'B', 'B', 'c', 'd' ]);
t.end();
});
test('undefs', function (t) {
var xs = [ 'a', 'b', 'c', 'd' ];
var ys = concatMap(xs, function () {});
t.same(ys, [ undefined, undefined, undefined, undefined ]);
t.end();
});
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
"version": "1.1.0",
"repository": {
"type": "git",
"url": "git://github.com/juliangruber/brace-expansion.git"
},
"homepage": "https://github.com/juliangruber/brace-expansion",
"main": "index.js",
"scripts": {
"test": "tape test/*.js",
"gentest": "bash test/generate.sh"
},
"dependencies": {
"balanced-match": "^0.2.0",
"concat-map": "0.0.1"
},
"devDependencies": {
"tape": "^3.0.3"
},
"keywords": [],
"author": {
"name": "Julian Gruber",
"email": "mail@juliangruber.com",
"url": "http://juliangruber.com"
},
"license": "MIT",
"testling": {
"files": "test/*.js",
"browsers": [
"ie/8..latest",
"firefox/20..latest",
"firefox/nightly",
"chrome/25..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"gitHead": "b5fa3b1c74e5e2dba2d0efa19b28335641bc1164",
"bugs": {
"url": "https://github.com/juliangruber/brace-expansion/issues"
},
"_id": "brace-expansion@1.1.0",
"_shasum": "c9b7d03c03f37bc704be100e522b40db8f6cfcd9",
"_from": "brace-expansion@>=1.0.0 <2.0.0",
"_npmVersion": "2.1.10",
"_nodeVersion": "0.10.32",
"_npmUser": {
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
"maintainers": [
{
"name": "juliangruber",
"email": "julian@juliangruber.com"
},
{
"name": "isaacs",
"email": "isaacs@npmjs.com"
}
],
"dist": {
"shasum": "c9b7d03c03f37bc704be100e522b40db8f6cfcd9",
"tarball": "http://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz",
"readme": "ERROR: No README data found!"
}
var test = require('tape');
var expand = require('..');
var fs = require('fs');
var resfile = __dirname + '/bash-results.txt';
var cases = fs.readFileSync(resfile, 'utf8').split('><><><><');
// throw away the EOF marker
cases.pop()
test('matches bash expansions', function(t) {
cases.forEach(function(testcase) {
var set = testcase.split('\n');
var pattern = set.shift();
var actual = expand(pattern);
// If it expands to the empty string, then it's actually
// just nothing, but Bash is a singly typed language, so
// "nothing" is the same as "".
if (set.length === 1 && set[0] === '') {
set = []
} else {
// otherwise, strip off the [] that were added so that
// "" expansions would be preserved properly.
set = set.map(function (s) {
return s.replace(/^\[|\]$/g, '')
})
}
t.same(actual, set, pattern);
});
t.end();
})
A{b,{d,e},{f,g}}Z
[AbZ]
[AdZ]
[AeZ]
[AfZ]
[AgZ]><><><><PRE-{a,b}{{a,b},a,b}-POST
[PRE-aa-POST]
[PRE-ab-POST]
[PRE-aa-POST]
[PRE-ab-POST]
[PRE-ba-POST]
[PRE-bb-POST]
[PRE-ba-POST]
[PRE-bb-POST]><><><><\{a,b}{{a,b},a,b}
[{a,b}a]
[{a,b}b]
[{a,b}a]
[{a,b}b]><><><><{{a,b}
[{a]
[{b]><><><><{a,b}}
[a}]
[b}]><><><><{,}
><><><><a{,}
[a]
[a]><><><><{,}b
[b]
[b]><><><><a{,}b
[ab]
[ab]><><><><a{b}c
[a{b}c]><><><><a{1..5}b
[a1b]
[a2b]
[a3b]
[a4b]
[a5b]><><><><a{01..5}b
[a01b]
[a02b]
[a03b]
[a04b]
[a05b]><><><><a{-01..5}b
[a-01b]
[a000b]
[a001b]
[a002b]
[a003b]
[a004b]
[a005b]><><><><a{-01..5..3}b
[a-01b]
[a002b]
[a005b]><><><><a{001..9}b
[a001b]
[a002b]
[a003b]
[a004b]
[a005b]
[a006b]
[a007b]
[a008b]
[a009b]><><><><a{b,c{d,e},{f,g}h}x{y,z
[abx{y,z]
[acdx{y,z]
[acex{y,z]
[afhx{y,z]
[aghx{y,z]><><><><a{b,c{d,e},{f,g}h}x{y,z\}
[abx{y,z}]
[acdx{y,z}]
[acex{y,z}]
[afhx{y,z}]
[aghx{y,z}]><><><><a{b,c{d,e},{f,g}h}x{y,z}
[abxy]
[abxz]
[acdxy]
[acdxz]
[acexy]
[acexz]
[afhxy]
[afhxz]
[aghxy]
[aghxz]><><><><a{b{c{d,e}f{x,y{{g}h
[a{b{cdf{x,y{{g}h]
[a{b{cef{x,y{{g}h]><><><><a{b{c{d,e}f{x,y{}g}h
[a{b{cdfxh]
[a{b{cdfy{}gh]
[a{b{cefxh]
[a{b{cefy{}gh]><><><><a{b{c{d,e}f{x,y}}g}h
[a{b{cdfx}g}h]
[a{b{cdfy}g}h]
[a{b{cefx}g}h]
[a{b{cefy}g}h]><><><><a{b{c{d,e}f}g}h
[a{b{cdf}g}h]
[a{b{cef}g}h]><><><><a{{x,y},z}b
[axb]
[ayb]
[azb]><><><><f{x,y{g,z}}h
[fxh]
[fygh]
[fyzh]><><><><f{x,y{{g,z}}h
[f{x,y{g}h]
[f{x,y{z}h]><><><><f{x,y{{g,z}}h}
[fx]
[fy{g}h]
[fy{z}h]><><><><f{x,y{{g}h
[f{x,y{{g}h]><><><><f{x,y{{g}}h
[f{x,y{{g}}h]><><><><f{x,y{}g}h
[fxh]
[fy{}gh]><><><><z{a,b{,c}d
[z{a,bd]
[z{a,bcd]><><><><z{a,b},c}d
[za,c}d]
[zb,c}d]><><><><{-01..5}
[-01]
[000]
[001]
[002]
[003]
[004]
[005]><><><><{-05..100..5}
[-05]
[000]
[005]
[010]
[015]
[020]
[025]
[030]
[035]
[040]
[045]
[050]
[055]
[060]
[065]
[070]
[075]
[080]
[085]
[090]
[095]
[100]><><><><{-05..100}
[-05]
[-04]
[-03]
[-02]
[-01]
[000]
[001]
[002]
[003]
[004]
[005]
[006]
[007]
[008]
[009]
[010]
[011]
[012]
[013]
[014]
[015]
[016]
[017]
[018]
[019]
[020]
[021]
[022]
[023]
[024]
[025]
[026]
[027]
[028]
[029]
[030]
[031]
[032]
[033]
[034]
[035]
[036]
[037]
[038]
[039]
[040]
[041]
[042]
[043]
[044]
[045]
[046]
[047]
[048]
[049]
[050]
[051]
[052]
[053]
[054]
[055]
[056]
[057]
[058]
[059]
[060]
[061]
[062]
[063]
[064]
[065]
[066]
[067]
[068]
[069]
[070]
[071]
[072]
[073]
[074]
[075]
[076]
[077]
[078]
[079]
[080]
[081]
[082]
[083]
[084]
[085]
[086]
[087]
[088]
[089]
[090]
[091]
[092]
[093]
[094]
[095]
[096]
[097]
[098]
[099]
[100]><><><><{0..5..2}
[0]
[2]
[4]><><><><{0001..05..2}
[0001]
[0003]
[0005]><><><><{0001..-5..2}
[0001]
[-001]
[-003]
[-005]><><><><{0001..-5..-2}
[0001]
[-001]
[-003]
[-005]><><><><{0001..5..-2}
[0001]
[0003]
[0005]><><><><{01..5}
[01]
[02]
[03]
[04]
[05]><><><><{1..05}
[01]
[02]
[03]
[04]
[05]><><><><{1..05..3}
[01]
[04]><><><><{05..100}
[005]
[006]
[007]
[008]
[009]
[010]
[011]
[012]
[013]
[014]
[015]
[016]
[017]
[018]
[019]
[020]
[021]
[022]
[023]
[024]
[025]
[026]
[027]
[028]
[029]
[030]
[031]
[032]
[033]
[034]
[035]
[036]
[037]
[038]
[039]
[040]
[041]
[042]
[043]
[044]
[045]
[046]
[047]
[048]
[049]
[050]
[051]
[052]
[053]
[054]
[055]
[056]
[057]
[058]
[059]
[060]
[061]
[062]
[063]
[064]
[065]
[066]
[067]
[068]
[069]
[070]
[071]
[072]
[073]
[074]
[075]
[076]
[077]
[078]
[079]
[080]
[081]
[082]
[083]
[084]
[085]
[086]
[087]
[088]
[089]
[090]
[091]
[092]
[093]
[094]
[095]
[096]
[097]
[098]
[099]
[100]><><><><{0a..0z}
[{0a..0z}]><><><><{a,b\}c,d}
[a]
[b}c]
[d]><><><><{a,b{c,d}
[{a,bc]
[{a,bd]><><><><{a,b}c,d}
[ac,d}]
[bc,d}]><><><><{a..F}
[a]
[`]
[_]
[^]
[]]
[]
[[]
[Z]
[Y]
[X]
[W]
[V]
[U]
[T]
[S]
[R]
[Q]
[P]
[O]
[N]
[M]
[L]
[K]
[J]
[I]
[H]
[G]
[F]><><><><{A..f}
[A]
[B]
[C]
[D]
[E]
[F]
[G]
[H]
[I]
[J]
[K]
[L]
[M]
[N]
[O]
[P]
[Q]
[R]
[S]
[T]
[U]
[V]
[W]
[X]
[Y]
[Z]
[[]
[]
[]]
[^]
[_]
[`]
[a]
[b]
[c]
[d]
[e]
[f]><><><><{a..Z}
[a]
[`]
[_]
[^]
[]]
[]
[[]
[Z]><><><><{A..z}
[A]
[B]
[C]
[D]
[E]
[F]
[G]
[H]
[I]
[J]
[K]
[L]
[M]
[N]
[O]
[P]
[Q]
[R]
[S]
[T]
[U]
[V]
[W]
[X]
[Y]
[Z]
[[]
[]
[]]
[^]
[_]
[`]
[a]
[b]
[c]
[d]
[e]
[f]
[g]
[h]
[i]
[j]
[k]
[l]
[m]
[n]
[o]
[p]
[q]
[r]
[s]
[t]
[u]
[v]
[w]
[x]
[y]
[z]><><><><{z..A}
[z]
[y]
[x]
[w]
[v]
[u]
[t]
[s]
[r]
[q]
[p]
[o]
[n]
[m]
[l]
[k]
[j]
[i]
[h]
[g]
[f]
[e]
[d]
[c]
[b]
[a]
[`]
[_]
[^]
[]]
[]
[[]
[Z]
[Y]
[X]
[W]
[V]
[U]
[T]
[S]
[R]
[Q]
[P]
[O]
[N]
[M]
[L]
[K]
[J]
[I]
[H]
[G]
[F]
[E]
[D]
[C]
[B]
[A]><><><><{Z..a}
[Z]
[[]
[]
[]]
[^]
[_]
[`]
[a]><><><><{a..F..2}
[a]
[_]
[]]
[[]
[Y]
[W]
[U]
[S]
[Q]
[O]
[M]
[K]
[I]
[G]><><><><{A..f..02}
[A]
[C]
[E]
[G]
[I]
[K]
[M]
[O]
[Q]
[S]
[U]
[W]
[Y]
[[]
[]]
[_]
[a]
[c]
[e]><><><><{a..Z..5}
[a]
[]><><><><d{a..Z..5}b
[dab]
[db]><><><><{A..z..10}
[A]
[K]
[U]
[_]
[i]
[s]><><><><{z..A..-2}
[z]
[x]
[v]
[t]
[r]
[p]
[n]
[l]
[j]
[h]
[f]
[d]
[b]
[`]
[^]
[]
[Z]
[X]
[V]
[T]
[R]
[P]
[N]
[L]
[J]
[H]
[F]
[D]
[B]><><><><{Z..a..20}
[Z]><><><><{a{,b}
[{a]
[{ab]><><><><{a},b}
[a}]
[b]><><><><{x,y{,}g}
[x]
[yg]
[yg]><><><><{x,y{}g}
[x]
[y{}g]><><><><{{a,b}
[{a]
[{b]><><><><{{a,b},c}
[a]
[b]
[c]><><><><{{a,b}c}
[{ac}]
[{bc}]><><><><{{a,b},}
[a]
[b]><><><><X{{a,b},}X
[XaX]
[XbX]
[XX]><><><><{{a,b},}c
[ac]
[bc]
[c]><><><><{{a,b}.}
[{a.}]
[{b.}]><><><><{{a,b}}
[{a}]
[{b}]><><><><X{a..#}X
[X{a..#}X]><><><><
><><><><{-10..00}
[-10]
[-09]
[-08]
[-07]
[-06]
[-05]
[-04]
[-03]
[-02]
[-01]
[000]><><><><{a,\\{a,b}c}
[a]
[\ac]
[\bc]><><><><{a,\{a,b}c}
[ac}]
[{ac}]
[bc}]><><><><a,\{b,c}
[a,{b,c}]><><><><{-10.\.00}
[{-10..00}]><><><><ff{c,b,a}
[ffc]
[ffb]
[ffa]><><><><f{d,e,f}g
[fdg]
[feg]
[ffg]><><><><{l,n,m}xyz
[lxyz]
[nxyz]
[mxyz]><><><><{abc\,def}
[{abc,def}]><><><><{abc}
[{abc}]><><><><{x\,y,\{abc\},trie}
[x,y]
[{abc}]
[trie]><><><><{}
[{}]><><><><}
[}]><><><><{
[{]><><><><abcd{efgh
[abcd{efgh]><><><><{1..10}
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]><><><><{0..10,braces}
[0..10]
[braces]><><><><{{0..10},braces}
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
[braces]><><><><x{{0..10},braces}y
[x0y]
[x1y]
[x2y]
[x3y]
[x4y]
[x5y]
[x6y]
[x7y]
[x8y]
[x9y]
[x10y]
[xbracesy]><><><><{3..3}
[3]><><><><x{3..3}y
[x3y]><><><><{10..1}
[10]
[9]
[8]
[7]
[6]
[5]
[4]
[3]
[2]
[1]><><><><{10..1}y
[10y]
[9y]
[8y]
[7y]
[6y]
[5y]
[4y]
[3y]
[2y]
[1y]><><><><x{10..1}y
[x10y]
[x9y]
[x8y]
[x7y]
[x6y]
[x5y]
[x4y]
[x3y]
[x2y]
[x1y]><><><><{a..f}
[a]
[b]
[c]
[d]
[e]
[f]><><><><{f..a}
[f]
[e]
[d]
[c]
[b]
[a]><><><><{a..A}
[a]
[`]
[_]
[^]
[]]
[]
[[]
[Z]
[Y]
[X]
[W]
[V]
[U]
[T]
[S]
[R]
[Q]
[P]
[O]
[N]
[M]
[L]
[K]
[J]
[I]
[H]
[G]
[F]
[E]
[D]
[C]
[B]
[A]><><><><{A..a}
[A]
[B]
[C]
[D]
[E]
[F]
[G]
[H]
[I]
[J]
[K]
[L]
[M]
[N]
[O]
[P]
[Q]
[R]
[S]
[T]
[U]
[V]
[W]
[X]
[Y]
[Z]
[[]
[]
[]]
[^]
[_]
[`]
[a]><><><><{f..f}
[f]><><><><{1..f}
[{1..f}]><><><><{f..1}
[{f..1}]><><><><{-1..-10}
[-1]
[-2]
[-3]
[-4]
[-5]
[-6]
[-7]
[-8]
[-9]
[-10]><><><><{-20..0}
[-20]
[-19]
[-18]
[-17]
[-16]
[-15]
[-14]
[-13]
[-12]
[-11]
[-10]
[-9]
[-8]
[-7]
[-6]
[-5]
[-4]
[-3]
[-2]
[-1]
[0]><><><><a-{b{d,e}}-c
[a-{bd}-c]
[a-{be}-c]><><><><a-{bdef-{g,i}-c
[a-{bdef-g-c]
[a-{bdef-i-c]><><><><{klklkl}{1,2,3}
[{klklkl}1]
[{klklkl}2]
[{klklkl}3]><><><><{1..10..2}
[1]
[3]
[5]
[7]
[9]><><><><{-1..-10..2}
[-1]
[-3]
[-5]
[-7]
[-9]><><><><{-1..-10..-2}
[-1]
[-3]
[-5]
[-7]
[-9]><><><><{10..1..-2}
[10]
[8]
[6]
[4]
[2]><><><><{10..1..2}
[10]
[8]
[6]
[4]
[2]><><><><{1..20..2}
[1]
[3]
[5]
[7]
[9]
[11]
[13]
[15]
[17]
[19]><><><><{1..20..20}
[1]><><><><{100..0..5}
[100]
[95]
[90]
[85]
[80]
[75]
[70]
[65]
[60]
[55]
[50]
[45]
[40]
[35]
[30]
[25]
[20]
[15]
[10]
[5]
[0]><><><><{100..0..-5}
[100]
[95]
[90]
[85]
[80]
[75]
[70]
[65]
[60]
[55]
[50]
[45]
[40]
[35]
[30]
[25]
[20]
[15]
[10]
[5]
[0]><><><><{a..z}
[a]
[b]
[c]
[d]
[e]
[f]
[g]
[h]
[i]
[j]
[k]
[l]
[m]
[n]
[o]
[p]
[q]
[r]
[s]
[t]
[u]
[v]
[w]
[x]
[y]
[z]><><><><{a..z..2}
[a]
[c]
[e]
[g]
[i]
[k]
[m]
[o]
[q]
[s]
[u]
[w]
[y]><><><><{z..a..-2}
[z]
[x]
[v]
[t]
[r]
[p]
[n]
[l]
[j]
[h]
[f]
[d]
[b]><><><><{2147483645..2147483649}
[2147483645]
[2147483646]
[2147483647]
[2147483648]
[2147483649]><><><><{10..0..2}
[10]
[8]
[6]
[4]
[2]
[0]><><><><{10..0..-2}
[10]
[8]
[6]
[4]
[2]
[0]><><><><{-50..-0..5}
[-50]
[-45]
[-40]
[-35]
[-30]
[-25]
[-20]
[-15]
[-10]
[-5]
[0]><><><><{1..10.f}
[{1..10.f}]><><><><{1..ff}
[{1..ff}]><><><><{1..10..ff}
[{1..10..ff}]><><><><{1.20..2}
[{1.20..2}]><><><><{1..20..f2}
[{1..20..f2}]><><><><{1..20..2f}
[{1..20..2f}]><><><><{1..2f..2}
[{1..2f..2}]><><><><{1..ff..2}
[{1..ff..2}]><><><><{1..ff}
[{1..ff}]><><><><{1..f}
[{1..f}]><><><><{1..0f}
[{1..0f}]><><><><{1..10f}
[{1..10f}]><><><><{1..10.f}
[{1..10.f}]><><><><{1..10.f}
[{1..10.f}]><><><><
\ No newline at end of file
# skip quotes for now
# "{x,x}"
# {"x,x"}
# {x","x}
# '{a,b}{{a,b},a,b}'
A{b,{d,e},{f,g}}Z
PRE-{a,b}{{a,b},a,b}-POST
\\{a,b}{{a,b},a,b}
{{a,b}
{a,b}}
{,}
a{,}
{,}b
a{,}b
a{b}c
a{1..5}b
a{01..5}b
a{-01..5}b
a{-01..5..3}b
a{001..9}b
a{b,c{d,e},{f,g}h}x{y,z
a{b,c{d,e},{f,g}h}x{y,z\\}
a{b,c{d,e},{f,g}h}x{y,z}
a{b{c{d,e}f{x,y{{g}h
a{b{c{d,e}f{x,y{}g}h
a{b{c{d,e}f{x,y}}g}h
a{b{c{d,e}f}g}h
a{{x,y},z}b
f{x,y{g,z}}h
f{x,y{{g,z}}h
f{x,y{{g,z}}h}
f{x,y{{g}h
f{x,y{{g}}h
f{x,y{}g}h
z{a,b{,c}d
z{a,b},c}d
{-01..5}
{-05..100..5}
{-05..100}
{0..5..2}
{0001..05..2}
{0001..-5..2}
{0001..-5..-2}
{0001..5..-2}
{01..5}
{1..05}
{1..05..3}
{05..100}
{0a..0z}
{a,b\\}c,d}
{a,b{c,d}
{a,b}c,d}
{a..F}
{A..f}
{a..Z}
{A..z}
{z..A}
{Z..a}
{a..F..2}
{A..f..02}
{a..Z..5}
d{a..Z..5}b
{A..z..10}
{z..A..-2}
{Z..a..20}
{a{,b}
{a},b}
{x,y{,}g}
{x,y{}g}
{{a,b}
{{a,b},c}
{{a,b}c}
{{a,b},}
X{{a,b},}X
{{a,b},}c
{{a,b}.}
{{a,b}}
X{a..#}X
# this next one is an empty string
{-10..00}
# Need to escape slashes in here for reasons i guess.
{a,\\\\{a,b}c}
{a,\\{a,b}c}
a,\\{b,c}
{-10.\\.00}
#### bash tests/braces.tests
# Note that some tests are edited out because some features of
# bash are intentionally not supported in this brace expander.
ff{c,b,a}
f{d,e,f}g
{l,n,m}xyz
{abc\\,def}
{abc}
{x\\,y,\\{abc\\},trie}
# not impementing back-ticks obviously
# XXXX\\{`echo a b c | tr ' ' ','`\\}
{}
# We only ever have to worry about parsing a single argument,
# not a command line, so spaces have a different meaning than bash.
# { }
}
{
abcd{efgh
# spaces
# foo {1,2} bar
# not impementing back-ticks obviously
# `zecho foo {1,2} bar`
# $(zecho foo {1,2} bar)
# ${var} is not a variable here, like it is in bash. omit.
# foo{bar,${var}.}
# foo{bar,${var}}
# isaacs: skip quotes for now
# "${var}"{x,y}
# $var{x,y}
# ${var}{x,y}
# new sequence brace operators
{1..10}
# this doesn't work yet
{0..10,braces}
# but this does
{{0..10},braces}
x{{0..10},braces}y
{3..3}
x{3..3}y
{10..1}
{10..1}y
x{10..1}y
{a..f}
{f..a}
{a..A}
{A..a}
{f..f}
# mixes are incorrectly-formed brace expansions
{1..f}
{f..1}
# spaces
# 0{1..9} {10..20}
# do negative numbers work?
{-1..-10}
{-20..0}
# weirdly-formed brace expansions -- fixed in post-bash-3.1
a-{b{d,e}}-c
a-{bdef-{g,i}-c
# isaacs: skip quotes for now
# {"klklkl"}{1,2,3}
# isaacs: this is a valid test, though
{klklkl}{1,2,3}
# {"x,x"}
{1..10..2}
{-1..-10..2}
{-1..-10..-2}
{10..1..-2}
{10..1..2}
{1..20..2}
{1..20..20}
{100..0..5}
{100..0..-5}
{a..z}
{a..z..2}
{z..a..-2}
# make sure brace expansion handles ints > 2**31 - 1 using intmax_t
{2147483645..2147483649}
# unwanted zero-padding -- fixed post-bash-4.0
{10..0..2}
{10..0..-2}
{-50..-0..5}
# bad
{1..10.f}
{1..ff}
{1..10..ff}
{1.20..2}
{1..20..f2}
{1..20..2f}
{1..2f..2}
{1..ff..2}
{1..ff}
{1..f}
{1..0f}
{1..10f}
{1..10.f}
{1..10.f}
var test = require('tape');
var expand = require('..');
test('ignores ${', function(t) {
t.deepEqual(expand('${1..3}'), ['${1..3}']);
t.deepEqual(expand('${a,b}${c,d}'), ['${a,b}${c,d}']);
t.deepEqual(expand('x${a,b}x${c,d}x'), ['x${a,b}x${c,d}x']);
t.end();
});
var test = require('tape');
var expand = require('..');
test('empty option', function(t) {
t.deepEqual(expand('-v{,,,,}'), [
'-v', '-v', '-v', '-v', '-v'
]);
t.end();
});
#!/usr/bin/env bash
set -e
# Bash 4.3 because of arbitrary need to pick a single standard.
if [ "${BASH_VERSINFO[0]}" != "4" ] || [ "${BASH_VERSINFO[1]}" != "3" ]; then
echo "this script requires bash 4.3" >&2
exit 1
fi
CDPATH= cd "$(dirname "$0")"
js='require("./")(process.argv[1]).join(" ")'
cat cases.txt | \
while read case; do
if [ "${case:0:1}" = "#" ]; then
continue;
fi;
b="$($BASH -c 'for c in '"$case"'; do echo ["$c"]; done')"
echo "$case"
echo -n "$b><><><><";
done > bash-results.txt
var test = require('tape');
var expand = require('..');
test('negative increment', function(t) {
t.deepEqual(expand('{3..1}'), ['3', '2', '1']);
t.deepEqual(expand('{10..8}'), ['10', '9', '8']);
t.deepEqual(expand('{10..08}'), ['10', '09', '08']);
t.deepEqual(expand('{c..a}'), ['c', 'b', 'a']);
t.deepEqual(expand('{4..0..2}'), ['4', '2', '0']);
t.deepEqual(expand('{4..0..-2}'), ['4', '2', '0']);
t.deepEqual(expand('{e..a..2}'), ['e', 'c', 'a']);
t.end();
});
var test = require('tape');
var expand = require('..');
test('nested', function(t) {
t.deepEqual(expand('{a,b{1..3},c}'), [
'a', 'b1', 'b2', 'b3', 'c'
]);
t.deepEqual(expand('{{A..Z},{a..z}}'),
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
);
t.deepEqual(expand('ppp{,config,oe{,conf}}'), [
'ppp', 'pppconfig', 'pppoe', 'pppoeconf'
]);
t.end();
});
var test = require('tape');
var expand = require('..');
test('order', function(t) {
t.deepEqual(expand('a{d,c,b}e'), [
'ade', 'ace', 'abe'
]);
t.end();
});
var test = require('tape');
var expand = require('..');
test('pad', function(t) {
t.deepEqual(expand('{9..11}'), [
'9', '10', '11'
]);
t.deepEqual(expand('{09..11}'), [
'09', '10', '11'
]);
t.end();
});
var test = require('tape');
var expand = require('..');
test('x and y of same type', function(t) {
t.deepEqual(expand('{a..9}'), ['{a..9}']);
t.end();
});
var test = require('tape');
var expand = require('..');
test('numeric sequences', function(t) {
t.deepEqual(expand('a{1..2}b{2..3}c'), [
'a1b2c', 'a1b3c', 'a2b2c', 'a2b3c'
]);
t.deepEqual(expand('{1..2}{2..3}'), [
'12', '13', '22', '23'
]);
t.end();
});
test('numeric sequences with step count', function(t) {
t.deepEqual(expand('{0..8..2}'), [
'0', '2', '4', '6', '8'
]);
t.deepEqual(expand('{1..8..2}'), [
'1', '3', '5', '7'
]);
t.end();
});
test('numeric sequence with negative x / y', function(t) {
t.deepEqual(expand('{3..-2}'), [
'3', '2', '1', '0', '-1', '-2'
]);
t.end();
});
test('alphabetic sequences', function(t) {
t.deepEqual(expand('1{a..b}2{b..c}3'), [
'1a2b3', '1a2c3', '1b2b3', '1b2c3'
]);
t.deepEqual(expand('{a..b}{b..c}'), [
'ab', 'ac', 'bb', 'bc'
]);
t.end();
});
test('alphabetic sequences with step count', function(t) {
t.deepEqual(expand('{a..k..2}'), [
'a', 'c', 'e', 'g', 'i', 'k'
]);
t.deepEqual(expand('{b..k..2}'), [
'b', 'd', 'f', 'h', 'j'
]);
t.end();
});
{
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me"
},
"name": "minimatch",
"description": "a glob matcher in javascript",
"version": "2.0.8",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/minimatch.git"
},
"main": "minimatch.js",
"scripts": {
"pretest": "standard minimatch.js test/*.js",
"test": "tap test/*.js",
"prepublish": "browserify -o browser.js -e minimatch.js --bare"
},
"engines": {
"node": "*"
},
"dependencies": {
"brace-expansion": "^1.0.0"
},
"devDependencies": {
"browserify": "^9.0.3",
"standard": "^3.7.2",
"tap": ""
},
"license": "ISC",
"files": [
"minimatch.js",
"browser.js"
],
"gitHead": "0bc7d9c4b2bc816502184862b45bd090de3406a3",
"bugs": {
"url": "https://github.com/isaacs/minimatch/issues"
},
"homepage": "https://github.com/isaacs/minimatch#readme",
"_id": "minimatch@2.0.8",
"_shasum": "0bc20f6bf3570a698ef0ddff902063c6cabda6bf",
"_from": "minimatch@>=2.0.1 <3.0.0",
"_npmVersion": "2.10.0",
"_nodeVersion": "2.0.1",
"_npmUser": {
"name": "isaacs",
"email": "isaacs@npmjs.com"
},
"dist": {
"shasum": "0bc20f6bf3570a698ef0ddff902063c6cabda6bf",
"tarball": "http://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"directories": {},
"_resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.8.tgz",
"readme": "ERROR: No README data found!"
}
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# once
Only call a function once.
## usage
```javascript
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
```
Or add to the Function.prototype in a responsible way:
```javascript
// only has to be done once
require('once').proto()
function load (file, cb) {
cb = cb.once()
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
```
Ironically, the prototype feature makes this module twice as
complicated as necessary.
To check whether you function has been called, use `fn.called`. Once the
function is called for the first time the return value of the original
function is saved in `fn.value` and subsequent calls will continue to
return this value.
```javascript
var once = require('once')
function load (cb) {
cb = once(cb)
var stream = createStream()
stream.once('data', cb)
stream.once('end', function () {
if (!cb.called) cb(new Error('not found'))
})
}
```
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# wrappy
Callback wrapping utility
## USAGE
```javascript
var wrappy = require("wrappy")
// var wrapper = wrappy(wrapperFunction)
// make sure a cb is called only once
// See also: http://npm.im/once for this specific use case
var once = wrappy(function (cb) {
var called = false
return function () {
if (called) return
called = true
return cb.apply(this, arguments)
}
})
function printBoo () {
console.log('boo')
}
// has some rando property
printBoo.iAmBooPrinter = true
var onlyPrintOnce = once(printBoo)
onlyPrintOnce() // prints 'boo'
onlyPrintOnce() // does nothing
// random property is retained!
assert.equal(onlyPrintOnce.iAmBooPrinter, true)
```
{
"name": "wrappy",
"version": "1.0.1",
"description": "Callback wrapping utility",
"main": "wrappy.js",
"directories": {
"test": "test"
},
"dependencies": {},
"devDependencies": {
"tap": "^0.4.12"
},
"scripts": {
"test": "tap test/*.js"
},
"repository": {
"type": "git",
"url": "https://github.com/npm/wrappy"
},
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/wrappy/issues"
},
"homepage": "https://github.com/npm/wrappy",
"readme": "# wrappy\n\nCallback wrapping utility\n\n## USAGE\n\n```javascript\nvar wrappy = require(\"wrappy\")\n\n// var wrapper = wrappy(wrapperFunction)\n\n// make sure a cb is called only once\n// See also: http://npm.im/once for this specific use case\nvar once = wrappy(function (cb) {\n var called = false\n return function () {\n if (called) return\n called = true\n return cb.apply(this, arguments)\n }\n})\n\nfunction printBoo () {\n console.log('boo')\n}\n// has some rando property\nprintBoo.iAmBooPrinter = true\n\nvar onlyPrintOnce = once(printBoo)\n\nonlyPrintOnce() // prints 'boo'\nonlyPrintOnce() // does nothing\n\n// random property is retained!\nassert.equal(onlyPrintOnce.iAmBooPrinter, true)\n```\n",
"readmeFilename": "README.md",
"_id": "wrappy@1.0.1",
"_shasum": "1e65969965ccbc2db4548c6b84a6f2c5aedd4739",
"_resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz",
"_from": "wrappy@>=1.0.0 <2.0.0"
}
var test = require('tap').test
var wrappy = require('../wrappy.js')
test('basic', function (t) {
function onceifier (cb) {
var called = false
return function () {
if (called) return
called = true
return cb.apply(this, arguments)
}
}
onceifier.iAmOnce = {}
var once = wrappy(onceifier)
t.equal(once.iAmOnce, onceifier.iAmOnce)
var called = 0
function boo () {
t.equal(called, 0)
called++
}
// has some rando property
boo.iAmBoo = true
var onlyPrintOnce = once(boo)
onlyPrintOnce() // prints 'boo'
onlyPrintOnce() // does nothing
t.equal(called, 1)
// random property is retained!
t.equal(onlyPrintOnce.iAmBoo, true)
var logs = []
var logwrap = wrappy(function (msg, cb) {
logs.push(msg + ' wrapping cb')
return function () {
logs.push(msg + ' before cb')
var ret = cb.apply(this, arguments)
logs.push(msg + ' after cb')
}
})
var c = logwrap('foo', function () {
t.same(logs, [ 'foo wrapping cb', 'foo before cb' ])
})
c()
t.same(logs, [ 'foo wrapping cb', 'foo before cb', 'foo after cb' ])
t.end()
})
// Returns a wrapper function that returns a wrapped callback
// The wrapper function should do some stuff, and return a
// presumably different callback function.
// This makes sure that own properties are retained, so that
// decorations and such are not lost along the way.
module.exports = wrappy
function wrappy (fn, cb) {
if (fn && cb) return wrappy(fn)(cb)
if (typeof fn !== 'function')
throw new TypeError('need wrapper function')
Object.keys(fn).forEach(function (k) {
wrapper[k] = fn[k]
})
return wrapper
function wrapper() {
var args = new Array(arguments.length)
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i]
}
var ret = fn.apply(this, args)
var cb = args[args.length-1]
if (typeof ret === 'function' && ret !== cb) {
Object.keys(cb).forEach(function (k) {
ret[k] = cb[k]
})
}
return ret
}
}
var wrappy = require('wrappy')
module.exports = wrappy(once)
once.proto = once(function () {
Object.defineProperty(Function.prototype, 'once', {
value: function () {
return once(this)
},
configurable: true
})
})
function once (fn) {
var f = function () {
if (f.called) return f.value
f.called = true
return f.value = fn.apply(this, arguments)
}
f.called = false
return f
}
{
"name": "once",
"version": "1.3.2",
"description": "Run a function exactly one time",
"main": "once.js",
"directories": {
"test": "test"
},
"dependencies": {
"wrappy": "1"
},
"devDependencies": {
"tap": "~0.3.0"
},
"scripts": {
"test": "tap test/*.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/once.git"
},
"keywords": [
"once",
"function",
"one",
"single"
],
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "ISC",
"gitHead": "e35eed5a7867574e2bf2260a1ba23970958b22f2",
"bugs": {
"url": "https://github.com/isaacs/once/issues"
},
"homepage": "https://github.com/isaacs/once#readme",
"_id": "once@1.3.2",
"_shasum": "d8feeca93b039ec1dcdee7741c92bdac5e28081b",
"_from": "once@>=1.3.0 <2.0.0",
"_npmVersion": "2.9.1",
"_nodeVersion": "2.0.0",
"_npmUser": {
"name": "isaacs",
"email": "isaacs@npmjs.com"
},
"dist": {
"shasum": "d8feeca93b039ec1dcdee7741c92bdac5e28081b",
"tarball": "http://registry.npmjs.org/once/-/once-1.3.2.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"_resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz",
"readme": "ERROR: No README data found!"
}
var test = require('tap').test
var once = require('../once.js')
test('once', function (t) {
var f = 0
function fn (g) {
t.equal(f, 0)
f ++
return f + g + this
}
fn.ownProperty = {}
var foo = once(fn)
t.equal(fn.ownProperty, foo.ownProperty)
t.notOk(foo.called)
for (var i = 0; i < 1E3; i++) {
t.same(f, i === 0 ? 0 : 1)
var g = foo.call(1, 1)
t.ok(foo.called)
t.same(g, 3)
t.same(f, 1)
}
t.end()
})
'use strict';
function posix(path) {
return path.charAt(0) === '/';
};
function win32(path) {
// https://github.com/joyent/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56
var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/;
var result = splitDeviceRe.exec(path);
var device = result[1] || '';
var isUnc = !!device && device.charAt(1) !== ':';
// UNC paths are always absolute
return !!result[2] || isUnc;
};
module.exports = process.platform === 'win32' ? win32 : posix;
module.exports.posix = posix;
module.exports.win32 = win32;
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
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.
{
"name": "path-is-absolute",
"version": "1.0.0",
"description": "Node.js 0.12 path.isAbsolute() ponyfill",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/sindresorhus/path-is-absolute"
},
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "node test.js"
},
"files": [
"index.js"
],
"keywords": [
"path",
"paths",
"file",
"dir",
"absolute",
"isabsolute",
"is-absolute",
"built-in",
"util",
"utils",
"core",
"ponyfill",
"polyfill",
"shim",
"is",
"detect",
"check"
],
"gitHead": "7a76a0c9f2263192beedbe0a820e4d0baee5b7a1",
"bugs": {
"url": "https://github.com/sindresorhus/path-is-absolute/issues"
},
"homepage": "https://github.com/sindresorhus/path-is-absolute",
"_id": "path-is-absolute@1.0.0",
"_shasum": "263dada66ab3f2fb10bf7f9d24dd8f3e570ef912",
"_from": "path-is-absolute@>=1.0.0 <2.0.0",
"_npmVersion": "2.5.1",
"_nodeVersion": "0.12.0",
"_npmUser": {
"name": "sindresorhus",
"email": "sindresorhus@gmail.com"
},
"maintainers": [
{
"name": "sindresorhus",
"email": "sindresorhus@gmail.com"
}
],
"dist": {
"shasum": "263dada66ab3f2fb10bf7f9d24dd8f3e570ef912",
"tarball": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz",
"readme": "ERROR: No README data found!"
}
# path-is-absolute [![Build Status](https://travis-ci.org/sindresorhus/path-is-absolute.svg?branch=master)](https://travis-ci.org/sindresorhus/path-is-absolute)
> Node.js 0.12 [`path.isAbsolute()`](http://nodejs.org/api/path.html#path_path_isabsolute_path) ponyfill
> Ponyfill: A polyfill that doesn't overwrite the native method
## Install
```
$ npm install --save path-is-absolute
```
## Usage
```js
var pathIsAbsolute = require('path-is-absolute');
// Linux
pathIsAbsolute('/home/foo');
//=> true
// Windows
pathIsAbsolute('C:/Users/');
//=> true
// Any OS
pathIsAbsolute.posix('/home/foo');
//=> true
```
## API
See the [`path.isAbsolute()` docs](http://nodejs.org/api/path.html#path_path_isabsolute_path).
### pathIsAbsolute(path)
### pathIsAbsolute.posix(path)
The Posix specific version.
### pathIsAbsolute.win32(path)
The Windows specific version.
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)
{
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"name": "glob",
"description": "a little globber",
"version": "5.0.10",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-glob.git"
},
"main": "glob.js",
"files": [
"glob.js",
"sync.js",
"common.js"
],
"engines": {
"node": "*"
},
"dependencies": {
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^2.0.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"devDependencies": {
"mkdirp": "0",
"rimraf": "^2.2.8",
"tap": "^1.1.4",
"tick": "0.0.6"
},
"scripts": {
"prepublish": "npm run benchclean",
"profclean": "rm -f v8.log profile.txt",
"test": "tap test/*.js --cov",
"test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js",
"bench": "bash benchmark.sh",
"prof": "bash prof.sh && cat profile.txt",
"benchclean": "bash benchclean.sh"
},
"license": "ISC",
"gitHead": "e3cdccc0e295c2e1d5f40cf74c73ea17a8319c5c",
"bugs": {
"url": "https://github.com/isaacs/node-glob/issues"
},
"homepage": "https://github.com/isaacs/node-glob#readme",
"_id": "glob@5.0.10",
"_shasum": "3ee350319f31f352cef6899a48f6b6b7834c6899",
"_from": "glob@>=5.0.10 <6.0.0",
"_npmVersion": "2.10.1",
"_nodeVersion": "2.0.1",
"_npmUser": {
"name": "isaacs",
"email": "isaacs@npmjs.com"
},
"dist": {
"shasum": "3ee350319f31f352cef6899a48f6b6b7834c6899",
"tarball": "http://registry.npmjs.org/glob/-/glob-5.0.10.tgz"
},
"maintainers": [
{
"name": "isaacs",
"email": "i@izs.me"
}
],
"directories": {},
"_resolved": "https://registry.npmjs.org/glob/-/glob-5.0.10.tgz",
"readme": "ERROR: No README data found!"
}
module.exports = globSync
globSync.GlobSync = GlobSync
var fs = require('fs')
var minimatch = require('minimatch')
var Minimatch = minimatch.Minimatch
var Glob = require('./glob.js').Glob
var util = require('util')
var path = require('path')
var assert = require('assert')
var isAbsolute = require('path-is-absolute')
var common = require('./common.js')
var alphasort = common.alphasort
var alphasorti = common.alphasorti
var setopts = common.setopts
var ownProp = common.ownProp
var childrenIgnored = common.childrenIgnored
function globSync (pattern, options) {
if (typeof options === 'function' || arguments.length === 3)
throw new TypeError('callback provided to sync glob\n'+
'See: https://github.com/isaacs/node-glob/issues/167')
return new GlobSync(pattern, options).found
}
function GlobSync (pattern, options) {
if (!pattern)
throw new Error('must provide pattern')
if (typeof options === 'function' || arguments.length === 3)
throw new TypeError('callback provided to sync glob\n'+
'See: https://github.com/isaacs/node-glob/issues/167')
if (!(this instanceof GlobSync))
return new GlobSync(pattern, options)
setopts(this, pattern, options)
if (this.noprocess)
return this
var n = this.minimatch.set.length
this.matches = new Array(n)
for (var i = 0; i < n; i ++) {
this._process(this.minimatch.set[i], i, false)
}
this._finish()
}
GlobSync.prototype._finish = function () {
assert(this instanceof GlobSync)
if (this.realpath) {
var self = this
this.matches.forEach(function (matchset, index) {
var set = self.matches[index] = Object.create(null)
for (var p in matchset) {
try {
p = self._makeAbs(p)
var real = fs.realpathSync(p, this.realpathCache)
set[real] = true
} catch (er) {
if (er.syscall === 'stat')
set[self._makeAbs(p)] = true
else
throw er
}
}
})
}
common.finish(this)
}
GlobSync.prototype._process = function (pattern, index, inGlobStar) {
assert(this instanceof GlobSync)
// Get the first [n] parts of pattern that are all strings.
var n = 0
while (typeof pattern[n] === 'string') {
n ++
}
// now n is the index of the first one that is *not* a string.
// See if there's anything else
var prefix
switch (n) {
// if not, then this is rather simple
case pattern.length:
this._processSimple(pattern.join('/'), index)
return
case 0:
// pattern *starts* with some non-trivial item.
// going to readdir(cwd), but not include the prefix in matches.
prefix = null
break
default:
// pattern has some string bits in the front.
// whatever it starts with, whether that's 'absolute' like /foo/bar,
// or 'relative' like '../baz'
prefix = pattern.slice(0, n).join('/')
break
}
var remain = pattern.slice(n)
// get the list of entries.
var read
if (prefix === null)
read = '.'
else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) {
if (!prefix || !isAbsolute(prefix))
prefix = '/' + prefix
read = prefix
} else
read = prefix
var abs = this._makeAbs(read)
//if ignored, skip processing
if (childrenIgnored(this, read))
return
var isGlobStar = remain[0] === minimatch.GLOBSTAR
if (isGlobStar)
this._processGlobStar(prefix, read, abs, remain, index, inGlobStar)
else
this._processReaddir(prefix, read, abs, remain, index, inGlobStar)
}
GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) {
var entries = this._readdir(abs, inGlobStar)
// if the abs isn't a dir, then nothing can match!
if (!entries)
return
// It will only match dot entries if it starts with a dot, or if
// dot is set. Stuff like @(.foo|.bar) isn't allowed.
var pn = remain[0]
var negate = !!this.minimatch.negate
var rawGlob = pn._glob
var dotOk = this.dot || rawGlob.charAt(0) === '.'
var matchedEntries = []
for (var i = 0; i < entries.length; i++) {
var e = entries[i]
if (e.charAt(0) !== '.' || dotOk) {
var m
if (negate && !prefix) {
m = !e.match(pn)
} else {
m = e.match(pn)
}
if (m)
matchedEntries.push(e)
}
}
var len = matchedEntries.length
// If there are no matched entries, then nothing matches.
if (len === 0)
return
// if this is the last remaining pattern bit, then no need for
// an additional stat *unless* the user has specified mark or
// stat explicitly. We know they exist, since readdir returned
// them.
if (remain.length === 1 && !this.mark && !this.stat) {
if (!this.matches[index])
this.matches[index] = Object.create(null)
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
if (prefix) {
if (prefix.slice(-1) !== '/')
e = prefix + '/' + e
else
e = prefix + e
}
if (e.charAt(0) === '/' && !this.nomount) {
e = path.join(this.root, e)
}
this.matches[index][e] = true
}
// This was the last one, and no stats were needed
return
}
// now test all matched entries as stand-ins for that part
// of the pattern.
remain.shift()
for (var i = 0; i < len; i ++) {
var e = matchedEntries[i]
var newPattern
if (prefix)
newPattern = [prefix, e]
else
newPattern = [e]
this._process(newPattern.concat(remain), index, inGlobStar)
}
}
GlobSync.prototype._emitMatch = function (index, e) {
var abs = this._makeAbs(e)
if (this.mark)
e = this._mark(e)
if (this.matches[index][e])
return
if (this.nodir) {
var c = this.cache[this._makeAbs(e)]
if (c === 'DIR' || Array.isArray(c))
return
}
this.matches[index][e] = true
if (this.stat)
this._stat(e)
}
GlobSync.prototype._readdirInGlobStar = function (abs) {
// follow all symlinked directories forever
// just proceed as if this is a non-globstar situation
if (this.follow)
return this._readdir(abs, false)
var entries
var lstat
var stat
try {
lstat = fs.lstatSync(abs)
} catch (er) {
// lstat failed, doesn't exist
return null
}
var isSym = lstat.isSymbolicLink()
this.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
if (!isSym && !lstat.isDirectory())
this.cache[abs] = 'FILE'
else
entries = this._readdir(abs, false)
return entries
}
GlobSync.prototype._readdir = function (abs, inGlobStar) {
var entries
if (inGlobStar && !ownProp(this.symlinks, abs))
return this._readdirInGlobStar(abs)
if (ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (!c || c === 'FILE')
return null
if (Array.isArray(c))
return c
}
try {
return this._readdirEntries(abs, fs.readdirSync(abs))
} catch (er) {
this._readdirError(abs, er)
return null
}
}
GlobSync.prototype._readdirEntries = function (abs, entries) {
// if we haven't asked to stat everything, then just
// assume that everything in there exists, so we can avoid
// having to stat it a second time.
if (!this.mark && !this.stat) {
for (var i = 0; i < entries.length; i ++) {
var e = entries[i]
if (abs === '/')
e = abs + e
else
e = abs + '/' + e
this.cache[e] = true
}
}
this.cache[abs] = entries
// mark and cache dir-ness
return entries
}
GlobSync.prototype._readdirError = function (f, er) {
// handle errors, and cache the information
switch (er.code) {
case 'ENOTDIR': // totally normal. means it *does* exist.
this.cache[this._makeAbs(f)] = 'FILE'
break
case 'ENOENT': // not terribly unusual
case 'ELOOP':
case 'ENAMETOOLONG':
case 'UNKNOWN':
this.cache[this._makeAbs(f)] = false
break
default: // some unusual error. Treat as failure.
this.cache[this._makeAbs(f)] = false
if (this.strict)
throw er
if (!this.silent)
console.error('glob error', er)
break
}
}
GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) {
var entries = this._readdir(abs, inGlobStar)
// no entries means not a dir, so it can never have matches
// foo.txt/** doesn't match foo.txt
if (!entries)
return
// test without the globstar, and with every child both below
// and replacing the globstar.
var remainWithoutGlobStar = remain.slice(1)
var gspref = prefix ? [ prefix ] : []
var noGlobStar = gspref.concat(remainWithoutGlobStar)
// the noGlobStar pattern exits the inGlobStar state
this._process(noGlobStar, index, false)
var len = entries.length
var isSym = this.symlinks[abs]
// If it's a symlink, and we're in a globstar, then stop
if (isSym && inGlobStar)
return
for (var i = 0; i < len; i++) {
var e = entries[i]
if (e.charAt(0) === '.' && !this.dot)
continue
// these two cases enter the inGlobStar state
var instead = gspref.concat(entries[i], remainWithoutGlobStar)
this._process(instead, index, true)
var below = gspref.concat(entries[i], remain)
this._process(below, index, true)
}
}
GlobSync.prototype._processSimple = function (prefix, index) {
// XXX review this. Shouldn't it be doing the mounting etc
// before doing stat? kinda weird?
var exists = this._stat(prefix)
if (!this.matches[index])
this.matches[index] = Object.create(null)
// If it doesn't exist, then just mark the lack of results
if (!exists)
return
if (prefix && isAbsolute(prefix) && !this.nomount) {
var trail = /[\/\\]$/.test(prefix)
if (prefix.charAt(0) === '/') {
prefix = path.join(this.root, prefix)
} else {
prefix = path.resolve(this.root, prefix)
if (trail)
prefix += '/'
}
}
if (process.platform === 'win32')
prefix = prefix.replace(/\\/g, '/')
// Mark this as a match
this.matches[index][prefix] = true
}
// Returns either 'DIR', 'FILE', or false
GlobSync.prototype._stat = function (f) {
var abs = this._makeAbs(f)
var needDir = f.slice(-1) === '/'
if (f.length > this.maxLength)
return false
if (!this.stat && ownProp(this.cache, abs)) {
var c = this.cache[abs]
if (Array.isArray(c))
c = 'DIR'
// It exists, but maybe not how we need it
if (!needDir || c === 'DIR')
return c
if (needDir && c === 'FILE')
return false
// otherwise we have to stat, because maybe c=true
// if we know it exists, but not what it is.
}
var exists
var stat = this.statCache[abs]
if (!stat) {
var lstat
try {
lstat = fs.lstatSync(abs)
} catch (er) {
return false
}
if (lstat.isSymbolicLink()) {
try {
stat = fs.statSync(abs)
} catch (er) {
stat = lstat
}
} else {
stat = lstat
}
}
this.statCache[abs] = stat
var c = stat.isDirectory() ? 'DIR' : 'FILE'
this.cache[abs] = this.cache[abs] || c
if (needDir && c !== 'DIR')
return false
return c
}
GlobSync.prototype._mark = function (p) {
return common.mark(this, p)
}
GlobSync.prototype._makeAbs = function (f) {
return common.makeAbs(this, f)
}
...@@ -19,18 +19,43 @@ ...@@ -19,18 +19,43 @@
under the License. under the License.
*/ */
var args = process.argv,
run = require('./lib/run'); var args = process.argv;
var Api = require('./Api');
var nopt = require('nopt');
// Handle help flag // Handle help flag
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(args[2]) > -1) { if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
run.help(); require('./lib/run').help();
} else { process.exit(0);
run.run(args).done(function() { }
// Parse arguments (includes build params as well)
var opts = nopt({
'verbose' : Boolean,
'silent' : Boolean,
'debug': Boolean,
'release': Boolean,
'nobuild': Boolean,
'archs': String,
'list': Boolean,
'device': Boolean,
'emulator': Boolean,
'target' : String,
'codeSignIdentity': String,
'codeSignResourceRules': String,
'provisioningProfile': String,
'buildConfig' : String,
'noSign' : Boolean
}, {}, args);
// Make options compatible with PlatformApi build method spec
opts.argv = opts.argv.remain;
new Api().run(opts).done(function() {
console.log('** RUN SUCCEEDED **'); console.log('** RUN SUCCEEDED **');
}, function (err) { }, function (err) {
var errorMessage = (err && err.stack) ? err.stack : err; var errorMessage = (err && err.stack) ? err.stack : err;
console.error(errorMessage); console.error(errorMessage);
process.exit(2); process.exit(2);
}); });
}
\ No newline at end of file
...@@ -14,12 +14,6 @@ ...@@ -14,12 +14,6 @@
:: KIND, either express or implied. See the License for the :: KIND, either express or implied. See the License for the
:: specific language governing permissions and limitations :: specific language governing permissions and limitations
:: under the License :: under the License
@ECHO OFF @ECHO OFF
SET script_path="%~dp0run" ECHO WARN: The `run` is not available for cordova-ios on windows machines.>&2
IF EXIST %script_path% (
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'run' script in 'cordova' folder, aborting...>&2
EXIT /B 1
)
...@@ -25,6 +25,11 @@ ...@@ -25,6 +25,11 @@
Note: it does not work if the --shared option was used to create the project. Note: it does not work if the --shared option was used to create the project.
*/ */
var VERSION="3.9.2" // Coho updates this line
var VERSION="4.0.1"
console.log(VERSION); module.exports.version = VERSION;
if (!module.parent) {
console.log(VERSION);
}
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
}, },
"cordova-plugin-registerusernotificationsettings": { "cordova-plugin-app-event": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
}, },
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
...@@ -89,11 +89,5 @@ ...@@ -89,11 +89,5 @@
"plugin.notification.local.core" "plugin.notification.local.core"
] ]
} }
], ]
"plugin_metadata": {
"cordova-plugin-device": "1.1.0",
"cordova-plugin-x-toast": "2.3.1",
"cordova-plugin-registerusernotificationsettings": "1.0.2",
"de.appplant.cordova.plugin.local-notification": "0.8.3-dev"
}
} }
\ No newline at end of file
...@@ -19,55 +19,20 @@ ...@@ -19,55 +19,20 @@
* *
*/ */
/*global require, module, atob, document */
/** /**
* Creates a gap bridge iframe used to notify the native code about queued * Creates a gap bridge iframe used to notify the native code about queued
* commands. * commands.
*/ */
var cordova = require('cordova'), var cordova = require('cordova'),
channel = require('cordova/channel'),
utils = require('cordova/utils'), utils = require('cordova/utils'),
base64 = require('cordova/base64'), base64 = require('cordova/base64'),
// XHR mode does not work on iOS 4.2.
// XHR mode's main advantage is working around a bug in -webkit-scroll, which
// doesn't exist only on iOS 5.x devices.
// IFRAME_NAV is the fastest.
// IFRAME_HASH could be made to enable synchronous bridge calls if we wanted this feature.
jsToNativeModes = {
IFRAME_NAV: 0, // Default. Uses a new iframe for each poke.
// XHR bridge appears to be flaky sometimes: CB-3900, CB-3359, CB-5457, CB-4970, CB-4998, CB-5134
XHR_NO_PAYLOAD: 1, // About the same speed as IFRAME_NAV. Performance not about the same as IFRAME_NAV, but more variable.
XHR_WITH_PAYLOAD: 2, // Flakey, and not as performant
XHR_OPTIONAL_PAYLOAD: 3, // Flakey, and not as performant
IFRAME_HASH_NO_PAYLOAD: 4, // Not fully baked. A bit faster than IFRAME_NAV, but risks jank since poke happens synchronously.
IFRAME_HASH_WITH_PAYLOAD: 5, // Slower than no payload. Maybe since it has to be URI encoded / decoded.
WK_WEBVIEW_BINDING: 6 // Only way that works for WKWebView :)
},
bridgeMode,
execIframe, execIframe,
execHashIframe,
hashToggle = 1,
execXhr,
requestCount = 0,
vcHeaderValue = null,
commandQueue = [], // Contains pending JS->Native messages. commandQueue = [], // Contains pending JS->Native messages.
isInContextOfEvalJs = 0, isInContextOfEvalJs = 0,
failSafeTimerId = 0; failSafeTimerId = 0;
function shouldBundleCommandJson() {
if (bridgeMode === jsToNativeModes.XHR_WITH_PAYLOAD) {
return true;
}
if (bridgeMode === jsToNativeModes.XHR_OPTIONAL_PAYLOAD) {
var payloadLength = 0;
for (var i = 0; i < commandQueue.length; ++i) {
payloadLength += commandQueue[i].length;
}
// The value here was determined using the benchmark within CordovaLibApp on an iPad 3.
return payloadLength < 4500;
}
return false;
}
function massageArgsJsToNative(args) { function massageArgsJsToNative(args) {
if (!args || utils.typeName(args) != 'Array') { if (!args || utils.typeName(args) != 'Array') {
return args; return args;
...@@ -118,17 +83,10 @@ function convertMessageToArgsNativeToJs(message) { ...@@ -118,17 +83,10 @@ function convertMessageToArgsNativeToJs(message) {
} }
function iOSExec() { function iOSExec() {
if (bridgeMode === undefined) {
bridgeMode = jsToNativeModes.IFRAME_NAV;
}
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) {
bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING;
}
var successCallback, failCallback, service, action, actionArgs, splitCommand; var successCallback, failCallback, service, action, actionArgs;
var callbackId = null; var callbackId = null;
if (typeof arguments[0] !== "string") { if (typeof arguments[0] !== 'string') {
// FORMAT ONE // FORMAT ONE
successCallback = arguments[0]; successCallback = arguments[0];
failCallback = arguments[1]; failCallback = arguments[1];
...@@ -142,18 +100,9 @@ function iOSExec() { ...@@ -142,18 +100,9 @@ function iOSExec() {
// an invalid callbackId and passes it even if no callbacks were given. // an invalid callbackId and passes it even if no callbacks were given.
callbackId = 'INVALID'; callbackId = 'INVALID';
} else { } else {
// FORMAT TWO, REMOVED throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
try { 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'
splitCommand = arguments[0].split("."); );
action = splitCommand.pop();
service = splitCommand.join(".");
actionArgs = Array.prototype.splice.call(arguments, 1);
console.log('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
"cordova.exec(null, null, \"" + service + "\", \"" + action + "\"," + JSON.stringify(actionArgs) + ");"
);
return;
} catch (e) {}
} }
// If actionArgs is not provided, default to an empty array // If actionArgs is not provided, default to an empty array
...@@ -175,116 +124,70 @@ function iOSExec() { ...@@ -175,116 +124,70 @@ function iOSExec() {
// effectively clone the command arguments in case they are mutated before // effectively clone the command arguments in case they are mutated before
// the command is executed. // the command is executed.
commandQueue.push(JSON.stringify(command)); commandQueue.push(JSON.stringify(command));
if (bridgeMode === jsToNativeModes.WK_WEBVIEW_BINDING) {
window.webkit.messageHandlers.cordova.postMessage(command);
} else {
// If we're in the context of a stringByEvaluatingJavaScriptFromString call,
// then the queue will be flushed when it returns; no need for a poke.
// Also, if there is already a command in the queue, then we've already
// poked the native side, so there is no reason to do so again.
if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNative();
}
}
}
function pokeNative() { // If we're in the context of a stringByEvaluatingJavaScriptFromString call,
switch (bridgeMode) { // then the queue will be flushed when it returns; no need for a poke.
case jsToNativeModes.XHR_NO_PAYLOAD: // Also, if there is already a command in the queue, then we've already
case jsToNativeModes.XHR_WITH_PAYLOAD: // poked the native side, so there is no reason to do so again.
case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNativeViaXhr(); pokeNative();
break;
default: // iframe-based.
pokeNativeViaIframe();
} }
} }
function pokeNativeViaXhr() { // CB-10106
// This prevents sending an XHR when there is already one being sent. function handleBridgeChange() {
// This should happen only in rare circumstances (refer to unit tests). if (execProxy !== cordovaExec()) {
if (execXhr && execXhr.readyState != 4) { var commandString = commandQueue.shift();
execXhr = null; while(commandString) {
} var command = JSON.parse(commandString);
// Re-using the XHR improves exec() performance by about 10%. var callbackId = command[0];
execXhr = execXhr || new XMLHttpRequest(); var service = command[1];
// Changing this to a GET will make the XHR reach the URIProtocol on 4.2. var action = command[2];
// For some reason it still doesn't work though... var actionArgs = command[3];
// Add a timestamp to the query param to prevent caching. var callbacks = cordova.callbacks[callbackId] || {};
execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);
if (!vcHeaderValue) { execProxy(callbacks.success, callbacks.fail, service, action, actionArgs);
vcHeaderValue = /.*\((.*)\)$/.exec(navigator.userAgent)[1];
} commandString = commandQueue.shift();
execXhr.setRequestHeader('vc', vcHeaderValue); };
execXhr.setRequestHeader('rc', ++requestCount); return true;
if (shouldBundleCommandJson()) {
execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());
} }
execXhr.send(null);
return false;
} }
function pokeNativeViaIframe() { function pokeNative() {
// CB-5488 - Don't attempt to create iframe before document.body is available. // CB-5488 - Don't attempt to create iframe before document.body is available.
if (!document.body) { if (!document.body) {
setTimeout(pokeNativeViaIframe); setTimeout(pokeNative);
return; return;
} }
if (bridgeMode === jsToNativeModes.IFRAME_HASH_NO_PAYLOAD || bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
// TODO: This bridge mode doesn't properly support being removed from the DOM (CB-7735) // Check if they've removed it from the DOM, and put it back if so.
if (!execHashIframe) { if (execIframe && execIframe.contentWindow) {
execHashIframe = document.createElement('iframe'); execIframe.contentWindow.location = 'gap://ready';
execHashIframe.style.display = 'none';
document.body.appendChild(execHashIframe);
// Hash changes don't work on about:blank, so switch it to file:///.
execHashIframe.contentWindow.history.replaceState(null, null, 'file:///#');
}
// The delegate method is called only when the hash changes, so toggle it back and forth.
hashToggle = hashToggle ^ 3;
var hashValue = '%0' + hashToggle;
if (bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
hashValue += iOSExec.nativeFetchMessages();
}
execHashIframe.contentWindow.location.hash = hashValue;
} else { } else {
// Check if they've removed it from the DOM, and put it back if so. execIframe = document.createElement('iframe');
if (execIframe && execIframe.contentWindow) { execIframe.style.display = 'none';
execIframe.contentWindow.location = 'gap://ready'; execIframe.src = 'gap://ready';
} else { document.body.appendChild(execIframe);
execIframe = document.createElement('iframe'); }
execIframe.style.display = 'none'; // Use a timer to protect against iframe being unloaded during the poke (CB-7735).
execIframe.src = 'gap://ready'; // This makes the bridge ~ 7% slower, but works around the poke getting lost
document.body.appendChild(execIframe); // when the iframe is removed from the DOM.
} // An onunload listener could be used in the case where the iframe has just been
// Use a timer to protect against iframe being unloaded during the poke (CB-7735). // created, but since unload events fire only once, it doesn't work in the normal
// This makes the bridge ~ 7% slower, but works around the poke getting lost // case of iframe reuse (where unload will have already fired due to the attempted
// when the iframe is removed from the DOM. // navigation of the page).
// An onunload listener could be used in the case where the iframe has just been failSafeTimerId = setTimeout(function() {
// created, but since unload events fire only once, it doesn't work in the normal if (commandQueue.length) {
// case of iframe reuse (where unload will have already fired due to the attempted // CB-10106 - flush the queue on bridge change
// navigation of the page). if (!handleBridgeChange()) {
failSafeTimerId = setTimeout(function() {
if (commandQueue.length) {
pokeNative(); pokeNative();
} }
}, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
}
}
iOSExec.jsToNativeModes = jsToNativeModes;
iOSExec.setJsToNativeBridgeMode = function(mode) {
// Remove the iFrame since it may be no longer required, and its existence
// can trigger browser bugs.
// https://issues.apache.org/jira/browse/CB-593
if (execIframe) {
if (execIframe.parentNode) {
execIframe.parentNode.removeChild(execIframe);
} }
execIframe = null; }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
} }
bridgeMode = mode;
};
iOSExec.nativeFetchMessages = function() { iOSExec.nativeFetchMessages = function() {
// Stop listing for window detatch once native side confirms poke. // Stop listing for window detatch once native side confirms poke.
...@@ -301,11 +204,14 @@ iOSExec.nativeFetchMessages = function() { ...@@ -301,11 +204,14 @@ iOSExec.nativeFetchMessages = function() {
return json; return json;
}; };
iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) { iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) {
return iOSExec.nativeEvalAndFetch(function() { return iOSExec.nativeEvalAndFetch(function() {
var success = status === 0 || status === 1; var success = status === 0 || status === 1;
var args = convertMessageToArgsNativeToJs(message); var args = convertMessageToArgsNativeToJs(message);
cordova.callbackFromNative(callbackId, success, status, args, keepCallback); function nc2() {
cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
}
setTimeout(nc2, 0);
}); });
}; };
...@@ -320,4 +226,28 @@ iOSExec.nativeEvalAndFetch = function(func) { ...@@ -320,4 +226,28 @@ iOSExec.nativeEvalAndFetch = function(func) {
} }
}; };
module.exports = iOSExec; // Proxy the exec for bridge changes. See CB-10106
function cordovaExec() {
var cexec = require('cordova/exec');
var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function');
return (cexec_valid && execProxy !== cexec)? cexec : iOSExec;
}
function execProxy() {
cordovaExec().apply(null, arguments);
};
execProxy.nativeFetchMessages = function() {
return cordovaExec().nativeFetchMessages.apply(null, arguments);
};
execProxy.nativeEvalAndFetch = function() {
return cordovaExec().nativeEvalAndFetch.apply(null, arguments);
};
execProxy.nativeCallback = function() {
return cordovaExec().nativeCallback.apply(null, arguments);
};
module.exports = execProxy;
// Platform: ios // Platform: ios
// 49a8db57fa070d20ea7b304a53ffec3d7250c5af // ded62dda172755defaf75378ed007dc05730ec22
/* /*
Licensed to the Apache Software Foundation (ASF) under one Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file or more contributor license agreements. See the NOTICE file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
under the License. under the License.
*/ */
;(function() { ;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '3.9.2'; var PLATFORM_VERSION_BUILD_LABEL = '4.0.1';
// file: src/scripts/require.js // file: src/scripts/require.js
/*jshint -W079 */ /*jshint -W079 */
...@@ -817,58 +817,23 @@ module.exports = channel; ...@@ -817,58 +817,23 @@ module.exports = channel;
}); });
// file: e:/cordova/cordova-ios/cordova-js-src/exec.js // file: /Users/shaz/Documents/Git/Apache/cordova-ios/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) { define("cordova/exec", function(require, exports, module) {
/*global require, module, atob, document */
/** /**
* Creates a gap bridge iframe used to notify the native code about queued * Creates a gap bridge iframe used to notify the native code about queued
* commands. * commands.
*/ */
var cordova = require('cordova'), var cordova = require('cordova'),
channel = require('cordova/channel'),
utils = require('cordova/utils'), utils = require('cordova/utils'),
base64 = require('cordova/base64'), base64 = require('cordova/base64'),
// XHR mode does not work on iOS 4.2.
// XHR mode's main advantage is working around a bug in -webkit-scroll, which
// doesn't exist only on iOS 5.x devices.
// IFRAME_NAV is the fastest.
// IFRAME_HASH could be made to enable synchronous bridge calls if we wanted this feature.
jsToNativeModes = {
IFRAME_NAV: 0, // Default. Uses a new iframe for each poke.
// XHR bridge appears to be flaky sometimes: CB-3900, CB-3359, CB-5457, CB-4970, CB-4998, CB-5134
XHR_NO_PAYLOAD: 1, // About the same speed as IFRAME_NAV. Performance not about the same as IFRAME_NAV, but more variable.
XHR_WITH_PAYLOAD: 2, // Flakey, and not as performant
XHR_OPTIONAL_PAYLOAD: 3, // Flakey, and not as performant
IFRAME_HASH_NO_PAYLOAD: 4, // Not fully baked. A bit faster than IFRAME_NAV, but risks jank since poke happens synchronously.
IFRAME_HASH_WITH_PAYLOAD: 5, // Slower than no payload. Maybe since it has to be URI encoded / decoded.
WK_WEBVIEW_BINDING: 6 // Only way that works for WKWebView :)
},
bridgeMode,
execIframe, execIframe,
execHashIframe,
hashToggle = 1,
execXhr,
requestCount = 0,
vcHeaderValue = null,
commandQueue = [], // Contains pending JS->Native messages. commandQueue = [], // Contains pending JS->Native messages.
isInContextOfEvalJs = 0, isInContextOfEvalJs = 0,
failSafeTimerId = 0; failSafeTimerId = 0;
function shouldBundleCommandJson() {
if (bridgeMode === jsToNativeModes.XHR_WITH_PAYLOAD) {
return true;
}
if (bridgeMode === jsToNativeModes.XHR_OPTIONAL_PAYLOAD) {
var payloadLength = 0;
for (var i = 0; i < commandQueue.length; ++i) {
payloadLength += commandQueue[i].length;
}
// The value here was determined using the benchmark within CordovaLibApp on an iPad 3.
return payloadLength < 4500;
}
return false;
}
function massageArgsJsToNative(args) { function massageArgsJsToNative(args) {
if (!args || utils.typeName(args) != 'Array') { if (!args || utils.typeName(args) != 'Array') {
return args; return args;
...@@ -919,17 +884,10 @@ function convertMessageToArgsNativeToJs(message) { ...@@ -919,17 +884,10 @@ function convertMessageToArgsNativeToJs(message) {
} }
function iOSExec() { function iOSExec() {
if (bridgeMode === undefined) {
bridgeMode = jsToNativeModes.IFRAME_NAV;
}
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { var successCallback, failCallback, service, action, actionArgs;
bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING;
}
var successCallback, failCallback, service, action, actionArgs, splitCommand;
var callbackId = null; var callbackId = null;
if (typeof arguments[0] !== "string") { if (typeof arguments[0] !== 'string') {
// FORMAT ONE // FORMAT ONE
successCallback = arguments[0]; successCallback = arguments[0];
failCallback = arguments[1]; failCallback = arguments[1];
...@@ -943,18 +901,9 @@ function iOSExec() { ...@@ -943,18 +901,9 @@ function iOSExec() {
// an invalid callbackId and passes it even if no callbacks were given. // an invalid callbackId and passes it even if no callbacks were given.
callbackId = 'INVALID'; callbackId = 'INVALID';
} else { } else {
// FORMAT TWO, REMOVED throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
try { 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'
splitCommand = arguments[0].split("."); );
action = splitCommand.pop();
service = splitCommand.join(".");
actionArgs = Array.prototype.splice.call(arguments, 1);
console.log('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
"cordova.exec(null, null, \"" + service + "\", \"" + action + "\"," + JSON.stringify(actionArgs) + ");"
);
return;
} catch (e) {}
} }
// If actionArgs is not provided, default to an empty array // If actionArgs is not provided, default to an empty array
...@@ -976,116 +925,70 @@ function iOSExec() { ...@@ -976,116 +925,70 @@ function iOSExec() {
// effectively clone the command arguments in case they are mutated before // effectively clone the command arguments in case they are mutated before
// the command is executed. // the command is executed.
commandQueue.push(JSON.stringify(command)); commandQueue.push(JSON.stringify(command));
if (bridgeMode === jsToNativeModes.WK_WEBVIEW_BINDING) {
window.webkit.messageHandlers.cordova.postMessage(command);
} else {
// If we're in the context of a stringByEvaluatingJavaScriptFromString call,
// then the queue will be flushed when it returns; no need for a poke.
// Also, if there is already a command in the queue, then we've already
// poked the native side, so there is no reason to do so again.
if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNative();
}
}
}
function pokeNative() { // If we're in the context of a stringByEvaluatingJavaScriptFromString call,
switch (bridgeMode) { // then the queue will be flushed when it returns; no need for a poke.
case jsToNativeModes.XHR_NO_PAYLOAD: // Also, if there is already a command in the queue, then we've already
case jsToNativeModes.XHR_WITH_PAYLOAD: // poked the native side, so there is no reason to do so again.
case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNativeViaXhr(); pokeNative();
break;
default: // iframe-based.
pokeNativeViaIframe();
} }
} }
function pokeNativeViaXhr() { // CB-10106
// This prevents sending an XHR when there is already one being sent. function handleBridgeChange() {
// This should happen only in rare circumstances (refer to unit tests). if (execProxy !== cordovaExec()) {
if (execXhr && execXhr.readyState != 4) { var commandString = commandQueue.shift();
execXhr = null; while(commandString) {
} var command = JSON.parse(commandString);
// Re-using the XHR improves exec() performance by about 10%. var callbackId = command[0];
execXhr = execXhr || new XMLHttpRequest(); var service = command[1];
// Changing this to a GET will make the XHR reach the URIProtocol on 4.2. var action = command[2];
// For some reason it still doesn't work though... var actionArgs = command[3];
// Add a timestamp to the query param to prevent caching. var callbacks = cordova.callbacks[callbackId] || {};
execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);
if (!vcHeaderValue) { execProxy(callbacks.success, callbacks.fail, service, action, actionArgs);
vcHeaderValue = /.*\((.*)\)$/.exec(navigator.userAgent)[1];
} commandString = commandQueue.shift();
execXhr.setRequestHeader('vc', vcHeaderValue); };
execXhr.setRequestHeader('rc', ++requestCount); return true;
if (shouldBundleCommandJson()) { }
execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());
} return false;
execXhr.send(null);
} }
function pokeNativeViaIframe() { function pokeNative() {
// CB-5488 - Don't attempt to create iframe before document.body is available. // CB-5488 - Don't attempt to create iframe before document.body is available.
if (!document.body) { if (!document.body) {
setTimeout(pokeNativeViaIframe); setTimeout(pokeNative);
return; return;
} }
if (bridgeMode === jsToNativeModes.IFRAME_HASH_NO_PAYLOAD || bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
// TODO: This bridge mode doesn't properly support being removed from the DOM (CB-7735) // Check if they've removed it from the DOM, and put it back if so.
if (!execHashIframe) { if (execIframe && execIframe.contentWindow) {
execHashIframe = document.createElement('iframe'); execIframe.contentWindow.location = 'gap://ready';
execHashIframe.style.display = 'none';
document.body.appendChild(execHashIframe);
// Hash changes don't work on about:blank, so switch it to file:///.
execHashIframe.contentWindow.history.replaceState(null, null, 'file:///#');
}
// The delegate method is called only when the hash changes, so toggle it back and forth.
hashToggle = hashToggle ^ 3;
var hashValue = '%0' + hashToggle;
if (bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
hashValue += iOSExec.nativeFetchMessages();
}
execHashIframe.contentWindow.location.hash = hashValue;
} else { } else {
// Check if they've removed it from the DOM, and put it back if so. execIframe = document.createElement('iframe');
if (execIframe && execIframe.contentWindow) { execIframe.style.display = 'none';
execIframe.contentWindow.location = 'gap://ready'; execIframe.src = 'gap://ready';
} else { document.body.appendChild(execIframe);
execIframe = document.createElement('iframe'); }
execIframe.style.display = 'none'; // Use a timer to protect against iframe being unloaded during the poke (CB-7735).
execIframe.src = 'gap://ready'; // This makes the bridge ~ 7% slower, but works around the poke getting lost
document.body.appendChild(execIframe); // when the iframe is removed from the DOM.
} // An onunload listener could be used in the case where the iframe has just been
// Use a timer to protect against iframe being unloaded during the poke (CB-7735). // created, but since unload events fire only once, it doesn't work in the normal
// This makes the bridge ~ 7% slower, but works around the poke getting lost // case of iframe reuse (where unload will have already fired due to the attempted
// when the iframe is removed from the DOM. // navigation of the page).
// An onunload listener could be used in the case where the iframe has just been failSafeTimerId = setTimeout(function() {
// created, but since unload events fire only once, it doesn't work in the normal if (commandQueue.length) {
// case of iframe reuse (where unload will have already fired due to the attempted // CB-10106 - flush the queue on bridge change
// navigation of the page). if (!handleBridgeChange()) {
failSafeTimerId = setTimeout(function() {
if (commandQueue.length) {
pokeNative(); pokeNative();
} }
}, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
}
}
iOSExec.jsToNativeModes = jsToNativeModes;
iOSExec.setJsToNativeBridgeMode = function(mode) {
// Remove the iFrame since it may be no longer required, and its existence
// can trigger browser bugs.
// https://issues.apache.org/jira/browse/CB-593
if (execIframe) {
if (execIframe.parentNode) {
execIframe.parentNode.removeChild(execIframe);
} }
execIframe = null; }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
} }
bridgeMode = mode;
};
iOSExec.nativeFetchMessages = function() { iOSExec.nativeFetchMessages = function() {
// Stop listing for window detatch once native side confirms poke. // Stop listing for window detatch once native side confirms poke.
...@@ -1102,11 +1005,14 @@ iOSExec.nativeFetchMessages = function() { ...@@ -1102,11 +1005,14 @@ iOSExec.nativeFetchMessages = function() {
return json; return json;
}; };
iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) { iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) {
return iOSExec.nativeEvalAndFetch(function() { return iOSExec.nativeEvalAndFetch(function() {
var success = status === 0 || status === 1; var success = status === 0 || status === 1;
var args = convertMessageToArgsNativeToJs(message); var args = convertMessageToArgsNativeToJs(message);
cordova.callbackFromNative(callbackId, success, status, args, keepCallback); function nc2() {
cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
}
setTimeout(nc2, 0);
}); });
}; };
...@@ -1121,7 +1027,31 @@ iOSExec.nativeEvalAndFetch = function(func) { ...@@ -1121,7 +1027,31 @@ iOSExec.nativeEvalAndFetch = function(func) {
} }
}; };
module.exports = iOSExec; // Proxy the exec for bridge changes. See CB-10106
function cordovaExec() {
var cexec = require('cordova/exec');
var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function');
return (cexec_valid && execProxy !== cexec)? cexec : iOSExec;
}
function execProxy() {
cordovaExec().apply(null, arguments);
};
execProxy.nativeFetchMessages = function() {
return cordovaExec().nativeFetchMessages.apply(null, arguments);
};
execProxy.nativeEvalAndFetch = function() {
return cordovaExec().nativeEvalAndFetch.apply(null, arguments);
};
execProxy.nativeCallback = function() {
return cordovaExec().nativeCallback.apply(null, arguments);
};
module.exports = execProxy;
}); });
...@@ -1606,7 +1536,7 @@ exports.reset(); ...@@ -1606,7 +1536,7 @@ exports.reset();
}); });
// file: e:/cordova/cordova-ios/cordova-js-src/platform.js // file: /Users/shaz/Documents/Git/Apache/cordova-ios/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) { define("cordova/platform", function(require, exports, module) {
module.exports = { module.exports = {
......
...@@ -51,11 +51,6 @@ module.exports = [ ...@@ -51,11 +51,6 @@ module.exports = [
]; ];
module.exports.metadata = module.exports.metadata =
// TOP OF METADATA // TOP OF METADATA
{ {}
"cordova-plugin-device": "1.1.0",
"cordova-plugin-x-toast": "2.3.1",
"cordova-plugin-registerusernotificationsettings": "1.0.2",
"de.appplant.cordova.plugin.local-notification": "0.8.3-dev"
}
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });
\ No newline at end of file
cordova.define("cordova-plugin-device.device", function(require, exports, module) { /* cordova.define("cordova-plugin-device.device", function(require, exports, module) {
/*
* *
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
......
cordova.define("cordova-plugin-x-toast.tests", function(require, exports, module) { exports.defineAutoTests = function() { cordova.define("cordova-plugin-x-toast.tests", function(require, exports, module) {
exports.defineAutoTests = function() {
var fail = function (done) { var fail = function (done) {
expect(true).toBe(false); expect(true).toBe(false);
......
cordova.define("cordova-plugin-x-toast.Toast", function(require, exports, module) { function Toast() { cordova.define("cordova-plugin-x-toast.Toast", function(require, exports, module) {
function Toast() {
} }
Toast.prototype.optionsBuilder = function () { Toast.prototype.optionsBuilder = function () {
......
cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Core", function(require, exports, module) { /* cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Core", function(require, exports, module) {
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved. * Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
* *
* @APPPLANT_LICENSE_HEADER_START@ * @APPPLANT_LICENSE_HEADER_START@
...@@ -55,52 +56,74 @@ exports.setDefaults = function (newDefaults) { ...@@ -55,52 +56,74 @@ exports.setDefaults = function (newDefaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} msgs
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (msgs, callback, scope, args) {
this.registerPermission(function(granted) { var fn = function(granted) {
if (!granted) if (!granted) return;
return;
var notifications = Array.isArray(opts) ? opts : [opts]; var notifications = Array.isArray(msgs) ? msgs : [msgs];
for (var i = 0; i < notifications.length; i++) { for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i]; var notification = notifications[i];
this.mergeWithDefaults(properties); this.mergeWithDefaults(notification);
this.convertProperties(properties); this.convertProperties(notification);
} }
this.exec('schedule', notifications, callback, scope); this.exec('schedule', notifications, callback, scope);
}, this); };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (msgs, callback, scope, args) {
var notifications = Array.isArray(opts) ? opts : [opts]; var fn = function(granted) {
for (var i = 0; i < notifications.length; i++) { if (!granted) return;
var properties = notifications[i];
this.convertProperties(properties); var notifications = Array.isArray(msgs) ? msgs : [msgs];
}
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
this.convertProperties(notification);
}
this.exec('update', notifications, callback, scope);
};
this.exec('update', notifications, callback, scope); if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
...@@ -414,6 +437,13 @@ exports.hasPermission = function (callback, scope) { ...@@ -414,6 +437,13 @@ exports.hasPermission = function (callback, scope) {
* The callback function's scope * The callback function's scope
*/ */
exports.registerPermission = function (callback, scope) { exports.registerPermission = function (callback, scope) {
if (this._registered) {
return this.hasPermission(callback, scope);
} else {
this._registered = true;
}
var fn = this.createCallbackFn(callback, scope); var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS') { if (device.platform != 'iOS') {
......
cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Util", function(require, exports, module) { /* cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Util", function(require, exports, module) {
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved. * Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
* *
* @APPPLANT_LICENSE_HEADER_START@ * @APPPLANT_LICENSE_HEADER_START@
...@@ -44,6 +45,9 @@ exports._defaults = { ...@@ -44,6 +45,9 @@ exports._defaults = {
// listener // listener
exports._listener = {}; exports._listener = {};
// Registered permission flag
exports._registered = false;
/******** /********
* UTIL * * UTIL *
......
cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification", function(require, exports, module) { /* cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification", function(require, exports, module) {
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved. * Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
* *
* @APPPLANT_LICENSE_HEADER_START@ * @APPPLANT_LICENSE_HEADER_START@
...@@ -47,29 +48,35 @@ exports.setDefaults = function (defaults) { ...@@ -47,29 +48,35 @@ exports.setDefaults = function (defaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} notifications
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (notifications, callback, scope, args) {
this.core.schedule(opts, callback, scope); this.core.schedule(notifications, callback, scope, args);
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (notifications, callback, scope, args) {
this.core.update(opts, callback, scope); this.core.update(notifications, callback, scope, args);
}; };
/** /**
......
...@@ -19,55 +19,20 @@ ...@@ -19,55 +19,20 @@
* *
*/ */
/*global require, module, atob, document */
/** /**
* Creates a gap bridge iframe used to notify the native code about queued * Creates a gap bridge iframe used to notify the native code about queued
* commands. * commands.
*/ */
var cordova = require('cordova'), var cordova = require('cordova'),
channel = require('cordova/channel'),
utils = require('cordova/utils'), utils = require('cordova/utils'),
base64 = require('cordova/base64'), base64 = require('cordova/base64'),
// XHR mode does not work on iOS 4.2.
// XHR mode's main advantage is working around a bug in -webkit-scroll, which
// doesn't exist only on iOS 5.x devices.
// IFRAME_NAV is the fastest.
// IFRAME_HASH could be made to enable synchronous bridge calls if we wanted this feature.
jsToNativeModes = {
IFRAME_NAV: 0, // Default. Uses a new iframe for each poke.
// XHR bridge appears to be flaky sometimes: CB-3900, CB-3359, CB-5457, CB-4970, CB-4998, CB-5134
XHR_NO_PAYLOAD: 1, // About the same speed as IFRAME_NAV. Performance not about the same as IFRAME_NAV, but more variable.
XHR_WITH_PAYLOAD: 2, // Flakey, and not as performant
XHR_OPTIONAL_PAYLOAD: 3, // Flakey, and not as performant
IFRAME_HASH_NO_PAYLOAD: 4, // Not fully baked. A bit faster than IFRAME_NAV, but risks jank since poke happens synchronously.
IFRAME_HASH_WITH_PAYLOAD: 5, // Slower than no payload. Maybe since it has to be URI encoded / decoded.
WK_WEBVIEW_BINDING: 6 // Only way that works for WKWebView :)
},
bridgeMode,
execIframe, execIframe,
execHashIframe,
hashToggle = 1,
execXhr,
requestCount = 0,
vcHeaderValue = null,
commandQueue = [], // Contains pending JS->Native messages. commandQueue = [], // Contains pending JS->Native messages.
isInContextOfEvalJs = 0, isInContextOfEvalJs = 0,
failSafeTimerId = 0; failSafeTimerId = 0;
function shouldBundleCommandJson() {
if (bridgeMode === jsToNativeModes.XHR_WITH_PAYLOAD) {
return true;
}
if (bridgeMode === jsToNativeModes.XHR_OPTIONAL_PAYLOAD) {
var payloadLength = 0;
for (var i = 0; i < commandQueue.length; ++i) {
payloadLength += commandQueue[i].length;
}
// The value here was determined using the benchmark within CordovaLibApp on an iPad 3.
return payloadLength < 4500;
}
return false;
}
function massageArgsJsToNative(args) { function massageArgsJsToNative(args) {
if (!args || utils.typeName(args) != 'Array') { if (!args || utils.typeName(args) != 'Array') {
return args; return args;
...@@ -118,17 +83,10 @@ function convertMessageToArgsNativeToJs(message) { ...@@ -118,17 +83,10 @@ function convertMessageToArgsNativeToJs(message) {
} }
function iOSExec() { function iOSExec() {
if (bridgeMode === undefined) {
bridgeMode = jsToNativeModes.IFRAME_NAV;
}
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) {
bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING;
}
var successCallback, failCallback, service, action, actionArgs, splitCommand; var successCallback, failCallback, service, action, actionArgs;
var callbackId = null; var callbackId = null;
if (typeof arguments[0] !== "string") { if (typeof arguments[0] !== 'string') {
// FORMAT ONE // FORMAT ONE
successCallback = arguments[0]; successCallback = arguments[0];
failCallback = arguments[1]; failCallback = arguments[1];
...@@ -142,18 +100,9 @@ function iOSExec() { ...@@ -142,18 +100,9 @@ function iOSExec() {
// an invalid callbackId and passes it even if no callbacks were given. // an invalid callbackId and passes it even if no callbacks were given.
callbackId = 'INVALID'; callbackId = 'INVALID';
} else { } else {
// FORMAT TWO, REMOVED throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
try { 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'
splitCommand = arguments[0].split("."); );
action = splitCommand.pop();
service = splitCommand.join(".");
actionArgs = Array.prototype.splice.call(arguments, 1);
console.log('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
"cordova.exec(null, null, \"" + service + "\", \"" + action + "\"," + JSON.stringify(actionArgs) + ");"
);
return;
} catch (e) {}
} }
// If actionArgs is not provided, default to an empty array // If actionArgs is not provided, default to an empty array
...@@ -175,116 +124,70 @@ function iOSExec() { ...@@ -175,116 +124,70 @@ function iOSExec() {
// effectively clone the command arguments in case they are mutated before // effectively clone the command arguments in case they are mutated before
// the command is executed. // the command is executed.
commandQueue.push(JSON.stringify(command)); commandQueue.push(JSON.stringify(command));
if (bridgeMode === jsToNativeModes.WK_WEBVIEW_BINDING) {
window.webkit.messageHandlers.cordova.postMessage(command);
} else {
// If we're in the context of a stringByEvaluatingJavaScriptFromString call,
// then the queue will be flushed when it returns; no need for a poke.
// Also, if there is already a command in the queue, then we've already
// poked the native side, so there is no reason to do so again.
if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNative();
}
}
}
function pokeNative() { // If we're in the context of a stringByEvaluatingJavaScriptFromString call,
switch (bridgeMode) { // then the queue will be flushed when it returns; no need for a poke.
case jsToNativeModes.XHR_NO_PAYLOAD: // Also, if there is already a command in the queue, then we've already
case jsToNativeModes.XHR_WITH_PAYLOAD: // poked the native side, so there is no reason to do so again.
case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNativeViaXhr(); pokeNative();
break;
default: // iframe-based.
pokeNativeViaIframe();
} }
} }
function pokeNativeViaXhr() { // CB-10106
// This prevents sending an XHR when there is already one being sent. function handleBridgeChange() {
// This should happen only in rare circumstances (refer to unit tests). if (execProxy !== cordovaExec()) {
if (execXhr && execXhr.readyState != 4) { var commandString = commandQueue.shift();
execXhr = null; while(commandString) {
} var command = JSON.parse(commandString);
// Re-using the XHR improves exec() performance by about 10%. var callbackId = command[0];
execXhr = execXhr || new XMLHttpRequest(); var service = command[1];
// Changing this to a GET will make the XHR reach the URIProtocol on 4.2. var action = command[2];
// For some reason it still doesn't work though... var actionArgs = command[3];
// Add a timestamp to the query param to prevent caching. var callbacks = cordova.callbacks[callbackId] || {};
execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);
if (!vcHeaderValue) { execProxy(callbacks.success, callbacks.fail, service, action, actionArgs);
vcHeaderValue = /.*\((.*)\)$/.exec(navigator.userAgent)[1];
} commandString = commandQueue.shift();
execXhr.setRequestHeader('vc', vcHeaderValue); };
execXhr.setRequestHeader('rc', ++requestCount); return true;
if (shouldBundleCommandJson()) {
execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());
} }
execXhr.send(null);
return false;
} }
function pokeNativeViaIframe() { function pokeNative() {
// CB-5488 - Don't attempt to create iframe before document.body is available. // CB-5488 - Don't attempt to create iframe before document.body is available.
if (!document.body) { if (!document.body) {
setTimeout(pokeNativeViaIframe); setTimeout(pokeNative);
return; return;
} }
if (bridgeMode === jsToNativeModes.IFRAME_HASH_NO_PAYLOAD || bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
// TODO: This bridge mode doesn't properly support being removed from the DOM (CB-7735) // Check if they've removed it from the DOM, and put it back if so.
if (!execHashIframe) { if (execIframe && execIframe.contentWindow) {
execHashIframe = document.createElement('iframe'); execIframe.contentWindow.location = 'gap://ready';
execHashIframe.style.display = 'none';
document.body.appendChild(execHashIframe);
// Hash changes don't work on about:blank, so switch it to file:///.
execHashIframe.contentWindow.history.replaceState(null, null, 'file:///#');
}
// The delegate method is called only when the hash changes, so toggle it back and forth.
hashToggle = hashToggle ^ 3;
var hashValue = '%0' + hashToggle;
if (bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
hashValue += iOSExec.nativeFetchMessages();
}
execHashIframe.contentWindow.location.hash = hashValue;
} else { } else {
// Check if they've removed it from the DOM, and put it back if so. execIframe = document.createElement('iframe');
if (execIframe && execIframe.contentWindow) { execIframe.style.display = 'none';
execIframe.contentWindow.location = 'gap://ready'; execIframe.src = 'gap://ready';
} else { document.body.appendChild(execIframe);
execIframe = document.createElement('iframe'); }
execIframe.style.display = 'none'; // Use a timer to protect against iframe being unloaded during the poke (CB-7735).
execIframe.src = 'gap://ready'; // This makes the bridge ~ 7% slower, but works around the poke getting lost
document.body.appendChild(execIframe); // when the iframe is removed from the DOM.
} // An onunload listener could be used in the case where the iframe has just been
// Use a timer to protect against iframe being unloaded during the poke (CB-7735). // created, but since unload events fire only once, it doesn't work in the normal
// This makes the bridge ~ 7% slower, but works around the poke getting lost // case of iframe reuse (where unload will have already fired due to the attempted
// when the iframe is removed from the DOM. // navigation of the page).
// An onunload listener could be used in the case where the iframe has just been failSafeTimerId = setTimeout(function() {
// created, but since unload events fire only once, it doesn't work in the normal if (commandQueue.length) {
// case of iframe reuse (where unload will have already fired due to the attempted // CB-10106 - flush the queue on bridge change
// navigation of the page). if (!handleBridgeChange()) {
failSafeTimerId = setTimeout(function() {
if (commandQueue.length) {
pokeNative(); pokeNative();
} }
}, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
}
}
iOSExec.jsToNativeModes = jsToNativeModes;
iOSExec.setJsToNativeBridgeMode = function(mode) {
// Remove the iFrame since it may be no longer required, and its existence
// can trigger browser bugs.
// https://issues.apache.org/jira/browse/CB-593
if (execIframe) {
if (execIframe.parentNode) {
execIframe.parentNode.removeChild(execIframe);
} }
execIframe = null; }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
} }
bridgeMode = mode;
};
iOSExec.nativeFetchMessages = function() { iOSExec.nativeFetchMessages = function() {
// Stop listing for window detatch once native side confirms poke. // Stop listing for window detatch once native side confirms poke.
...@@ -301,11 +204,14 @@ iOSExec.nativeFetchMessages = function() { ...@@ -301,11 +204,14 @@ iOSExec.nativeFetchMessages = function() {
return json; return json;
}; };
iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) { iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) {
return iOSExec.nativeEvalAndFetch(function() { return iOSExec.nativeEvalAndFetch(function() {
var success = status === 0 || status === 1; var success = status === 0 || status === 1;
var args = convertMessageToArgsNativeToJs(message); var args = convertMessageToArgsNativeToJs(message);
cordova.callbackFromNative(callbackId, success, status, args, keepCallback); function nc2() {
cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
}
setTimeout(nc2, 0);
}); });
}; };
...@@ -320,4 +226,28 @@ iOSExec.nativeEvalAndFetch = function(func) { ...@@ -320,4 +226,28 @@ iOSExec.nativeEvalAndFetch = function(func) {
} }
}; };
module.exports = iOSExec; // Proxy the exec for bridge changes. See CB-10106
function cordovaExec() {
var cexec = require('cordova/exec');
var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function');
return (cexec_valid && execProxy !== cexec)? cexec : iOSExec;
}
function execProxy() {
cordovaExec().apply(null, arguments);
};
execProxy.nativeFetchMessages = function() {
return cordovaExec().nativeFetchMessages.apply(null, arguments);
};
execProxy.nativeEvalAndFetch = function() {
return cordovaExec().nativeEvalAndFetch.apply(null, arguments);
};
execProxy.nativeCallback = function() {
return cordovaExec().nativeCallback.apply(null, arguments);
};
module.exports = execProxy;
// Platform: ios // Platform: ios
// 49a8db57fa070d20ea7b304a53ffec3d7250c5af // ded62dda172755defaf75378ed007dc05730ec22
/* /*
Licensed to the Apache Software Foundation (ASF) under one Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file or more contributor license agreements. See the NOTICE file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
under the License. under the License.
*/ */
;(function() { ;(function() {
var PLATFORM_VERSION_BUILD_LABEL = '3.9.2'; var PLATFORM_VERSION_BUILD_LABEL = '4.0.1';
// file: src/scripts/require.js // file: src/scripts/require.js
/*jshint -W079 */ /*jshint -W079 */
...@@ -817,58 +817,23 @@ module.exports = channel; ...@@ -817,58 +817,23 @@ module.exports = channel;
}); });
// file: e:/cordova/cordova-ios/cordova-js-src/exec.js // file: /Users/shaz/Documents/Git/Apache/cordova-ios/cordova-js-src/exec.js
define("cordova/exec", function(require, exports, module) { define("cordova/exec", function(require, exports, module) {
/*global require, module, atob, document */
/** /**
* Creates a gap bridge iframe used to notify the native code about queued * Creates a gap bridge iframe used to notify the native code about queued
* commands. * commands.
*/ */
var cordova = require('cordova'), var cordova = require('cordova'),
channel = require('cordova/channel'),
utils = require('cordova/utils'), utils = require('cordova/utils'),
base64 = require('cordova/base64'), base64 = require('cordova/base64'),
// XHR mode does not work on iOS 4.2.
// XHR mode's main advantage is working around a bug in -webkit-scroll, which
// doesn't exist only on iOS 5.x devices.
// IFRAME_NAV is the fastest.
// IFRAME_HASH could be made to enable synchronous bridge calls if we wanted this feature.
jsToNativeModes = {
IFRAME_NAV: 0, // Default. Uses a new iframe for each poke.
// XHR bridge appears to be flaky sometimes: CB-3900, CB-3359, CB-5457, CB-4970, CB-4998, CB-5134
XHR_NO_PAYLOAD: 1, // About the same speed as IFRAME_NAV. Performance not about the same as IFRAME_NAV, but more variable.
XHR_WITH_PAYLOAD: 2, // Flakey, and not as performant
XHR_OPTIONAL_PAYLOAD: 3, // Flakey, and not as performant
IFRAME_HASH_NO_PAYLOAD: 4, // Not fully baked. A bit faster than IFRAME_NAV, but risks jank since poke happens synchronously.
IFRAME_HASH_WITH_PAYLOAD: 5, // Slower than no payload. Maybe since it has to be URI encoded / decoded.
WK_WEBVIEW_BINDING: 6 // Only way that works for WKWebView :)
},
bridgeMode,
execIframe, execIframe,
execHashIframe,
hashToggle = 1,
execXhr,
requestCount = 0,
vcHeaderValue = null,
commandQueue = [], // Contains pending JS->Native messages. commandQueue = [], // Contains pending JS->Native messages.
isInContextOfEvalJs = 0, isInContextOfEvalJs = 0,
failSafeTimerId = 0; failSafeTimerId = 0;
function shouldBundleCommandJson() {
if (bridgeMode === jsToNativeModes.XHR_WITH_PAYLOAD) {
return true;
}
if (bridgeMode === jsToNativeModes.XHR_OPTIONAL_PAYLOAD) {
var payloadLength = 0;
for (var i = 0; i < commandQueue.length; ++i) {
payloadLength += commandQueue[i].length;
}
// The value here was determined using the benchmark within CordovaLibApp on an iPad 3.
return payloadLength < 4500;
}
return false;
}
function massageArgsJsToNative(args) { function massageArgsJsToNative(args) {
if (!args || utils.typeName(args) != 'Array') { if (!args || utils.typeName(args) != 'Array') {
return args; return args;
...@@ -919,17 +884,10 @@ function convertMessageToArgsNativeToJs(message) { ...@@ -919,17 +884,10 @@ function convertMessageToArgsNativeToJs(message) {
} }
function iOSExec() { function iOSExec() {
if (bridgeMode === undefined) {
bridgeMode = jsToNativeModes.IFRAME_NAV;
}
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.cordova && window.webkit.messageHandlers.cordova.postMessage) { var successCallback, failCallback, service, action, actionArgs;
bridgeMode = jsToNativeModes.WK_WEBVIEW_BINDING;
}
var successCallback, failCallback, service, action, actionArgs, splitCommand;
var callbackId = null; var callbackId = null;
if (typeof arguments[0] !== "string") { if (typeof arguments[0] !== 'string') {
// FORMAT ONE // FORMAT ONE
successCallback = arguments[0]; successCallback = arguments[0];
failCallback = arguments[1]; failCallback = arguments[1];
...@@ -943,18 +901,9 @@ function iOSExec() { ...@@ -943,18 +901,9 @@ function iOSExec() {
// an invalid callbackId and passes it even if no callbacks were given. // an invalid callbackId and passes it even if no callbacks were given.
callbackId = 'INVALID'; callbackId = 'INVALID';
} else { } else {
// FORMAT TWO, REMOVED throw new Error('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
try { 'cordova.exec(null, null, \'Service\', \'action\', [ arg1, arg2 ]);'
splitCommand = arguments[0].split("."); );
action = splitCommand.pop();
service = splitCommand.join(".");
actionArgs = Array.prototype.splice.call(arguments, 1);
console.log('The old format of this exec call has been removed (deprecated since 2.1). Change to: ' +
"cordova.exec(null, null, \"" + service + "\", \"" + action + "\"," + JSON.stringify(actionArgs) + ");"
);
return;
} catch (e) {}
} }
// If actionArgs is not provided, default to an empty array // If actionArgs is not provided, default to an empty array
...@@ -976,116 +925,70 @@ function iOSExec() { ...@@ -976,116 +925,70 @@ function iOSExec() {
// effectively clone the command arguments in case they are mutated before // effectively clone the command arguments in case they are mutated before
// the command is executed. // the command is executed.
commandQueue.push(JSON.stringify(command)); commandQueue.push(JSON.stringify(command));
if (bridgeMode === jsToNativeModes.WK_WEBVIEW_BINDING) {
window.webkit.messageHandlers.cordova.postMessage(command);
} else {
// If we're in the context of a stringByEvaluatingJavaScriptFromString call,
// then the queue will be flushed when it returns; no need for a poke.
// Also, if there is already a command in the queue, then we've already
// poked the native side, so there is no reason to do so again.
if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNative();
}
}
}
function pokeNative() { // If we're in the context of a stringByEvaluatingJavaScriptFromString call,
switch (bridgeMode) { // then the queue will be flushed when it returns; no need for a poke.
case jsToNativeModes.XHR_NO_PAYLOAD: // Also, if there is already a command in the queue, then we've already
case jsToNativeModes.XHR_WITH_PAYLOAD: // poked the native side, so there is no reason to do so again.
case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: if (!isInContextOfEvalJs && commandQueue.length == 1) {
pokeNativeViaXhr(); pokeNative();
break;
default: // iframe-based.
pokeNativeViaIframe();
} }
} }
function pokeNativeViaXhr() { // CB-10106
// This prevents sending an XHR when there is already one being sent. function handleBridgeChange() {
// This should happen only in rare circumstances (refer to unit tests). if (execProxy !== cordovaExec()) {
if (execXhr && execXhr.readyState != 4) { var commandString = commandQueue.shift();
execXhr = null; while(commandString) {
} var command = JSON.parse(commandString);
// Re-using the XHR improves exec() performance by about 10%. var callbackId = command[0];
execXhr = execXhr || new XMLHttpRequest(); var service = command[1];
// Changing this to a GET will make the XHR reach the URIProtocol on 4.2. var action = command[2];
// For some reason it still doesn't work though... var actionArgs = command[3];
// Add a timestamp to the query param to prevent caching. var callbacks = cordova.callbacks[callbackId] || {};
execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);
if (!vcHeaderValue) { execProxy(callbacks.success, callbacks.fail, service, action, actionArgs);
vcHeaderValue = /.*\((.*)\)$/.exec(navigator.userAgent)[1];
} commandString = commandQueue.shift();
execXhr.setRequestHeader('vc', vcHeaderValue); };
execXhr.setRequestHeader('rc', ++requestCount); return true;
if (shouldBundleCommandJson()) { }
execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());
} return false;
execXhr.send(null);
} }
function pokeNativeViaIframe() { function pokeNative() {
// CB-5488 - Don't attempt to create iframe before document.body is available. // CB-5488 - Don't attempt to create iframe before document.body is available.
if (!document.body) { if (!document.body) {
setTimeout(pokeNativeViaIframe); setTimeout(pokeNative);
return; return;
} }
if (bridgeMode === jsToNativeModes.IFRAME_HASH_NO_PAYLOAD || bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
// TODO: This bridge mode doesn't properly support being removed from the DOM (CB-7735) // Check if they've removed it from the DOM, and put it back if so.
if (!execHashIframe) { if (execIframe && execIframe.contentWindow) {
execHashIframe = document.createElement('iframe'); execIframe.contentWindow.location = 'gap://ready';
execHashIframe.style.display = 'none';
document.body.appendChild(execHashIframe);
// Hash changes don't work on about:blank, so switch it to file:///.
execHashIframe.contentWindow.history.replaceState(null, null, 'file:///#');
}
// The delegate method is called only when the hash changes, so toggle it back and forth.
hashToggle = hashToggle ^ 3;
var hashValue = '%0' + hashToggle;
if (bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) {
hashValue += iOSExec.nativeFetchMessages();
}
execHashIframe.contentWindow.location.hash = hashValue;
} else { } else {
// Check if they've removed it from the DOM, and put it back if so. execIframe = document.createElement('iframe');
if (execIframe && execIframe.contentWindow) { execIframe.style.display = 'none';
execIframe.contentWindow.location = 'gap://ready'; execIframe.src = 'gap://ready';
} else { document.body.appendChild(execIframe);
execIframe = document.createElement('iframe'); }
execIframe.style.display = 'none'; // Use a timer to protect against iframe being unloaded during the poke (CB-7735).
execIframe.src = 'gap://ready'; // This makes the bridge ~ 7% slower, but works around the poke getting lost
document.body.appendChild(execIframe); // when the iframe is removed from the DOM.
} // An onunload listener could be used in the case where the iframe has just been
// Use a timer to protect against iframe being unloaded during the poke (CB-7735). // created, but since unload events fire only once, it doesn't work in the normal
// This makes the bridge ~ 7% slower, but works around the poke getting lost // case of iframe reuse (where unload will have already fired due to the attempted
// when the iframe is removed from the DOM. // navigation of the page).
// An onunload listener could be used in the case where the iframe has just been failSafeTimerId = setTimeout(function() {
// created, but since unload events fire only once, it doesn't work in the normal if (commandQueue.length) {
// case of iframe reuse (where unload will have already fired due to the attempted // CB-10106 - flush the queue on bridge change
// navigation of the page). if (!handleBridgeChange()) {
failSafeTimerId = setTimeout(function() {
if (commandQueue.length) {
pokeNative(); pokeNative();
} }
}, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
}
}
iOSExec.jsToNativeModes = jsToNativeModes;
iOSExec.setJsToNativeBridgeMode = function(mode) {
// Remove the iFrame since it may be no longer required, and its existence
// can trigger browser bugs.
// https://issues.apache.org/jira/browse/CB-593
if (execIframe) {
if (execIframe.parentNode) {
execIframe.parentNode.removeChild(execIframe);
} }
execIframe = null; }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire).
} }
bridgeMode = mode;
};
iOSExec.nativeFetchMessages = function() { iOSExec.nativeFetchMessages = function() {
// Stop listing for window detatch once native side confirms poke. // Stop listing for window detatch once native side confirms poke.
...@@ -1102,11 +1005,14 @@ iOSExec.nativeFetchMessages = function() { ...@@ -1102,11 +1005,14 @@ iOSExec.nativeFetchMessages = function() {
return json; return json;
}; };
iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) { iOSExec.nativeCallback = function(callbackId, status, message, keepCallback, debug) {
return iOSExec.nativeEvalAndFetch(function() { return iOSExec.nativeEvalAndFetch(function() {
var success = status === 0 || status === 1; var success = status === 0 || status === 1;
var args = convertMessageToArgsNativeToJs(message); var args = convertMessageToArgsNativeToJs(message);
cordova.callbackFromNative(callbackId, success, status, args, keepCallback); function nc2() {
cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
}
setTimeout(nc2, 0);
}); });
}; };
...@@ -1121,7 +1027,31 @@ iOSExec.nativeEvalAndFetch = function(func) { ...@@ -1121,7 +1027,31 @@ iOSExec.nativeEvalAndFetch = function(func) {
} }
}; };
module.exports = iOSExec; // Proxy the exec for bridge changes. See CB-10106
function cordovaExec() {
var cexec = require('cordova/exec');
var cexec_valid = (typeof cexec.nativeFetchMessages === 'function') && (typeof cexec.nativeEvalAndFetch === 'function') && (typeof cexec.nativeCallback === 'function');
return (cexec_valid && execProxy !== cexec)? cexec : iOSExec;
}
function execProxy() {
cordovaExec().apply(null, arguments);
};
execProxy.nativeFetchMessages = function() {
return cordovaExec().nativeFetchMessages.apply(null, arguments);
};
execProxy.nativeEvalAndFetch = function() {
return cordovaExec().nativeEvalAndFetch.apply(null, arguments);
};
execProxy.nativeCallback = function() {
return cordovaExec().nativeCallback.apply(null, arguments);
};
module.exports = execProxy;
}); });
...@@ -1606,7 +1536,7 @@ exports.reset(); ...@@ -1606,7 +1536,7 @@ exports.reset();
}); });
// file: e:/cordova/cordova-ios/cordova-js-src/platform.js // file: /Users/shaz/Documents/Git/Apache/cordova-ios/cordova-js-src/platform.js
define("cordova/platform", function(require, exports, module) { define("cordova/platform", function(require, exports, module) {
module.exports = { module.exports = {
......
...@@ -51,11 +51,6 @@ module.exports = [ ...@@ -51,11 +51,6 @@ module.exports = [
]; ];
module.exports.metadata = module.exports.metadata =
// TOP OF METADATA // TOP OF METADATA
{ {}
"cordova-plugin-device": "1.1.0",
"cordova-plugin-x-toast": "2.3.1",
"cordova-plugin-registerusernotificationsettings": "1.0.2",
"de.appplant.cordova.plugin.local-notification": "0.8.3-dev"
}
// BOTTOM OF METADATA // BOTTOM OF METADATA
}); });
\ No newline at end of file
cordova.define("cordova-plugin-device.device", function(require, exports, module) { /* cordova.define("cordova-plugin-device.device", function(require, exports, module) {
/*
* *
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
......
cordova.define("cordova-plugin-x-toast.tests", function(require, exports, module) { exports.defineAutoTests = function() { cordova.define("cordova-plugin-x-toast.tests", function(require, exports, module) {
exports.defineAutoTests = function() {
var fail = function (done) { var fail = function (done) {
expect(true).toBe(false); expect(true).toBe(false);
......
cordova.define("cordova-plugin-x-toast.Toast", function(require, exports, module) { function Toast() { cordova.define("cordova-plugin-x-toast.Toast", function(require, exports, module) {
function Toast() {
} }
Toast.prototype.optionsBuilder = function () { Toast.prototype.optionsBuilder = function () {
......
cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Core", function(require, exports, module) { /* cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Core", function(require, exports, module) {
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved. * Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
* *
* @APPPLANT_LICENSE_HEADER_START@ * @APPPLANT_LICENSE_HEADER_START@
...@@ -55,52 +56,74 @@ exports.setDefaults = function (newDefaults) { ...@@ -55,52 +56,74 @@ exports.setDefaults = function (newDefaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} msgs
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (msgs, callback, scope, args) {
this.registerPermission(function(granted) { var fn = function(granted) {
if (!granted) if (!granted) return;
return;
var notifications = Array.isArray(opts) ? opts : [opts]; var notifications = Array.isArray(msgs) ? msgs : [msgs];
for (var i = 0; i < notifications.length; i++) { for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i]; var notification = notifications[i];
this.mergeWithDefaults(properties); this.mergeWithDefaults(notification);
this.convertProperties(properties); this.convertProperties(notification);
} }
this.exec('schedule', notifications, callback, scope); this.exec('schedule', notifications, callback, scope);
}, this); };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (msgs, callback, scope, args) {
var notifications = Array.isArray(opts) ? opts : [opts]; var fn = function(granted) {
for (var i = 0; i < notifications.length; i++) { if (!granted) return;
var properties = notifications[i];
this.convertProperties(properties); var notifications = Array.isArray(msgs) ? msgs : [msgs];
}
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
this.convertProperties(notification);
}
this.exec('update', notifications, callback, scope);
};
this.exec('update', notifications, callback, scope); if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
...@@ -414,6 +437,13 @@ exports.hasPermission = function (callback, scope) { ...@@ -414,6 +437,13 @@ exports.hasPermission = function (callback, scope) {
* The callback function's scope * The callback function's scope
*/ */
exports.registerPermission = function (callback, scope) { exports.registerPermission = function (callback, scope) {
if (this._registered) {
return this.hasPermission(callback, scope);
} else {
this._registered = true;
}
var fn = this.createCallbackFn(callback, scope); var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS') { if (device.platform != 'iOS') {
......
cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Util", function(require, exports, module) { /* cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification.Util", function(require, exports, module) {
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved. * Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
* *
* @APPPLANT_LICENSE_HEADER_START@ * @APPPLANT_LICENSE_HEADER_START@
...@@ -44,6 +45,9 @@ exports._defaults = { ...@@ -44,6 +45,9 @@ exports._defaults = {
// listener // listener
exports._listener = {}; exports._listener = {};
// Registered permission flag
exports._registered = false;
/******** /********
* UTIL * * UTIL *
......
cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification", function(require, exports, module) { /* cordova.define("de.appplant.cordova.plugin.local-notification.LocalNotification", function(require, exports, module) {
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved. * Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
* *
* @APPPLANT_LICENSE_HEADER_START@ * @APPPLANT_LICENSE_HEADER_START@
...@@ -47,29 +48,35 @@ exports.setDefaults = function (defaults) { ...@@ -47,29 +48,35 @@ exports.setDefaults = function (defaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} notifications
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (notifications, callback, scope, args) {
this.core.schedule(opts, callback, scope); this.core.schedule(notifications, callback, scope, args);
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (notifications, callback, scope, args) {
this.core.update(opts, callback, scope); this.core.update(notifications, callback, scope, args);
}; };
/** /**
......
{ {
"android": "latest", "android": "latest",
"ios": "3.9.2" "ios": "4.0.1"
} }
\ No newline at end of file
...@@ -86,7 +86,7 @@ module.exports.metadata = ...@@ -86,7 +86,7 @@ module.exports.metadata =
{ {
"cordova-plugin-device": "1.1.0", "cordova-plugin-device": "1.1.0",
"cordova-plugin-x-toast": "2.3.1", "cordova-plugin-x-toast": "2.3.1",
"cordova-plugin-registerusernotificationsettings": "1.0.2", "cordova-plugin-app-event": "1.1.0",
"de.appplant.cordova.plugin.local-notification": "0.8.3-dev" "de.appplant.cordova.plugin.local-notification": "0.8.3-dev"
} }
// BOTTOM OF METADATA // BOTTOM OF METADATA
......
...@@ -55,52 +55,74 @@ exports.setDefaults = function (newDefaults) { ...@@ -55,52 +55,74 @@ exports.setDefaults = function (newDefaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} msgs
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (msgs, callback, scope, args) {
this.registerPermission(function(granted) { var fn = function(granted) {
if (!granted) if (!granted) return;
return;
var notifications = Array.isArray(opts) ? opts : [opts]; var notifications = Array.isArray(msgs) ? msgs : [msgs];
for (var i = 0; i < notifications.length; i++) { for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i]; var notification = notifications[i];
this.mergeWithDefaults(properties); this.mergeWithDefaults(notification);
this.convertProperties(properties); this.convertProperties(notification);
} }
this.exec('schedule', notifications, callback, scope); this.exec('schedule', notifications, callback, scope);
}, this); };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (msgs, callback, scope, args) {
var notifications = Array.isArray(opts) ? opts : [opts]; var fn = function(granted) {
for (var i = 0; i < notifications.length; i++) { if (!granted) return;
var properties = notifications[i];
this.convertProperties(properties); var notifications = Array.isArray(msgs) ? msgs : [msgs];
}
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
this.convertProperties(notification);
}
this.exec('update', notifications, callback, scope);
};
this.exec('update', notifications, callback, scope); if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
...@@ -414,6 +436,13 @@ exports.hasPermission = function (callback, scope) { ...@@ -414,6 +436,13 @@ exports.hasPermission = function (callback, scope) {
* The callback function's scope * The callback function's scope
*/ */
exports.registerPermission = function (callback, scope) { exports.registerPermission = function (callback, scope) {
if (this._registered) {
return this.hasPermission(callback, scope);
} else {
this._registered = true;
}
var fn = this.createCallbackFn(callback, scope); var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS') { if (device.platform != 'iOS') {
......
...@@ -44,6 +44,9 @@ exports._defaults = { ...@@ -44,6 +44,9 @@ exports._defaults = {
// listener // listener
exports._listener = {}; exports._listener = {};
// Registered permission flag
exports._registered = false;
/******** /********
* UTIL * * UTIL *
......
...@@ -47,29 +47,35 @@ exports.setDefaults = function (defaults) { ...@@ -47,29 +47,35 @@ exports.setDefaults = function (defaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} notifications
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (notifications, callback, scope, args) {
this.core.schedule(opts, callback, scope); this.core.schedule(notifications, callback, scope, args);
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (notifications, callback, scope, args) {
this.core.update(opts, callback, scope); this.core.update(notifications, callback, scope, args);
}; };
/** /**
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"PACKAGE_NAME": "NotificationExample" "PACKAGE_NAME": "NotificationExample"
}, },
"cordova-plugin-registerusernotificationsettings": { "cordova-plugin-app-event": {
"PACKAGE_NAME": "NotificationExample" "PACKAGE_NAME": "NotificationExample"
}, },
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
"plugin_metadata": { "plugin_metadata": {
"cordova-plugin-device": "1.1.0", "cordova-plugin-device": "1.1.0",
"cordova-plugin-x-toast": "2.3.1", "cordova-plugin-x-toast": "2.3.1",
"cordova-plugin-registerusernotificationsettings": "1.0.2", "cordova-plugin-app-event": "1.1.0",
"de.appplant.cordova.plugin.local-notification": "0.8.3-dev" "de.appplant.cordova.plugin.local-notification": "0.8.3-dev"
} }
} }
\ No newline at end of file
...@@ -86,7 +86,7 @@ module.exports.metadata = ...@@ -86,7 +86,7 @@ module.exports.metadata =
{ {
"cordova-plugin-device": "1.1.0", "cordova-plugin-device": "1.1.0",
"cordova-plugin-x-toast": "2.3.1", "cordova-plugin-x-toast": "2.3.1",
"cordova-plugin-registerusernotificationsettings": "1.0.2", "cordova-plugin-app-event": "1.1.0",
"de.appplant.cordova.plugin.local-notification": "0.8.3-dev" "de.appplant.cordova.plugin.local-notification": "0.8.3-dev"
} }
// BOTTOM OF METADATA // BOTTOM OF METADATA
......
...@@ -55,52 +55,74 @@ exports.setDefaults = function (newDefaults) { ...@@ -55,52 +55,74 @@ exports.setDefaults = function (newDefaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} msgs
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (msgs, callback, scope, args) {
this.registerPermission(function(granted) { var fn = function(granted) {
if (!granted) if (!granted) return;
return;
var notifications = Array.isArray(opts) ? opts : [opts]; var notifications = Array.isArray(msgs) ? msgs : [msgs];
for (var i = 0; i < notifications.length; i++) { for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i]; var notification = notifications[i];
this.mergeWithDefaults(properties); this.mergeWithDefaults(notification);
this.convertProperties(properties); this.convertProperties(notification);
} }
this.exec('schedule', notifications, callback, scope); this.exec('schedule', notifications, callback, scope);
}, this); };
if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (msgs, callback, scope, args) {
var notifications = Array.isArray(opts) ? opts : [opts]; var fn = function(granted) {
for (var i = 0; i < notifications.length; i++) { if (!granted) return;
var properties = notifications[i];
this.convertProperties(properties); var notifications = Array.isArray(msgs) ? msgs : [msgs];
}
for (var i = 0; i < notifications.length; i++) {
var notification = notifications[i];
this.convertProperties(notification);
}
this.exec('update', notifications, callback, scope);
};
this.exec('update', notifications, callback, scope); if (args && args.skipPermission) {
fn.call(this, true);
} else {
this.registerPermission(fn, this);
}
}; };
/** /**
...@@ -414,6 +436,13 @@ exports.hasPermission = function (callback, scope) { ...@@ -414,6 +436,13 @@ exports.hasPermission = function (callback, scope) {
* The callback function's scope * The callback function's scope
*/ */
exports.registerPermission = function (callback, scope) { exports.registerPermission = function (callback, scope) {
if (this._registered) {
return this.hasPermission(callback, scope);
} else {
this._registered = true;
}
var fn = this.createCallbackFn(callback, scope); var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS') { if (device.platform != 'iOS') {
......
...@@ -44,6 +44,9 @@ exports._defaults = { ...@@ -44,6 +44,9 @@ exports._defaults = {
// listener // listener
exports._listener = {}; exports._listener = {};
// Registered permission flag
exports._registered = false;
/******** /********
* UTIL * * UTIL *
......
...@@ -47,29 +47,35 @@ exports.setDefaults = function (defaults) { ...@@ -47,29 +47,35 @@ exports.setDefaults = function (defaults) {
/** /**
* Schedule a new local notification. * Schedule a new local notification.
* *
* @param {Object} opts * @param {Object} notifications
* The notification properties * The notification properties
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been canceled * A function to be called after the notification has been canceled
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.schedule = function (opts, callback, scope) { exports.schedule = function (notifications, callback, scope, args) {
this.core.schedule(opts, callback, scope); this.core.schedule(notifications, callback, scope, args);
}; };
/** /**
* Update existing notifications specified by IDs in options. * Update existing notifications specified by IDs in options.
* *
* @param {Object} options * @param {Object} notifications
* The notification properties to update * The notification properties to update
* @param {Function} callback * @param {Function} callback
* A function to be called after the notification has been updated * A function to be called after the notification has been updated
* @param {Object?} scope * @param {Object?} scope
* The scope for the callback function * The scope for the callback function
* @param {Object?} args
* skipPermission:true schedules the notifications immediatly without
* registering or checking for permission
*/ */
exports.update = function (opts, callback, scope) { exports.update = function (notifications, callback, scope, args) {
this.core.update(opts, callback, scope); this.core.update(notifications, callback, scope, args);
}; };
/** /**
......
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
}, },
"cordova-plugin-app-event": {
"PACKAGE_NAME": "de.appplant.localnotification.example"
},
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
} }
}, },
"dependent_plugins": { "dependent_plugins": {}
"cordova-plugin-registerusernotificationsettings": {
"PACKAGE_NAME": "de.appplant.localnotification.example"
}
}
} }
\ No newline at end of file
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013-2015 appPlant UG, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
\ No newline at end of file
[![npm version](https://badge.fury.io/js/cordova-common-registerusernotificationsettings.svg)](http://badge.fury.io/js/cordova-plugin-registerusernotificationsettings)
[![PayPayl donate button](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=L3HKQCD9UA35A "Donate once-off to this project using Paypal")
Cordova RegisterUserNotificationSettings Plugin
===============================================
Implements didRegisterUserNotificationSettings and broadcasts the event for listening plugins.
```obj-c
#import "AppDelegate+APPRegisterUserNotificationSettings.h"
- (void) pluginInitialize
{
NSNotificationCenter* center = [NSNotificationCenter
defaultCenter];
[center addObserver:self
selector:@selector(didRegisterUserNotificationSettings:)
name:UIApplicationRegisterUserNotificationSettings
object:nil];
}
- (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
{
...
}
```
{
"name": "cordova-plugin-registerusernotificationsettings",
"version": "1.0.2",
"description": "Implements didRegisterUserNotificationSettings and broadcasts the event.",
"cordova": {
"id": "cordova-plugin-registerusernotificationsettings",
"platforms": [
"ios"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/katzer/cordova-common-registerusernotificationsettings.git"
},
"keywords": [
"appplant",
"ecosystem:cordova",
"cordova-ios"
],
"engines": [
{
"name": "cordova",
"version": ">=3.0.0"
}
],
"author": "Sebastián Katzer",
"license": "Apache 2.0",
"bugs": {
"url": "https://github.com/katzer/cordova-common-registerusernotificationsettings/issues"
},
"homepage": "https://github.com/katzer/cordova-common-registerusernotificationsettings#readme"
}
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-registerusernotificationsettings"
version="1.0.2">
<name>RegisterUserNotificationSettings</name>
<description>Implements didRegisterUserNotificationSettings and broadcasts the event.</description>
<repo>https://github.com/katzer/cordova-common-registerusernotificationsettings</repo>
<keywords>appplant</keywords>
<license>Apache 2.0</license>
<author>Sebastián Katzer</author>
<!-- cordova -->
<engines>
<engine name="cordova" version=">=3.0.0" />
</engines>
<!-- ios -->
<platform name="ios">
<header-file src="src/ios/AppDelegate+APPRegisterUserNotificationSettings.h" />
<source-file src="src/ios/AppDelegate+APPRegisterUserNotificationSettings.m" />
</platform>
</plugin>
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
*
* @APPPLANT_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPPLANT_LICENSE_HEADER_END@
*/
#import "AppDelegate.h"
#import <Availability.h>
extern NSString* const UIApplicationRegisterUserNotificationSettings;
@interface AppDelegate (APPRegisterUserNotificationSettings)
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
// Tells the delegate what types of notifications may be used
- (void) application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings;
#endif
@end
\ No newline at end of file
/*
* Copyright (c) 2013-2015 by appPlant UG. All rights reserved.
*
* @APPPLANT_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPPLANT_LICENSE_HEADER_END@
*/
#import "AppDelegate+APPRegisterUserNotificationSettings.h"
#import <Availability.h>
NSString* const UIApplicationRegisterUserNotificationSettings = @"UIApplicationRegisterUserNotificationSettings";
@implementation AppDelegate (APPRegisterUserNotificationSettings)
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
/**
* Tells the delegate what types of notifications may be used
* to get the user’s attention.
*/
- (void) application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
{
NSNotificationCenter* center = [NSNotificationCenter
defaultCenter];
// re-post (broadcast)
[center postNotificationName:UIApplicationRegisterUserNotificationSettings
object:settings];
}
#endif
@end
\ No newline at end of file
...@@ -7,12 +7,6 @@ ...@@ -7,12 +7,6 @@
"is_top_level": false, "is_top_level": false,
"variables": {} "variables": {}
}, },
"de.appplant.cordova.plugin.email-composer": {
"source": {
"type": "local",
"path": "../cordova-plugin-email-composer"
}
},
"de.appplant.cordova.common.RegisterUserNotificationSettings": { "de.appplant.cordova.common.RegisterUserNotificationSettings": {
"source": { "source": {
"type": "git", "type": "git",
...@@ -20,26 +14,6 @@ ...@@ -20,26 +14,6 @@
"subdir": "." "subdir": "."
} }
}, },
"de.appplant.cordova.plugin.badge": {
"source": {
"type": "registry",
"id": "de.appplant.cordova.plugin.badge"
}
},
"de.appplant.cordova.plugin.hidden-statusbar-overlay": {
"source": {
"type": "registry",
"id": "de.appplant.cordova.plugin.hidden-statusbar-overlay"
}
},
"cordova-plugin-android-support-v4": {
"source": {
"type": "registry",
"id": "cordova-plugin-android-support-v4"
},
"is_top_level": false,
"variables": {}
},
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"source": { "source": {
"type": "registry", "type": "registry",
...@@ -64,12 +38,12 @@ ...@@ -64,12 +38,12 @@
"is_top_level": false, "is_top_level": false,
"variables": {} "variables": {}
}, },
"de.appplant.cordova.common.registerusernotificationsettings": { "cordova-plugin-app-event": {
"source": { "source": {
"type": "registry", "type": "local",
"id": "de.appplant.cordova.common.registerusernotificationsettings" "path": "/Users/sebastian/Documents/github/cordova-plugin-app-event"
}, },
"is_top_level": false, "is_top_level": true,
"variables": {} "variables": {}
}, },
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
......
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
}, },
"cordova-plugin-app-event": {
"PACKAGE_NAME": "de.appplant.localnotification.example"
},
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
"PACKAGE_NAME": "de.appplant.localnotification.example" "PACKAGE_NAME": "de.appplant.localnotification.example"
} }
}, },
"dependent_plugins": { "dependent_plugins": {}
"cordova-plugin-registerusernotificationsettings": {
"PACKAGE_NAME": "de.appplant.localnotification.example"
}
}
} }
\ No newline at end of file
...@@ -13,13 +13,12 @@ ...@@ -13,13 +13,12 @@
"cordova-plugin-x-toast": { "cordova-plugin-x-toast": {
"PACKAGE_NAME": "NotificationExample" "PACKAGE_NAME": "NotificationExample"
}, },
"cordova-plugin-app-event": {
"PACKAGE_NAME": "NotificationExample"
},
"de.appplant.cordova.plugin.local-notification": { "de.appplant.cordova.plugin.local-notification": {
"PACKAGE_NAME": "NotificationExample" "PACKAGE_NAME": "NotificationExample"
} }
}, },
"dependent_plugins": { "dependent_plugins": {}
"cordova-plugin-registerusernotificationsettings": {
"PACKAGE_NAME": "NotificationExample"
}
}
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment