diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js index a9436108d..0370e69b2 100644 --- a/Examples/UIExplorer/BorderExample.js +++ b/Examples/UIExplorer/BorderExample.js @@ -26,12 +26,12 @@ var styles = StyleSheet.create({ }, border1: { borderWidth: 10, - borderColor: 'brown', + borderColor: '#a52a2a', }, borderRadius: { borderWidth: 10, borderRadius: 10, - borderColor: 'cyan', + borderColor: '#00ffff', }, border2: { borderWidth: 10, @@ -134,6 +134,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'border*Width & border*Color', + platform: 'ios', render() { return ; } @@ -141,6 +142,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'border*Width & border*Color', + platform: 'ios', render() { return ; } @@ -148,6 +150,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'borderRadius & clipping', + platform: 'ios', render() { return ( diff --git a/Examples/UIExplorer/ExampleTypes.js b/Examples/UIExplorer/ExampleTypes.js index 44db06a6c..ac9deaadf 100644 --- a/Examples/UIExplorer/ExampleTypes.js +++ b/Examples/UIExplorer/ExampleTypes.js @@ -14,11 +14,13 @@ * @providesModule ExampleTypes * @flow */ +'use strict'; export type Example = { title: string, render: () => ?ReactElement, description?: string, + platform?: string; }; export type ExampleModule = { diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/ImageExample.js index cdf664c43..cea7b5511 100644 --- a/Examples/UIExplorer/ImageExample.js +++ b/Examples/UIExplorer/ImageExample.js @@ -97,6 +97,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Image Download Progress', @@ -105,6 +106,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Border Color', @@ -122,6 +124,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Border Width', @@ -139,6 +142,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Border Radius', @@ -146,17 +150,12 @@ exports.examples = [ return ( ); @@ -245,19 +244,19 @@ exports.examples = [ @@ -266,19 +265,19 @@ exports.examples = [ @@ -336,6 +335,7 @@ exports.examples = [ render: function() { return ; }, + platform: 'ios', }, ]; diff --git a/Examples/UIExplorer/LayoutEventsExample.js b/Examples/UIExplorer/LayoutEventsExample.js index 3407be48a..4dec90f8f 100644 --- a/Examples/UIExplorer/LayoutEventsExample.js +++ b/Examples/UIExplorer/LayoutEventsExample.js @@ -85,8 +85,7 @@ var LayoutEventExample = React.createClass({ return ( - onLayout events are called on mount and whenever layout is updated, - including after layout animations complete.{' '} + layout events are called on mount and whenever layout is recalculated. Note that the layout event will typically be received before the layout has updated on screen, especially when using layout animations.{' '} Press here to change layout. @@ -136,6 +135,9 @@ var styles = StyleSheet.create({ pressText: { fontWeight: 'bold', }, + italicText: { + fontStyle: 'italic', + }, }); exports.title = 'Layout Events'; diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index fb20657a3..9ec430af5 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; + 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */; }; + 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */; }; 83A936C81B7E0F08005B9C36 /* RCTConvert_UIColorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -217,6 +219,9 @@ 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; + 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderTests.m; sourceTree = ""; }; + 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderHelpers.m; sourceTree = ""; }; + 8385CF051B8747A000C6273E /* RCTImageLoaderHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTImageLoaderHelpers.h; sourceTree = ""; }; 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_UIColorTests.m; sourceTree = ""; }; D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ @@ -365,6 +370,9 @@ 1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */, 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */, 1300627E1B59179B0043FE5A /* RCTGzipTests.m */, + 8385CF051B8747A000C6273E /* RCTImageLoaderHelpers.h */, + 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */, + 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */, 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */, 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */, 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */, @@ -811,6 +819,8 @@ 83A936C81B7E0F08005B9C36 /* RCTConvert_UIColorTests.m in Sources */, 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */, 138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */, + 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */, + 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m index ccf5e7f74..267ed1409 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m @@ -57,7 +57,7 @@ expectErrorRegex:@"because shouldThrow"]; } -- (void)DISABLED_testTimers // #8192477 +- (void)testTimers { [_runner runTest:_cmd module:@"TimersTest"]; } diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m index cd44b7229..4fe7294fb 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m @@ -23,7 +23,7 @@ #define RUN_RUNLOOP_WHILE(CONDITION) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wshadow\"") \ -NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \ +NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:5]; \ while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \ } \ @@ -141,7 +141,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a XCTAssertNil(weakMethod, @"RCTModuleMethod should have been deallocated"); } -- (void)DISABLED_testJavaScriptExecutorIsDeallocated // flaky: #8195866 +- (void)testJavaScriptExecutorIsDeallocated { __weak id weakExecutor; @autoreleasepool { @@ -157,7 +157,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a XCTAssertNil(weakExecutor, @"JavaScriptExecutor should have been released"); } -- (void)disabled_testJavaScriptContextIsDeallocated +- (void)testJavaScriptContextIsDeallocated { __weak id weakContext; @autoreleasepool { diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h new file mode 100644 index 000000000..364cddc88 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h @@ -0,0 +1,47 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * 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 NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK 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. + */ + +#import "RCTImageLoader.h" + +typedef BOOL (^RCTImageURLLoaderCanLoadImageURLHandler)(NSURL *requestURL); +typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(NSURL *imageURL, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler); + +@interface RCTConcreteImageURLLoader : NSObject + +- (instancetype)initWithPriority:(float)priority + canLoadImageURLHandler:(RCTImageURLLoaderCanLoadImageURLHandler)canLoadImageURLHandler + loadImageURLHandler:(RCTImageURLLoaderLoadImageURLHandler)loadImageURLHandler; + +@end + +typedef BOOL (^RCTImageDecoderCanDecodeImageDataHandler)(NSData *imageData); +typedef RCTImageLoaderCancellationBlock (^RCTImageDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler); + +@interface RCTConcreteImageDecoder : NSObject + +- (instancetype)initWithPriority:(float)priority + canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler + decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler; + +@end + +#define _RCTDefineImageHandler(SUPERCLASS, CLASS_NAME) \ +@interface CLASS_NAME : SUPERCLASS @end \ +@implementation CLASS_NAME RCT_EXPORT_MODULE() @end + +#define RCTDefineImageURLLoader(CLASS_NAME) \ +_RCTDefineImageHandler(RCTConcreteImageURLLoader, CLASS_NAME) + +#define RCTDefineImageDecoder(CLASS_NAME) \ +_RCTDefineImageHandler(RCTConcreteImageDecoder, CLASS_NAME) diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m new file mode 100644 index 000000000..2038fad50 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m @@ -0,0 +1,105 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * 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 NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK 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. + */ + +#import "RCTImageLoaderHelpers.h" + +@implementation RCTConcreteImageURLLoader +{ + RCTImageURLLoaderCanLoadImageURLHandler _canLoadImageURLHandler; + RCTImageURLLoaderLoadImageURLHandler _loadImageURLHandler; + float _priority; +} + ++ (NSString *)moduleName +{ + return nil; +} + +- (instancetype)init +{ + return nil; +} + +- (instancetype)initWithPriority:(float)priority canLoadImageURLHandler:(RCTImageURLLoaderCanLoadImageURLHandler)canLoadImageURLHandler loadImageURLHandler:(RCTImageURLLoaderLoadImageURLHandler)loadImageURLHandler +{ + if ((self = [super init])) { + _canLoadImageURLHandler = [canLoadImageURLHandler copy]; + _loadImageURLHandler = [loadImageURLHandler copy]; + _priority = priority; + } + + return self; +} + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return _canLoadImageURLHandler(requestURL); +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler); +} + +- (float)imageLoaderPriority +{ + return _priority; +} + +@end + +@implementation RCTConcreteImageDecoder +{ + RCTImageDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler; + RCTImageDecoderDecodeImageDataHandler _decodeImageDataHandler; + float _priority; +} + ++ (NSString *)moduleName +{ + return nil; +} + +- (instancetype)init +{ + return nil; +} + +- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler +{ + if ((self = [super init])) { + _canDecodeImageDataHandler = [canDecodeImageDataHandler copy]; + _decodeImageDataHandler = [decodeImageDataHandler copy]; + _priority = priority; + } + + return self; +} + +- (BOOL)canDecodeImageData:(NSData *)imageData +{ + return _canDecodeImageDataHandler(imageData); +} + +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler); +} + +- (float)imageDecoderPriority +{ + return _priority; +} + +@end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m new file mode 100644 index 000000000..d0323df7a --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m @@ -0,0 +1,146 @@ +/** + * The examples provided by Facebook are for non-commercial testing and + * evaluation purposes only. + * + * Facebook reserves all rights not expressly granted. + * + * 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 NON INFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK 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. + */ + +#import + +#import "RCTBridge.h" +#import "RCTImageLoader.h" +#import "RCTImageLoaderHelpers.h" + +unsigned char blackGIF[] = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b +}; + +RCTDefineImageURLLoader(RCTImageLoaderTestsURLLoader1) +RCTDefineImageURLLoader(RCTImageLoaderTestsURLLoader2) +RCTDefineImageDecoder(RCTImageLoaderTestsDecoder1) +RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) + +@interface RCTImageLoaderTests : XCTestCase + +@end + +@implementation RCTImageLoaderTests + +- (void)testImageLoading +{ + UIImage *image = [UIImage new]; + + id loader = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { + return YES; + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { + progressHandler(1, 1); + completionHandler(nil, image); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader, imageLoader]; } launchOptions:nil]; + + [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + XCTAssertEqual(progress, 1); + XCTAssertEqual(total, 1); + } completionBlock:^(NSError *loadError, id loadedImage) { + XCTAssertEqualObjects(loadedImage, image); + XCTAssertNil(loadError); + }]; +} + +- (void)testImageLoaderUsesImageURLLoaderWithHighestPriority +{ + UIImage *image = [UIImage new]; + + id loader1 = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { + return YES; + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { + progressHandler(1, 1); + completionHandler(nil, image); + return nil; + }]; + + id loader2 = [[RCTImageLoaderTestsURLLoader2 alloc] initWithPriority:0.5 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { + return YES; + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderProgressBlock progressHandler, __unused RCTImageLoaderCompletionBlock completionHandler) { + XCTFail(@"Should not have used loader2"); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2, imageLoader]; } launchOptions:nil]; + + [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + XCTAssertEqual(progress, 1); + XCTAssertEqual(total, 1); + } completionBlock:^(NSError *loadError, id loadedImage) { + XCTAssertEqualObjects(loadedImage, image); + XCTAssertNil(loadError); + }]; +} + +- (void)testImageDecoding +{ + NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO]; + UIImage *image = [[UIImage alloc] initWithData:data]; + + id decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + return YES; + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { + XCTAssertEqualObjects(imageData, data); + completionHandler(nil, image); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder, imageLoader]; } launchOptions:nil]; + + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) { + XCTAssertEqualObjects(decodedImage, image); + XCTAssertNil(decodeError); + }]; + XCTAssertNil(cancelBlock); +} + +- (void)testImageLoaderUsesImageDecoderWithHighestPriority +{ + NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO]; + UIImage *image = [[UIImage alloc] initWithData:data]; + + id decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + return YES; + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { + XCTAssertEqualObjects(imageData, data); + completionHandler(nil, image); + return nil; + }]; + + id decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + return YES; + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) { + XCTFail(@"Should not have used decoder2"); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2, imageLoader]; } launchOptions:nil]; + + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) { + XCTAssertEqualObjects(decodedImage, image); + XCTAssertNil(decodeError); + }]; + XCTAssertNil(cancelBlock); +} + +@end diff --git a/Examples/UIExplorer/createExamplePage.js b/Examples/UIExplorer/createExamplePage.js index 86525c485..410688160 100644 --- a/Examples/UIExplorer/createExamplePage.js +++ b/Examples/UIExplorer/createExamplePage.js @@ -17,6 +17,9 @@ 'use strict'; var React = require('react-native'); +var { + Platform, +} = React; var ReactNative = require('ReactNative'); var UIExplorerBlock = require('./UIExplorerBlock'); var UIExplorerPage = require('./UIExplorerPage'); @@ -36,6 +39,12 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule) }, getBlock: function(example: Example, i) { + if (example.platform) { + if (Platform.OS !== example.platform) { + return; + } + example.title += ' (' + example.platform + ' only)'; + } // Hack warning: This is a hack because the www UI explorer requires // renderComponent to be called. var originalRender = React.render; diff --git a/JSCLegacyProfiler/JSCLegacyProfiler.mm b/JSCLegacyProfiler/JSCLegacyProfiler.mm index dd2f01f8a..e906eda0f 100644 --- a/JSCLegacyProfiler/JSCLegacyProfiler.mm +++ b/JSCLegacyProfiler/JSCLegacyProfiler.mm @@ -1,5 +1,3 @@ -//#include "config.h" - #include "JSCLegacyProfiler.h" #include "APICast.h" diff --git a/JSCLegacyProfiler/Makefile b/JSCLegacyProfiler/Makefile index a1f704d98..306c6632c 100644 --- a/JSCLegacyProfiler/Makefile +++ b/JSCLegacyProfiler/Makefile @@ -1,112 +1,101 @@ -HEADER_PATHS := `find ./tmp/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"` -SDK_VERSION=$(shell plutil -convert json -o - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.plist | awk -f parseSDKVersion.awk) +HEADER_PATHS := `find download/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"` + +SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/$1.platform/Developer/SDKs/$1.sdk + +SDK_VERSION = $(shell plutil -convert json -o - $(call SDK_PATH,iPhoneOS)/SDKSettings.plist | awk -f parseSDKVersion.awk) + CERT ?= "iPhone Developer" +ARCHS = x86_64 arm64 armv7 i386 + +PLATFORM = \ + if [[ "$*" = "x86_64" || "$*" = "i386" ]]; then \ + PLATFORM=iPhoneSimulator; \ + else \ + PLATFORM=iPhoneOS; \ + fi; + +SYSROOT = -isysroot $(call SDK_PATH,$${PLATFORM}) + +IOS8_LIBS = download/WebCore/WebCore-7600.1.25 download/WTF/WTF-7600.1.24 download/JavaScriptCore/JavaScriptCore-7600.1.17 download/JavaScriptCore/JavaScriptCore-7600.1.17/Bytecodes.h libyajl.a + +ios8: RCTJSCProfiler.ios8.dylib /tmp/RCTJSCProfiler ifneq ($(SDK_VERSION), 8) -all: $(error "Expected to be compiled with iOS SDK version 8, found $(SDK_VERSION)") +else + cp $^ endif -ios8: prepare build generate +/tmp/JSCProfiler: + mkdir -p $@ -prepare: clean create download +.PRECIOUS: RCTJSCProfiler.ios8.dylib +RCTJSCProfiler.ios8.dylib: RCTJSCProfiler_unsigned.ios8.dylib + cp $< $@ + codesign -f -s ${CERT} $@ || rm $@ -build: x86_64 arm64 armv7 +.PRECIOUS: RCTJSCProfiler_unsigned.ios8.dylib +RCTJSCProfiler_unsigned.ios8.dylib: $(patsubst %,RCTJSCProfiler_%.ios8.dylib,$(ARCHS)) + lipo -create -output $@ $^ -generate: lipo codesign +.PRECIOUS: RCTJSCProfiler_%.ios8.dylib +RCTJSCProfiler_%.ios8.dylib: $(IOS8_LIBS) + $(PLATFORM) \ + clang -w -dynamiclib -o RCTJSCProfiler_$*.ios8.dylib -std=c++11 \ + -arch $* \ + -install_name RCTJSCProfiler.ios8.dylib \ + -include ./download/JavaScriptCore/JavaScriptCore-7600.1.17/config.h \ + -I download \ + -I download/WebCore/WebCore-7600.1.25/icu \ + -I download/WTF/WTF-7600.1.24 \ + -I download/yajl-2.1.0/build/yajl-2.1.0/include \ + -DNDEBUG=1\ + -miphoneos-version-min=8.0 \ + $(SYSROOT) \ + $(HEADER_PATHS) \ + -undefined dynamic_lookup \ + JSCLegacyProfiler.mm libyajl.a -clean: - @rm -rf tmp/ /tmp/RCTJSCProfiler +.PRECIOUS: %/Bytecodes.h +%/Bytecodes.h: + python $*/generate-bytecode-files --bytecodes_h $@ $*/bytecode/BytecodeList.json -lipo: - lipo -create -output /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib ./tmp/RCTJSCProfiler_x86_64 ./tmp/RCTJSCProfiler_arm64 ./tmp/RCTJSCProfiler_armv7 +.PRECIOUS: libyajl.a +libyajl.a: $(patsubst %,libyajl_%.a,$(ARCHS)) + lipo -create $^ -output $@ -codesign: - codesign -f -s ${CERT} /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib - -create: - mkdir -p ./tmp /tmp/RCTJSCProfiler/ ./tmp/CoreFoundation ./tmp/Foundation - for file in ./tmp/CoreFoundation/CFUserNotification.h ./tmp/CoreFoundation/CFXMLNode.h ./tmp/CoreFoundation/CFXMLParser.h ./tmp/Foundation/Foundation.h; do echo '' > "$$file"; done - -download: wtf jsc webcore yajl - -wtf: - curl -o tmp/WTF.tar.gz http://www.opensource.apple.com/tarballs/WTF/WTF-7600.1.24.tar.gz - tar -zxvf tmp/WTF.tar.gz -C tmp - -jsc: - curl -o tmp/JSC.tar.gz http://www.opensource.apple.com/tarballs/JavaScriptCore/JavaScriptCore-7600.1.17.tar.gz - tar -zxvf tmp/JSC.tar.gz -C tmp - mv ./tmp/JavaScriptCore-7600.1.17 ./tmp/JavaScriptCore - python ./tmp/JavaScriptCore/generate-bytecode-files --bytecodes_h ./tmp/JavaScriptCore/Bytecodes.h ./tmp/JavaScriptCore/bytecode/BytecodeList.json - -webcore: - curl -o tmp/WebCore.tar.gz http://www.opensource.apple.com/tarballs/WebCore/WebCore-7600.1.25.tar.gz - tar -zxvf tmp/WebCore.tar.gz -C tmp - -yajl: - curl -o tmp/yajl.tar.gz https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0 - tar -zxvf tmp/yajl.tar.gz -C tmp - mkdir -p ./tmp/yajl-2.1.0/build && cd ./tmp/yajl-2.1.0/build && cmake .. && make - echo `find . -name '*.c'` - cd ./tmp/yajl-2.1.0/src && \ - clang -arch arm64 -arch armv7 -std=c99 \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/ \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/machine \ +.PRECIOUS: libyajl_%.a +libyajl_%.a: download/yajl-2.1.0 + $(PLATFORM) \ + cd download/yajl-2.1.0/src; \ + clang -arch $(*F) -std=c99 \ + -miphoneos-version-min=8.0 \ + $(SYSROOT) \ -I ../build/yajl-2.1.0/include \ -c `find . -name '*.c'` - libtool -static -o ./tmp/yajl.a `find ./tmp/yajl-2.1.0/src/ -name '*.o'` + find download/yajl-2.1.0/src/ -name '*.o' -exec libtool -static -o $@ {} + -x86_64: - clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_x86_64 -std=c++11 \ - -install_name RCTJSCProfiler.ios8.dylib \ - -include ./tmp/JavaScriptCore/config.h \ - -I ./tmp \ - -I ./tmp/WebCore-7600.1.25/icu \ - -I ./tmp/WTF-7600.1.24 \ - -I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \ - -DNDEBUG=1\ - -miphoneos-version-min=8.0 \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/system \ - ${HEADER_PATHS} \ - -undefined dynamic_lookup \ - ./JSCLegacyProfiler.mm ./tmp/yajl-2.1.0/build/yajl-2.1.0/lib/libyajl_s.a +.PRECIOUS: download/yajl-2.1.0 +download/yajl-2.1.0: download/yajl-2.1.0.tar.gz + tar -zxvf $< -C download + mkdir -p download/yajl-2.1.0/build && cd download/yajl-2.1.0/build && cmake .. -arm64: - echo $(HEADER_PATHS) - clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_arm64 -std=c++11 \ - -install_name RCTJSCProfiler.ios8.dylib \ - -arch arm64 \ - -include ./tmp/JavaScriptCore/config.h \ - -I ./tmp \ - -I ./tmp/WebCore-7600.1.25/icu \ - -I ./tmp/WTF-7600.1.24 \ - -I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/machine \ - -DNDEBUG=1\ - -miphoneos-version-min=8.0 \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/system \ - ${HEADER_PATHS} \ - -undefined dynamic_lookup \ - ./JSCLegacyProfiler.mm ./tmp/yajl.a +.PRECIOUS: download/yajl-2.1.0.tar.gz +download/yajl-2.1.0.tar.gz: + mkdir -p `dirname $@` + curl -o $@ https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0 -armv7: - clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_armv7 -std=c++11 \ - -install_name RCTJSCProfiler.ios8.dylib \ - -arch armv7 \ - -include ./tmp/JavaScriptCore/config.h \ - -I ./tmp \ - -I ./tmp/WebCore-7600.1.25/icu \ - -I ./tmp/WTF-7600.1.24 \ - -I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include \ - -DNDEBUG=1\ - -miphoneos-version-min=8.0 \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/system \ - ${HEADER_PATHS} \ - -undefined dynamic_lookup \ - ./JSCLegacyProfiler.mm ./tmp/yajl.a +.PRECIOUS: download/% +download/%: download/%.tar.gz + tar -zxvf $< -C `dirname $@` + +.PRECIOUS: %.tar.gz +%.tar.gz: + mkdir -p `dirname $@` + curl -o $@ http://www.opensource.apple.com/tarballs/$(patsubst download/%,%,$@) + +.PHONY: clean +clean: + @rm -rf $(wildcard *.dylib) + @rm -rf $(wildcard *.a) + @rm -rf download diff --git a/Libraries/Animated/Easing.js b/Libraries/Animated/Easing.js index 58969278b..fe40b20b2 100644 --- a/Libraries/Animated/Easing.js +++ b/Libraries/Animated/Easing.js @@ -69,10 +69,7 @@ class Easing { * http://tiny.cc/elastic_b_1 (default bounciness = 1) * http://tiny.cc/elastic_b_3 (bounciness = 3) */ - static elastic(bounciness: number): (t: number) => number { - if (arguments.length === 0) { - bounciness = 1; - } + static elastic(bounciness: number = 1): (t: number) => number { var p = bounciness * Math.PI; return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p); }; diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js index 266a50d34..113a09262 100644 --- a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js +++ b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js @@ -30,6 +30,12 @@ var NativeModules = { customBubblingEventTypes: {}, customDirectEventTypes: {}, Dimensions: {}, + RCTModalFullscreenView: { + Constants: {}, + }, + RCTScrollView: { + Constants: {}, + }, }, AsyncLocalStorage: { getItem: jest.genMockFunction(), @@ -44,6 +50,13 @@ var NativeModules = { appVersion: '0', buildVersion: '0', }, + ModalFullscreenViewManager: {}, + AlertManager: { + alertWithArgs: jest.genMockFunction(), + }, + Pasteboard: { + setPasteboardString: jest.genMockFunction(), + }, }; module.exports = NativeModules; diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index da4350e4c..146034f7c 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -277,7 +277,9 @@ if (Platform.OS === 'android') { uiViewClassName: 'RCTMap', }); } else { - var RCTMap = requireNativeComponent('RCTMap', MapView); + var RCTMap = requireNativeComponent('RCTMap', MapView, { + nativeOnly: {onChange: true, onPress: true} + }); } module.exports = MapView; diff --git a/Libraries/Components/SliderIOS/SliderIOS.ios.js b/Libraries/Components/SliderIOS/SliderIOS.ios.js index bfb2b9271..0c06fbf6f 100644 --- a/Libraries/Components/SliderIOS/SliderIOS.ios.js +++ b/Libraries/Components/SliderIOS/SliderIOS.ios.js @@ -107,6 +107,8 @@ var styles = StyleSheet.create({ }, }); -var RCTSlider = requireNativeComponent('RCTSlider', SliderIOS); +var RCTSlider = requireNativeComponent('RCTSlider', SliderIOS, { + nativeOnly: { onChange: true }, +}); module.exports = SliderIOS; diff --git a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js index 5a56e36b7..e99d51210 100644 --- a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js +++ b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js @@ -108,6 +108,8 @@ var styles = StyleSheet.create({ }, }); -var RCTSwitch = requireNativeComponent('RCTSwitch', SwitchIOS); +var RCTSwitch = requireNativeComponent('RCTSwitch', SwitchIOS, { + nativeOnly: { onChange: true } +}); module.exports = SwitchIOS; diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 4b6442ed4..27ddcdd4e 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -211,6 +211,7 @@ var TouchableHighlight = React.createClass({ accessible={true} ref={UNDERLAY_REF} style={this.state.underlayStyle} + onLayout={this.props.onLayout} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest} onResponderGrant={this.touchableHandleResponderGrant} diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 7564a1b6f..12b03f030 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -158,6 +158,7 @@ var TouchableOpacity = React.createClass({ accessible={true} style={[this.props.style, {opacity: this.state.anim}]} testID={this.props.testID} + onLayout={this.props.onLayout} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest} onResponderGrant={this.touchableHandleResponderGrant} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index d8b171182..a4921052e 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -44,7 +44,15 @@ var TouchableWithoutFeedback = React.createClass({ onPress: React.PropTypes.func, onPressIn: React.PropTypes.func, onPressOut: React.PropTypes.func, + /** + * Invoked on mount and layout changes with + * + * `{nativeEvent: {layout: {x, y, width, height}}}` + */ + onLayout: React.PropTypes.func, + onLongPress: React.PropTypes.func, + /** * Delay in ms, from the start of the touch, before onPressIn is called. */ @@ -113,6 +121,7 @@ var TouchableWithoutFeedback = React.createClass({ return (React: any).cloneElement(onlyChild(this.props.children), { accessible: this.props.accessible !== false, testID: this.props.testID, + onLayout: this.props.onLayout, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest, onResponderGrant: this.touchableHandleResponderGrant, diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index fdb2ff6ed..d5235b5be 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -185,6 +185,10 @@ var View = React.createClass({ * Invoked on mount and layout changes with * * {nativeEvent: { layout: {x, y, width, height}}}. + * + * This event is fired immediately once the layout has been calculated, but + * the new layout may not yet be reflected on the screen at the time the + * event is received, especially if a layout animation is in progress. */ onLayout: PropTypes.func, diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 5bb0349cc..a3d457bfd 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -226,7 +226,13 @@ var WebView = React.createClass({ }, }); -var RCTWebView = requireNativeComponent('RCTWebView', WebView); +var RCTWebView = requireNativeComponent('RCTWebView', WebView, { + nativeOnly: { + onLoadingStart: true, + onLoadingError: true, + onLoadingFinish: true, + }, +}); var styles = StyleSheet.create({ container: { diff --git a/Libraries/Image/RCTImageRequestHandler.h b/Libraries/Image/RCTAssetBundleImageLoader.h similarity index 76% rename from Libraries/Image/RCTImageRequestHandler.h rename to Libraries/Image/RCTAssetBundleImageLoader.h index 0f0359885..ff460e08e 100644 --- a/Libraries/Image/RCTImageRequestHandler.h +++ b/Libraries/Image/RCTAssetBundleImageLoader.h @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTURLRequestHandler.h" +#import "RCTImageLoader.h" -@interface RCTImageRequestHandler : NSObject +@interface RCTAssetBundleImageLoader : NSObject @end diff --git a/Libraries/Image/RCTAssetBundleImageLoader.m b/Libraries/Image/RCTAssetBundleImageLoader.m new file mode 100644 index 000000000..fb9aaa057 --- /dev/null +++ b/Libraries/Image/RCTAssetBundleImageLoader.m @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTAssetBundleImageLoader.h" + +#import "RCTUtils.h" + +@implementation RCTAssetBundleImageLoader + +RCT_EXPORT_MODULE() + +- (NSString *)imageNameForRequestURL:(NSURL *)requestURL +{ + if (!requestURL.fileURL) { + return nil; + } + + NSString *resourcesPath = [NSBundle mainBundle].resourcePath; + NSString *requestPath = requestURL.absoluteURL.path; + if (requestPath.length < resourcesPath.length + 1) { + return nil; + } + + return [requestPath substringFromIndex:resourcesPath.length + 1]; +} + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + NSString *imageName = [self imageNameForRequestURL:requestURL]; + if (!imageName.length) { + return NO; + } + + if ([[NSBundle mainBundle] URLForResource:imageName withExtension:nil] || + [[NSBundle mainBundle] URLForResource:imageName withExtension:@"png"]) { + return YES; + } + + return imageName.pathComponents.count == 1 && !imageName.pathExtension.length; +} + + - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + NSString *imageName = [self imageNameForRequestURL:imageURL]; + + __block BOOL cancelled = NO; + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelled) { + return; + } + + UIImage *image = [UIImage imageNamed:imageName]; + if (image) { + if (progressHandler) { + progressHandler(1, 1); + } + + if (completionHandler) { + completionHandler(nil, image); + } + } else { + if (completionHandler) { + NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName]; + completionHandler(RCTErrorWithMessage(message), nil); + } + } + }); + + return ^{ + cancelled = YES; + }; +} + +@end diff --git a/Libraries/Image/RCTAssetsLibraryImageLoader.h b/Libraries/Image/RCTAssetsLibraryImageLoader.h new file mode 100644 index 000000000..f075325e1 --- /dev/null +++ b/Libraries/Image/RCTAssetsLibraryImageLoader.h @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTImageLoader.h" + +@interface RCTAssetsLibraryImageLoader : NSObject + +@end + +@interface RCTBridge (RCTAssetsLibraryImageLoader) + +/** + * The shared Assets Library image loader + */ +@property (nonatomic, readonly) RCTAssetsLibraryImageLoader *assetsLibraryImageLoader; + +/** + * The shared asset library instance. + */ +@property (nonatomic, readonly) ALAssetsLibrary *assetsLibrary; + +@end diff --git a/Libraries/Image/RCTAssetsLibraryImageLoader.m b/Libraries/Image/RCTAssetsLibraryImageLoader.m new file mode 100644 index 000000000..bc6908fed --- /dev/null +++ b/Libraries/Image/RCTAssetsLibraryImageLoader.m @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTAssetsLibraryImageLoader.h" + +#import +#import +#import + +#import "RCTBridge.h" +#import "RCTConvert.h" +#import "RCTImageLoader.h" +#import "RCTImageUtils.h" +#import "RCTUtils.h" + +static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void); +static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error); + +@implementation RCTAssetsLibraryImageLoader +{ + ALAssetsLibrary *_assetsLibrary; +} + +RCT_EXPORT_MODULE() + +@synthesize bridge = _bridge; + +- (ALAssetsLibrary *)assetsLibrary +{ + return _assetsLibrary ?: (_assetsLibrary = [ALAssetsLibrary new]); +} + +#pragma mark - RCTImageLoader + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return [requestURL.scheme.lowercaseString isEqualToString:@"assets-library"]; +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + [[self assetsLibrary] assetForURL:imageURL resultBlock:^(ALAsset *asset) { + if (asset) { + // ALAssetLibrary API is async and will be multi-threaded. Loading a few full + // resolution images at once will spike the memory up to store the image data, + // and might trigger memory warnings and/or OOM crashes. + // To improve this, process the loaded asset in a serial queue. + dispatch_async(RCTAssetsLibraryImageLoaderQueue(), ^{ + // Also make sure the image is released immediately after it's used so it + // doesn't spike the memory up during the process. + @autoreleasepool { + + BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); + ALAssetRepresentation *representation = [asset defaultRepresentation]; + + UIImage *image; + NSError *error = nil; + if (useMaximumSize) { + image = [UIImage imageWithCGImage:representation.fullResolutionImage + scale:scale + orientation:(UIImageOrientation)representation.orientation]; + } else { + image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error); + } + + completionHandler(error, image); + } + }); + } else { + NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL]; + NSError *error = RCTErrorWithMessage(errorText); + completionHandler(error, nil); + } + } failureBlock:^(NSError *loadError) { + NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError]; + NSError *error = RCTErrorWithMessage(errorText); + completionHandler(error, nil); + }]; + + return ^{}; +} + +@end + +@implementation RCTBridge (RCTAssetsLibraryImageLoader) + +- (RCTAssetsLibraryImageLoader *)assetsLibraryImageLoader +{ + return self.modules[RCTBridgeModuleNameForClass([RCTAssetsLibraryImageLoader class])]; +} + +- (ALAssetsLibrary *)assetsLibrary +{ + return [self.assetsLibraryImageLoader assetsLibrary]; +} + +@end + +static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void) +{ + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("com.facebook.RCTAssetsLibraryImageLoader", DISPATCH_QUEUE_SERIAL); + }); + + return queue; +} + +// Why use a custom scaling method? Greater efficiency, reduced memory overhead: +// http://www.mindsea.com/2012/12/downscaling-huge-alassets-without-fear-of-sigkill + +static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error) +{ + NSUInteger length = (NSUInteger)representation.size; + NSMutableData *data = [NSMutableData dataWithLength:length]; + if (![representation getBytes:data.mutableBytes + fromOffset:0 + length:length + error:error]) { + return nil; + } + + CGSize sourceSize = representation.dimensions; + CGSize targetSize = RCTTargetSize(sourceSize, representation.scale, + size, scale, resizeMode, NO); + + NSDictionary *options = @{ + (id)kCGImageSourceShouldAllowFloat: @YES, + (id)kCGImageSourceCreateThumbnailWithTransform: @YES, + (id)kCGImageSourceCreateThumbnailFromImageAlways: @YES, + (id)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * scale) + }; + + CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil); + CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); + if (sourceRef) { + CFRelease(sourceRef); + } + + if (imageRef) { + UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale + orientation:UIImageOrientationUp]; + CGImageRelease(imageRef); + return image; + } + + return nil; +} diff --git a/Libraries/Image/RCTCameraRollManager.m b/Libraries/Image/RCTCameraRollManager.m index e2f2a297f..3d719f4ab 100644 --- a/Libraries/Image/RCTCameraRollManager.m +++ b/Libraries/Image/RCTCameraRollManager.m @@ -14,6 +14,7 @@ #import #import +#import "RCTAssetsLibraryImageLoader.h" #import "RCTBridge.h" #import "RCTImageLoader.h" #import "RCTLog.h" diff --git a/Libraries/Image/RCTGIFImage.h b/Libraries/Image/RCTGIFImageDecoder.h similarity index 56% rename from Libraries/Image/RCTGIFImage.h rename to Libraries/Image/RCTGIFImageDecoder.h index 25533591a..51c25fa2e 100644 --- a/Libraries/Image/RCTGIFImage.h +++ b/Libraries/Image/RCTGIFImageDecoder.h @@ -7,9 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import -#import -#import +#import "RCTImageLoader.h" -extern CAKeyframeAnimation *RCTGIFImageWithData(NSData *data); -extern CAKeyframeAnimation *RCTGIFImageWithFileURL(NSURL *URL); +@interface RCTGIFImageDecoder : NSObject + +@end diff --git a/Libraries/Image/RCTGIFImage.m b/Libraries/Image/RCTGIFImageDecoder.m similarity index 74% rename from Libraries/Image/RCTGIFImage.m rename to Libraries/Image/RCTGIFImageDecoder.m index 4258262ed..3d85f94e7 100644 --- a/Libraries/Image/RCTGIFImage.m +++ b/Libraries/Image/RCTGIFImageDecoder.m @@ -7,16 +7,29 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTGIFImage.h" +#import "RCTGIFImageDecoder.h" -#import "RCTLog.h" +#import +#import +#import -static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSource) +#import "RCTUtils.h" + +@implementation RCTGIFImageDecoder + +RCT_EXPORT_MODULE() + +- (BOOL)canDecodeImageData:(NSData *)imageData { - if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) { - return nil; - } + char header[7] = {}; + [imageData getBytes:header length:6]; + return !strcmp(header, "GIF87a") || !strcmp(header, "GIF89a"); +} + +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL); NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue]; @@ -48,6 +61,7 @@ static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSou delays[i] = delayTime; images[i] = (__bridge_transfer id)image; } + CFRelease(imageSource); NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count]; NSTimeInterval runningDuration = 0; @@ -64,34 +78,9 @@ static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSou animation.keyTimes = keyTimes; animation.values = images; animation.duration = duration; - return animation; + completionHandler(nil, animation); + + return nil; } -CAKeyframeAnimation *RCTGIFImageWithData(NSData *data) -{ - if (data.length == 0) { - return nil; - } - - CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL); - CAKeyframeAnimation *animation = RCTGIFImageWithImageSource(imageSource); - CFRelease(imageSource); - return animation; -} - -CAKeyframeAnimation *RCTGIFImageWithFileURL(NSURL *URL) -{ - if (!URL) { - return nil; - } - - if (!URL.fileURL) { - RCTLogError(@"Loading remote image URLs synchronously is a really bad idea."); - return nil; - } - - CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)URL, NULL); - CAKeyframeAnimation *animation = RCTGIFImageWithImageSource(imageSource); - CFRelease(imageSource); - return animation; -} +@end diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj index cbc8c58ee..06029c731 100644 --- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj +++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj @@ -9,8 +9,7 @@ /* Begin PBXBuildFile section */ 1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */; }; 1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; }; - 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; }; - 1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1345A8381B26592900583190 /* RCTImageRequestHandler.m */; }; + 1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; }; 134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; }; 137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; }; 143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; }; @@ -18,6 +17,9 @@ 35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; }; 354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; }; 58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; }; + 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; }; + 8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; }; + 83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -37,10 +39,8 @@ 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageView.m; sourceTree = ""; }; 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageViewManager.h; sourceTree = ""; }; 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageViewManager.m; sourceTree = ""; }; - 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImage.h; sourceTree = ""; }; - 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImage.m; sourceTree = ""; }; - 1345A8371B26592900583190 /* RCTImageRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageRequestHandler.h; sourceTree = ""; }; - 1345A8381B26592900583190 /* RCTImageRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageRequestHandler.m; sourceTree = ""; }; + 1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImageDecoder.h; sourceTree = ""; }; + 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = ""; }; 134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = ""; }; 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = ""; }; 137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = ""; }; @@ -56,6 +56,12 @@ 58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = ""; }; 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = ""; }; + 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = ""; }; + 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = ""; }; + 8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = ""; }; + 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = ""; }; + 83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetBundleImageLoader.h; sourceTree = ""; }; + 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetBundleImageLoader.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,10 +78,14 @@ 58B511541A9E6B3D00147676 = { isa = PBXGroup; children = ( + 83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */, + 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */, + 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */, + 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */, 143879331AAD238D00F088A5 /* RCTCameraRollManager.h */, 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */, - 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */, - 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */, + 1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */, + 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */, 58B511891A9E6BD600147676 /* RCTImageDownloader.h */, 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */, 354631661B69857700AA0B86 /* RCTImageEditingManager.h */, @@ -84,8 +94,6 @@ 143879371AAD32A300F088A5 /* RCTImageLoader.m */, 137620331B31C53500677FF0 /* RCTImagePickerManager.h */, 137620341B31C53500677FF0 /* RCTImagePickerManager.m */, - 1345A8371B26592900583190 /* RCTImageRequestHandler.h */, - 1345A8381B26592900583190 /* RCTImageRequestHandler.m */, 1304D5A71AA8C4A30002E2BE /* RCTImageView.h */, 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */, 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */, @@ -94,6 +102,8 @@ 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */, 134B00A01B54232B00EC8DFB /* RCTImageUtils.h */, 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */, + 8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */, + 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */, 58B5115E1A9E6B3D00147676 /* Products */, ); indentWidth = 2; @@ -164,17 +174,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */, 35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */, + 8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */, 58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */, 137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */, 1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */, - 1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */, - 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */, + 1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */, 143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */, 143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */, 354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */, 1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */, 134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */, + 83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Libraries/Image/RCTImageDownloader.h b/Libraries/Image/RCTImageDownloader.h index c231162c4..0e5c8de0b 100644 --- a/Libraries/Image/RCTImageDownloader.h +++ b/Libraries/Image/RCTImageDownloader.h @@ -7,25 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import - #import "RCTBridge.h" #import "RCTImageLoader.h" -@interface RCTImageDownloader : NSObject - -/** - * Downloads an image and decompresses it a the size specified. The compressed - * image will be cached in memory and to disk. Note that the callback block - * will not be executed on the same thread you called the method from, nor on - * the main thread. Returns a token that can be used to cancel the download. - */ -- (RCTImageLoaderCancellationBlock)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - progressBlock:(RCTImageLoaderProgressBlock)progressBlock - completionBlock:(RCTImageLoaderCompletionBlock)block; +@interface RCTImageDownloader : NSObject @end diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index 22bbe9031..f110bd628 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -9,7 +9,7 @@ #import "RCTImageDownloader.h" -#import "RCTGIFImage.h" +#import "RCTImageLoader.h" #import "RCTImageUtils.h" #import "RCTLog.h" #import "RCTNetworking.h" @@ -25,16 +25,6 @@ RCT_EXPORT_MODULE() -+ (RCTImageDownloader *)sharedInstance -{ - static RCTImageDownloader *sharedInstance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [RCTImageDownloader new]; - }); - return sharedInstance; -} - - (instancetype)init { if ((self = [super init])) { @@ -44,14 +34,23 @@ RCT_EXPORT_MODULE() return self; } +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + // Have to exclude 'file://' from the main bundle, otherwise this would conflict with RCTAssetBundleImageLoader + return + [requestURL.scheme compare:@"http" options:NSCaseInsensitiveSearch range:NSMakeRange(0, 4)] == NSOrderedSame || + ([requestURL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame && ![requestURL.path hasPrefix:[NSBundle mainBundle].resourcePath]) || + [requestURL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame; +} + /** * Downloads a block of raw data and returns it. Note that the callback block * will not be executed on the same thread you called the method from, nor on * the main thread. Returns a token that can be used to cancel the download. */ - (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url - progressBlock:(RCTImageLoaderProgressBlock)progressBlock - completionBlock:(RCTImageLoaderCompletionBlock)completionBlock + progressHandler:(RCTImageLoaderProgressBlock)progressBlock + completionHandler:(RCTImageLoaderCompletionBlock)completionBlock { if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) { RCTLogError(@"You need to import the RCTNetworking library in order to download remote images."); @@ -99,49 +98,89 @@ RCT_EXPORT_MODULE() return ^{ [task cancel]; }; } -- (RCTImageLoaderCancellationBlock)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - progressBlock:(RCTImageLoaderProgressBlock)progressBlock - completionBlock:(RCTImageLoaderCompletionBlock)completionBlock +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + progressHandler:(RCTImageLoaderProgressBlock)progressHandler + completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { - scale = scale ?: RCTScreenScale(); + if ([imageURL.scheme.lowercaseString hasPrefix:@"http"]) { + __block RCTImageLoaderCancellationBlock decodeCancel = nil; - return [self downloadDataForURL:url progressBlock:progressBlock completionBlock:^(NSError *error, id data) { - - if (!data || error) { - completionBlock(error, nil); - return; - } - - if ([url.path.lowercaseString hasSuffix:@".gif"]) { - id image = RCTGIFImageWithData(data); - if (!image && !error) { - NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", url]; - error = RCTErrorWithMessage(errorMessage); + __weak RCTImageDownloader *weakSelf = self; + RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) { + if (error) { + completionHandler(error, nil); + } else { + decodeCancel = [weakSelf.bridge.imageLoader decodeImageData:imageData size:size scale:scale resizeMode:resizeMode completionBlock:completionHandler]; } - completionBlock(error, image); - return; - } + }]; - UIImage *image = [UIImage imageWithData:data scale:scale]; - if (image && !CGSizeEqualToSize(size, CGSizeZero)) { + return ^{ + downloadCancel(); - // Get destination size - CGSize targetSize = RCTTargetSize(image.size, image.scale, - size, scale, resizeMode, NO); + if (decodeCancel) { + decodeCancel(); + } + }; + } else if ([imageURL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame) { + __block BOOL cancelled = NO; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (cancelled) { + return; + } - // Decompress image at required size - BOOL opaque = !RCTImageHasAlpha(image.CGImage); - UIGraphicsBeginImageContextWithOptions(targetSize, opaque, scale); - [image drawInRect:(CGRect){CGPointZero, targetSize}]; - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } + // Normally -dataWithContentsOfURL: would be bad but this is a data URL. + NSData *data = [NSData dataWithContentsOfURL:imageURL]; - completionBlock(nil, image); - }]; + UIImage *image = [UIImage imageWithData:data]; + if (image) { + if (progressHandler) { + progressHandler(1, 1); + } + if (completionHandler) { + completionHandler(nil, image); + } + } else { + if (completionHandler) { + NSString *message = [NSString stringWithFormat:@"Invalid image data for URL: %@", imageURL]; + completionHandler(RCTErrorWithMessage(message), nil); + } + } + }); + return ^{ + cancelled = YES; + }; + } else if ([imageURL.scheme isEqualToString:@"file"]) { + __block BOOL cancelled = NO; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (cancelled) { + return; + } + + UIImage *image = [UIImage imageWithContentsOfFile:imageURL.resourceSpecifier]; + if (image) { + if (progressHandler) { + progressHandler(1, 1); + } + if (completionHandler) { + completionHandler(nil, image); + } + } else { + if (completionHandler) { + NSString *message = [NSString stringWithFormat:@"Could not find image at path: %@", imageURL.absoluteString]; + completionHandler(RCTErrorWithMessage(message), nil); + } + } + }); + return ^{ + cancelled = YES; + }; + } else { + RCTLogError(@"Unexpected image schema %@", imageURL.scheme); + return ^{}; + } } @end diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index bde29ec40..bba887602 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -10,14 +10,16 @@ #import #import "RCTBridge.h" +#import "RCTURLRequestHandler.h" @class ALAssetsLibrary; typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total); typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* UIImage or CAAnimation */); +typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* NSData, UIImage, CAAnimation */); typedef void (^RCTImageLoaderCancellationBlock)(void); -@interface RCTImageLoader : NSObject +@interface RCTImageLoader : NSObject /** * Loads the specified image at the highest available resolution. @@ -38,14 +40,14 @@ typedef void (^RCTImageLoaderCancellationBlock)(void); completionBlock:(RCTImageLoaderCompletionBlock)completionBlock; /** - * Is the specified image tag an asset library image? + * Finds an appropriate image decoder and passes the target size, scale and + * resizeMode for optimal image decoding. */ -+ (BOOL)isAssetLibraryImage:(NSString *)imageTag; - -/** - * Is the specified image tag a remote image? - */ -+ (BOOL)isRemoteImage:(NSString *)imageTag; +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + completionBlock:(RCTImageLoaderCompletionBlock)completionBlock; @end @@ -56,9 +58,72 @@ typedef void (^RCTImageLoaderCancellationBlock)(void); */ @property (nonatomic, readonly) RCTImageLoader *imageLoader; +@end + /** - * The shared asset library instance. + * Provides the interface needed to register an image data loader. Image data + * loaders are also bridge modules, so should be registered using + * RCT_EXPORT_MODULE(). */ -@property (nonatomic, readonly) ALAssetsLibrary *assetsLibrary; +@protocol RCTImageURLLoader + +/** + * Indicates whether this data loader is capable of processing the specified + * request URL. Typically the handler would examine the scheme/protocol of the + * URL to determine this. + */ +- (BOOL)canLoadImageURL:(NSURL *)requestURL; + +/** + * Send a network request to load the request URL. The method should call the + * progressHandler (if applicable) and the completionHandler when the request + * has finished. The method should also return a cancellation block, if + * applicable. + */ +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler; + +@optional + +/** + * If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:` + * then `imageLoaderPriority` is used to determine which one to use. The handler + * with the highest priority will be selected. Default priority is zero. If + * two or more valid handlers have the same priority, the selection order is + * undefined. + */ +- (float)imageLoaderPriority; + +@end + +/** + * Provides the interface needed to register an image decoder. Image decoders + * are also bridge modules, so should be registered using RCT_EXPORT_MODULE(). + */ +@protocol RCTImageDecoder + +/** + * Indicates whether this handler is capable of decoding the specified data. + * Typically the handler would examine some sort of header data to determine + * this. + */ +- (BOOL)canDecodeImageData:(NSData *)imageData; + +/** + * Decode an image from the data object. The method should call the + * completionHandler when the decoding operation has finished. The method + * should also return a cancellation block, if applicable. + */ +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler; + +@optional + +/** + * If more than one RCTImageDecoder responds YES to `-canDecodeImageData:` + * then `imageDecoderPriority` is used to determine which one to use. The + * handler with the highest priority will be selected. Default priority is zero. + * If two or more valid handlers have the same priority, the selection order is + * undefined. + */ +- (float)imageDecoderPriority; @end diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index a72e01ef2..422a189d5 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -9,19 +9,11 @@ #import "RCTImageLoader.h" -#import -#import -#import -#import #import -#import "RCTBridge.h" #import "RCTConvert.h" #import "RCTDefines.h" -#import "RCTGIFImage.h" #import "RCTImageDownloader.h" -#import "RCTImageStoreManager.h" -#import "RCTImageUtils.h" #import "RCTLog.h" #import "RCTUtils.h" @@ -36,21 +28,7 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr } } -static dispatch_queue_t RCTImageLoaderQueue(void) -{ - static dispatch_queue_t queue = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("com.facebook.rctImageLoader", DISPATCH_QUEUE_SERIAL); - }); - - return queue; -} - @implementation RCTImageLoader -{ - ALAssetsLibrary *_assetsLibrary; -} @synthesize bridge = _bridge; @@ -67,56 +45,33 @@ RCT_EXPORT_MODULE() completionBlock:callback]; } -// Why use a custom scaling method? Greater efficiency, reduced memory overhead: -// http://www.mindsea.com/2012/12/downscaling-huge-alassets-without-fear-of-sigkill - -static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, - CGSize size, CGFloat scale, - UIViewContentMode resizeMode, - NSError **error) +- (id)imageURLLoaderForRequest:(NSURL *)requestURL { - NSUInteger length = (NSUInteger)representation.size; - NSMutableData *data = [NSMutableData dataWithLength:length]; - if (![representation getBytes:data.mutableBytes - fromOffset:0 - length:length - error:error]) { - return nil; + NSMutableArray *handlers = [NSMutableArray array]; + for (id module in _bridge.modules.allValues) { + if ([module conformsToProtocol:@protocol(RCTImageURLLoader)]) { + if ([(id)module canLoadImageURL:requestURL]) { + [handlers addObject:module]; + } + } } + [handlers sortUsingComparator:^NSComparisonResult(id a, id b) { + float priorityA = [a respondsToSelector:@selector(imageLoaderPriority)] ? [a imageLoaderPriority] : 0; + float priorityB = [b respondsToSelector:@selector(imageLoaderPriority)] ? [b imageLoaderPriority] : 0; + if (priorityA < priorityB) { + return NSOrderedAscending; + } else if (priorityA > priorityB) { + return NSOrderedDescending; + } else { + RCTLogError(@"The RCTImageLoader %@ and %@ both reported that they can" + " handle the load request %@, and have equal priority (%g)." + " This could result in non-deterministic behavior.", + a, b, requestURL, priorityA); - CGSize sourceSize = representation.dimensions; - CGSize targetSize = RCTTargetSize(sourceSize, representation.scale, - size, scale, resizeMode, NO); - - NSDictionary *options = @{ - (id)kCGImageSourceShouldAllowFloat: @YES, - (id)kCGImageSourceCreateThumbnailWithTransform: @YES, - (id)kCGImageSourceCreateThumbnailFromImageAlways: @YES, - (id)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * scale) - }; - - CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil); - CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); - if (sourceRef) { - CFRelease(sourceRef); - } - - if (imageRef) { - UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale - orientation:UIImageOrientationUp]; - CGImageRelease(imageRef); - return image; - } - - return nil; -} - -- (ALAssetsLibrary *)assetsLibrary -{ - if (!_assetsLibrary) { - _assetsLibrary = [ALAssetsLibrary new]; - } - return _assetsLibrary; + return NSOrderedSame; + } + }]; + return [handlers lastObject]; } - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag @@ -126,141 +81,134 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, progressBlock:(RCTImageLoaderProgressBlock)progressBlock completionBlock:(RCTImageLoaderCompletionBlock)completionBlock { - if ([imageTag hasPrefix:@"assets-library://"]) { - [[self assetsLibrary] assetForURL:[RCTConvert NSURL:imageTag] resultBlock:^(ALAsset *asset) { - if (asset) { - // ALAssetLibrary API is async and will be multi-threaded. Loading a few full - // resolution images at once will spike the memory up to store the image data, - // and might trigger memory warnings and/or OOM crashes. - // To improve this, process the loaded asset in a serial queue. - dispatch_async(RCTImageLoaderQueue(), ^{ - // Also make sure the image is released immediately after it's used so it - // doesn't spike the memory up during the process. - @autoreleasepool { + NSURL *requestURL = [RCTConvert NSURL:imageTag]; + id loadHandler = [self imageURLLoaderForRequest:requestURL]; + if (!loadHandler) { + RCTLogError(@"No suitable image URL loader found for %@", imageTag); + } - BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); - ALAssetRepresentation *representation = [asset defaultRepresentation]; - - UIImage *image; - NSError *error = nil; - if (useMaximumSize) { - image = [UIImage imageWithCGImage:representation.fullResolutionImage - scale:scale - orientation:(UIImageOrientation)representation.orientation]; - } else { - image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error); - } - - RCTDispatchCallbackOnMainQueue(completionBlock, error, image); - } - }); - } else { - NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageTag]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - } - } failureBlock:^(NSError *loadError) { - NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageTag, loadError]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - }]; - return ^{}; - } else if ([imageTag hasPrefix:@"ph://"]) { - // Using PhotoKit for iOS 8+ - // The 'ph://' prefix is used by FBMediaKit to differentiate between - // assets-library. It is prepended to the local ID so that it is in the - // form of an, NSURL which is what assets-library uses. - NSString *phAssetID = [imageTag substringFromIndex:@"ph://".length]; - PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil]; - if (results.count == 0) { - NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - return ^{}; + return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:^(int64_t progress, int64_t total) { + if (!progressBlock) { + return; } - PHAsset *asset = results.firstObject; - - PHImageRequestOptions *imageOptions = [PHImageRequestOptions new]; - - BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); - CGSize targetSize; - - if ( useMaximumSize ){ - targetSize = PHImageManagerMaximumSize; - imageOptions.resizeMode = PHImageRequestOptionsResizeModeNone; + if ([NSThread isMainThread]) { + progressBlock(progress, total); } else { - targetSize = size; - imageOptions.resizeMode = PHImageRequestOptionsResizeModeFast; + dispatch_async(dispatch_get_main_queue(), ^{ + progressBlock(progress, total); + }); } + } completionHandler:^(NSError *error, id image) { + RCTDispatchCallbackOnMainQueue(completionBlock, error, image); + }] ?: ^{}; +} - PHImageContentMode contentMode = PHImageContentModeAspectFill; - if (resizeMode == UIViewContentModeScaleAspectFit) { - contentMode = PHImageContentModeAspectFit; - } - [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:imageOptions resultHandler:^(UIImage *result, NSDictionary *info) { - if (result) { - RCTDispatchCallbackOnMainQueue(completionBlock, nil, result); - } else { - NSString *errorText = [NSString stringWithFormat:@"Failed to load PHAsset with local identifier %@ with no error message.", phAssetID]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - return; +- (id)imageDecoderForRequest:(NSData *)imageData +{ + NSMutableArray *handlers = [NSMutableArray array]; + for (id module in _bridge.modules.allValues) { + if ([module conformsToProtocol:@protocol(RCTImageDecoder)]) { + if ([(id)module canDecodeImageData:imageData]) { + [handlers addObject:module]; } - }]; - return ^{}; - } else if ([imageTag hasPrefix:@"http"]) { - NSURL *url = [RCTConvert NSURL:imageTag]; - if (!url) { - NSString *errorMessage = [NSString stringWithFormat:@"Invalid URL: %@", imageTag]; - RCTDispatchCallbackOnMainQueue(completionBlock, RCTErrorWithMessage(errorMessage), nil); - return ^{}; } - return [_bridge.imageDownloader downloadImageForURL:url size:size scale:scale resizeMode:resizeMode progressBlock:progressBlock completionBlock:^(NSError *error, id image) { + } + [handlers sortUsingComparator:^NSComparisonResult(id a, id b) { + float priorityA = [a respondsToSelector:@selector(imageDecoderPriority)] ? [a imageDecoderPriority] : 0; + float priorityB = [b respondsToSelector:@selector(imageDecoderPriority)] ? [b imageDecoderPriority] : 0; + if (priorityA < priorityB) { + return NSOrderedAscending; + } else if (priorityA > priorityB) { + return NSOrderedDescending; + } else { + RCTLogError(@"The RCTImageDecoder %@ and %@ both reported that they can" + " handle the decode request , and have" + " equal priority (%g). This could result in" + " non-deterministic behavior.", + a, b, imageData, imageData.length, priorityA); + + return NSOrderedSame; + } + }]; + return [handlers lastObject]; +} + +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + completionBlock:(RCTImageLoaderCompletionBlock)completionBlock +{ + id imageDecoder = [self imageDecoderForRequest:data]; + if (imageDecoder) { + return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, id image) { RCTDispatchCallbackOnMainQueue(completionBlock, error, image); }]; - } else if ([imageTag hasPrefix:@"rct-image-store://"]) { - [_bridge.imageStoreManager getImageForTag:imageTag withBlock:^(UIImage *image) { + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + UIImage *image = [UIImage imageWithData:data]; if (image) { RCTDispatchCallbackOnMainQueue(completionBlock, nil, image); } else { - NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag]; - NSError *error = RCTErrorWithMessage(errorMessage); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); + NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data ", data, data.length]; + NSError *finalError = RCTErrorWithMessage(errorMessage); + RCTDispatchCallbackOnMainQueue(completionBlock, finalError, nil); } - }]; - return ^{}; - } else if ([imageTag.lowercaseString hasSuffix:@".gif"]) { - id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]); - if (image) { - RCTDispatchCallbackOnMainQueue(completionBlock, nil, image); - } else { - NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag]; - NSError *error = RCTErrorWithMessage(errorMessage); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - } - return ^{}; - } else { - UIImage *image = [RCTConvert UIImage:imageTag]; - if (image) { - RCTDispatchCallbackOnMainQueue(completionBlock, nil, image); - } else { - NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag]; - NSError *error = RCTErrorWithMessage(errorMessage); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - } + }); return ^{}; } } -+ (BOOL)isAssetLibraryImage:(NSString *)imageTag +#pragma mark - RCTURLRequestHandler + +- (BOOL)canHandleRequest:(NSURLRequest *)request { - return [imageTag hasPrefix:@"assets-library://"] || [imageTag hasPrefix:@"ph://"]; + id handler = [self imageURLLoaderForRequest:request.URL]; + + // RCTImageDownloader is an image plugin that uses the networking stack. + // We don't want to route any network calls through the image downloader + // as that would cause cyclical dependencies. + return handler && ![handler isKindOfClass:[RCTImageDownloader class]]; } -+ (BOOL)isRemoteImage:(NSString *)imageTag +- (id)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate { - return [imageTag hasPrefix:@"http://"] || [imageTag hasPrefix:@"https://"]; + __block RCTImageLoaderCancellationBlock requestToken; + requestToken = [self.bridge.imageLoader loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) { + if (error) { + [delegate URLRequest:requestToken didCompleteWithError:error]; + return; + } + + NSString *mimeType = nil; + NSData *imageData = nil; + if (RCTImageHasAlpha(image.CGImage)) { + mimeType = @"image/png"; + imageData = UIImagePNGRepresentation(image); + } else { + mimeType = @"image/jpeg"; + imageData = UIImageJPEGRepresentation(image, 1.0); + } + + NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL + MIMEType:mimeType + expectedContentLength:imageData.length + textEncodingName:nil]; + + [delegate URLRequest:requestToken didReceiveResponse:response]; + [delegate URLRequest:requestToken didReceiveData:imageData]; + [delegate URLRequest:requestToken didCompleteWithError:nil]; + }]; + + return requestToken; +} + +- (void)cancelRequest:(id)requestToken +{ + if (requestToken) { + ((RCTImageLoaderCancellationBlock)requestToken)(); + } } @end @@ -272,9 +220,4 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, return self.modules[RCTBridgeModuleNameForClass([RCTImageLoader class])]; } -- (ALAssetsLibrary *)assetsLibrary -{ - return [self.imageLoader assetsLibrary]; -} - @end diff --git a/Libraries/Image/RCTImageRequestHandler.m b/Libraries/Image/RCTImageRequestHandler.m deleted file mode 100644 index 1b3d76df1..000000000 --- a/Libraries/Image/RCTImageRequestHandler.m +++ /dev/null @@ -1,70 +0,0 @@ -// -// RCTImageRequestHandler.m -// RCTImage -// -// Created by Nick Lockwood on 09/06/2015. -// Copyright (c) 2015 Facebook. All rights reserved. -// - -#import "RCTImageRequestHandler.h" - -#import - -#import "RCTBridge.h" -#import "RCTImageLoader.h" -#import "RCTUtils.h" - -@implementation RCTImageRequestHandler - -RCT_EXPORT_MODULE() - -@synthesize bridge = _bridge; - -- (BOOL)canHandleRequest:(NSURLRequest *)request -{ - return [@[@"assets-library", @"ph"] containsObject:request.URL.scheme.lowercaseString]; -} - -- (id)sendRequest:(NSURLRequest *)request - withDelegate:(id)delegate -{ - NSString *URLString = request.URL.absoluteString; - - __block RCTImageLoaderCancellationBlock requestToken = nil; - requestToken = [_bridge.imageLoader loadImageWithTag:URLString callback:^(NSError *error, UIImage *image) { - if (error) { - [delegate URLRequest:requestToken didCompleteWithError:error]; - return; - } - - NSString *mimeType = nil; - NSData *imageData = nil; - if (RCTImageHasAlpha(image.CGImage)) { - mimeType = @"image/png"; - imageData = UIImagePNGRepresentation(image); - } else { - mimeType = @"image/jpeg"; - imageData = UIImageJPEGRepresentation(image, 1.0); - } - - NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL - MIMEType:mimeType - expectedContentLength:imageData.length - textEncodingName:nil]; - - [delegate URLRequest:requestToken didReceiveResponse:response]; - [delegate URLRequest:requestToken didReceiveData:imageData]; - [delegate URLRequest:requestToken didCompleteWithError:nil]; - }]; - - return requestToken; -} - -- (void)cancelRequest:(id /* RCTImageLoaderCancellationBlock */)requestToken -{ - if (requestToken) { - ((RCTImageLoaderCancellationBlock)requestToken)(); - } -} - -@end diff --git a/Libraries/Image/RCTImageStoreManager.h b/Libraries/Image/RCTImageStoreManager.h index afb4f24cb..9862d1bb2 100644 --- a/Libraries/Image/RCTImageStoreManager.h +++ b/Libraries/Image/RCTImageStoreManager.h @@ -3,9 +3,10 @@ #import #import "RCTBridge.h" +#import "RCTImageLoader.h" #import "RCTURLRequestHandler.h" -@interface RCTImageStoreManager : NSObject +@interface RCTImageStoreManager : NSObject /** * Set and get cached images. These must be called from the main thread. diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index 3ff66ce59..65a2e63f2 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -97,44 +97,27 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String } } -#pragma mark - RCTURLRequestHandler +#pragma mark - RCTImageLoader -- (BOOL)canHandleRequest:(NSURLRequest *)request +- (BOOL)canLoadImageURL:(NSURL *)requestURL { - return [@[@"rct-image-store"] containsObject:request.URL.scheme.lowercaseString]; + return [requestURL.scheme.lowercaseString isEqualToString:@"rct-image-store"]; } -- (id)sendRequest:(NSURLRequest *)request - withDelegate:(id)delegate +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { - NSString *imageTag = request.URL.absoluteString; + NSString *imageTag = imageURL.absoluteString; [self getImageForTag:imageTag withBlock:^(UIImage *image) { - if (!image) { - NSError *error = RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]); - [delegate URLRequest:request didCompleteWithError:error]; - return; - } - - NSString *mimeType = nil; - NSData *imageData = nil; - if (RCTImageHasAlpha(image.CGImage)) { - mimeType = @"image/png"; - imageData = UIImagePNGRepresentation(image); + if (image) { + completionHandler(nil, image); } else { - mimeType = @"image/jpeg"; - imageData = UIImageJPEGRepresentation(image, 1.0); + NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag]; + NSError *error = RCTErrorWithMessage(errorMessage); + completionHandler(error, nil); } - - NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL - MIMEType:mimeType - expectedContentLength:imageData.length - textEncodingName:nil]; - - [delegate URLRequest:request didReceiveResponse:response]; - [delegate URLRequest:request didReceiveData:imageData]; - [delegate URLRequest:request didCompleteWithError:nil]; }]; - return request; + + return nil; } @end diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index ddbd23d77..01e5b2fbe 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -12,7 +12,6 @@ #import "RCTBridge.h" #import "RCTConvert.h" #import "RCTEventDispatcher.h" -#import "RCTGIFImage.h" #import "RCTImageLoader.h" #import "RCTImageUtils.h" #import "RCTUtils.h" @@ -21,11 +20,11 @@ @interface RCTImageView () -@property (nonatomic, assign) BOOL onLoadStart; -@property (nonatomic, assign) BOOL onProgress; -@property (nonatomic, assign) BOOL onError; -@property (nonatomic, assign) BOOL onLoad; -@property (nonatomic, assign) BOOL onLoadEnd; +@property (nonatomic, copy) RCTDirectEventBlock onLoadStart; +@property (nonatomic, copy) RCTDirectEventBlock onProgress; +@property (nonatomic, copy) RCTDirectEventBlock onError; +@property (nonatomic, copy) RCTDirectEventBlock onLoad; +@property (nonatomic, copy) RCTDirectEventBlock onLoadEnd; @end @@ -101,11 +100,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } } ++ (BOOL)srcNeedsReload:(NSString *)src +{ + return + [src hasPrefix:@"http://"] || + [src hasPrefix:@"https://"] || + [src hasPrefix:@"assets-library://"] || + [src hasPrefix:@"ph://"]; +} + - (void)setContentMode:(UIViewContentMode)contentMode { if (self.contentMode != contentMode) { super.contentMode = contentMode; - if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) { + if ([RCTImageView srcNeedsReload:_src]) { [self reloadImage]; } } @@ -116,19 +124,16 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) { if (_onLoadStart) { - NSDictionary *event = @{ @"target": self.reactTag }; - [_bridge.eventDispatcher sendInputEventWithName:@"loadStart" body:event]; + _onLoadStart(nil); } RCTImageLoaderProgressBlock progressHandler = nil; if (_onProgress) { progressHandler = ^(int64_t loaded, int64_t total) { - NSDictionary *event = @{ - @"target": self.reactTag, + _onProgress(@{ @"loaded": @((double)loaded), @"total": @((double)total), - }; - [_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event]; + }); }; } @@ -147,21 +152,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } if (error) { if (_onError) { - NSDictionary *event = @{ - @"target": self.reactTag, - @"error": error.localizedDescription, - }; - [_bridge.eventDispatcher sendInputEventWithName:@"error" body:event]; + _onError(@{ @"error": error.localizedDescription }); } } else { if (_onLoad) { - NSDictionary *event = @{ @"target": self.reactTag }; - [_bridge.eventDispatcher sendInputEventWithName:@"load" body:event]; + _onLoad(nil); } } if (_onLoadEnd) { - NSDictionary *event = @{ @"target": self.reactTag }; - [_bridge.eventDispatcher sendInputEventWithName:@"loadEnd" body:event]; + _onLoadEnd(nil); } }]; } else { @@ -175,7 +174,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) [super reactSetFrame:frame]; if (self.image == nil) { [self reloadImage]; - } else if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) { + } else if ([RCTImageView srcNeedsReload:_src]) { // Get optimal image size CGSize currentSize = self.image.size; diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m index c797cdfab..bcf2f0bdb 100644 --- a/Libraries/Image/RCTImageViewManager.m +++ b/Libraries/Image/RCTImageViewManager.m @@ -27,11 +27,11 @@ RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets) RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage) RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode) RCT_EXPORT_VIEW_PROPERTY(src, NSString) -RCT_EXPORT_VIEW_PROPERTY(onLoadStart, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onProgress, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onError, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onLoad, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, BOOL) +RCT_EXPORT_VIEW_PROPERTY(onLoadStart, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock) RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView) { if (json) { @@ -43,15 +43,4 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView) } } -- (NSArray *)customDirectEventTypes -{ - return @[ - @"loadStart", - @"progress", - @"error", - @"load", - @"loadEnd", - ]; -} - @end diff --git a/Libraries/Image/RCTPhotoLibraryImageLoader.h b/Libraries/Image/RCTPhotoLibraryImageLoader.h new file mode 100644 index 000000000..33cbab3f0 --- /dev/null +++ b/Libraries/Image/RCTPhotoLibraryImageLoader.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTImageLoader.h" + +@interface RCTPhotoLibraryImageLoader : NSObject + +@end diff --git a/Libraries/Image/RCTPhotoLibraryImageLoader.m b/Libraries/Image/RCTPhotoLibraryImageLoader.m new file mode 100644 index 000000000..144f80805 --- /dev/null +++ b/Libraries/Image/RCTPhotoLibraryImageLoader.m @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTPhotoLibraryImageLoader.h" + +#import + +#import "RCTImageUtils.h" +#import "RCTUtils.h" + +@implementation RCTPhotoLibraryImageLoader + +RCT_EXPORT_MODULE() + +@synthesize bridge = _bridge; + +#pragma mark - RCTImageLoader + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return [requestURL.scheme.lowercaseString isEqualToString:@"ph"]; +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + // Using PhotoKit for iOS 8+ + // The 'ph://' prefix is used by FBMediaKit to differentiate between + // assets-library. It is prepended to the local ID so that it is in the + // form of an, NSURL which is what assets-library uses. + NSString *phAssetID = [imageURL.absoluteString substringFromIndex:[@"ph://" length]]; + PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil]; + if (results.count == 0) { + NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID]; + NSError *error = RCTErrorWithMessage(errorText); + completionHandler(error, nil); + return ^{}; + } + + PHAsset *asset = [results firstObject]; + + PHImageRequestOptions *imageOptions = [PHImageRequestOptions new]; + imageOptions.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { + static const double multiplier = 1e6; + progressHandler(progress * multiplier, multiplier); + }; + + BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); + CGSize targetSize; + + if (useMaximumSize) { + targetSize = PHImageManagerMaximumSize; + imageOptions.resizeMode = PHImageRequestOptionsResizeModeNone; + } else { + targetSize = size; + imageOptions.resizeMode = PHImageRequestOptionsResizeModeFast; + } + + PHImageContentMode contentMode = PHImageContentModeAspectFill; + if (resizeMode == UIViewContentModeScaleAspectFit) { + contentMode = PHImageContentModeAspectFit; + } + + PHImageRequestID requestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:imageOptions resultHandler:^(UIImage *result, NSDictionary *info) { + if (result) { + completionHandler(nil, result); + } else { + completionHandler(info[PHImageErrorKey], nil); + } + }]; + + return ^{ + [[PHImageManager defaultManager] cancelImageRequest:requestID]; + }; +} + +@end diff --git a/Libraries/LinkingIOS/LinkingIOS.js b/Libraries/LinkingIOS/LinkingIOS.js index 84960ff32..494091190 100644 --- a/Libraries/LinkingIOS/LinkingIOS.js +++ b/Libraries/LinkingIOS/LinkingIOS.js @@ -30,7 +30,7 @@ var DEVICE_NOTIF_EVENT = 'openURL'; * * #### Handling deep links * - * If your app was launched from a external url registered to your app you can + * If your app was launched from an external url registered to your app you can * access and handle it from any component you want with * * ``` @@ -127,7 +127,7 @@ class LinkingIOS { } /** - * Determine wether or not the an installed app can handle a given `url` + * Determine wether or not an installed app can handle a given `url` * The callback function will be called with `bool supported` as the only argument */ static canOpenURL(url: string, callback: Function) { diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index bcae5fe0a..d21360ed2 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -35,6 +35,7 @@ _textView = [[UITextView alloc] initWithFrame:self.bounds]; _textView.backgroundColor = [UIColor clearColor]; + _textView.scrollsToTop = NO; _textView.delegate = self; [self addSubview:_textView]; } diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 5ef4c6502..3151d8005 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -664,22 +664,23 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR // verify that class has been registered (void)_modulesByName[moduleData.name]; } - NSMutableOrderedSet *set = [buckets objectForKey:moduleData]; + id queue = [moduleData queue]; + NSMutableOrderedSet *set = [buckets objectForKey:queue]; if (!set) { set = [NSMutableOrderedSet new]; - [buckets setObject:set forKey:moduleData]; + [buckets setObject:set forKey:queue]; } [set addObject:@(i)]; } - for (RCTModuleData *moduleData in buckets) { + for (id queue in buckets) { RCTProfileBeginFlowEvent(); - [moduleData dispatchBlock:^{ + dispatch_block_t block = ^{ RCTProfileEndFlowEvent(); RCTProfileBeginEvent(0, RCTCurrentThreadName(), nil); - NSOrderedSet *calls = [buckets objectForKey:moduleData]; + NSOrderedSet *calls = [buckets objectForKey:queue]; @autoreleasepool { for (NSNumber *indexObj in calls) { NSUInteger index = indexObj.unsignedIntegerValue; @@ -693,7 +694,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR RCTProfileEndEvent(0, @"objc_call,dispatch_async", @{ @"calls": @(calls.count), }); - }]; + }; + + if (queue == RCTJSThread) { + [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; + } else if (queue) { + dispatch_async(queue, block); + } } // TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case? diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 0aa73c68d..abc71b201 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -38,7 +38,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) // Handle general request errors if (error) { if ([error.domain isEqualToString:NSURLErrorDomain]) { - NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:scriptURL.absoluteString]; + NSString *desc = [@"Could not connect to development server.\n\nEnsure the following:\n- Node server is running and available on the same network - run 'npm start' from react-native root\n- Node server URL is correctly set in AppDelegate\n\nURL: " stringByAppendingString:scriptURL.absoluteString]; NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: desc, NSLocalizedFailureReasonErrorKey: error.localizedDescription, diff --git a/React/Base/RCTKeyboardObserver.m b/React/Base/RCTKeyboardObserver.m index 5b0174cdf..fbe4e4f76 100644 --- a/React/Base/RCTKeyboardObserver.m +++ b/React/Base/RCTKeyboardObserver.m @@ -62,7 +62,7 @@ IMPLEMENT_KEYBOARD_HANDLER(keyboardDidChangeFrame) @end -static NSDictionary *RCTRectDictionaryValue(CGRect rect) +NS_INLINE NSDictionary *RCTRectDictionaryValue(CGRect rect) { return @{ @"screenX": @(rect.origin.x), @@ -72,16 +72,34 @@ static NSDictionary *RCTRectDictionaryValue(CGRect rect) }; } +static NSString *RCTAnimationNameForCurve(UIViewAnimationCurve curve) +{ + switch (curve) { + case UIViewAnimationCurveEaseIn: + return @"easeIn"; + case UIViewAnimationCurveEaseInOut: + return @"easeInEaseOut"; + case UIViewAnimationCurveEaseOut: + return @"easeOut"; + case UIViewAnimationCurveLinear: + return @"linear"; + default: + return @"keyboard"; + } +} + static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification) { NSDictionary *userInfo = notification.userInfo; CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; return @{ @"startCoordinates": RCTRectDictionaryValue(beginFrame), @"endCoordinates": RCTRectDictionaryValue(endFrame), @"duration": @(duration * 1000.0), // ms + @"easing": RCTAnimationNameForCurve(curve), }; } diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index edc3846a0..5206cabef 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -66,13 +66,15 @@ RCT_EXPORT_MODULE() - (void)setUp { if (!_webView) { - _webView = [UIWebView new]; + [self executeBlockOnJavaScriptQueue:^{ + _webView = [UIWebView new]; + _webView.delegate = self; + }]; } _objectsToInject = [NSMutableDictionary new]; - _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL], - _scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL], - _webView.delegate = self; + _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL]; + _scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL]; } - (void)invalidate diff --git a/React/Modules/RCTSourceCode.m b/React/Modules/RCTSourceCode.m index 05860e1dd..685ae611a 100644 --- a/React/Modules/RCTSourceCode.m +++ b/React/Modules/RCTSourceCode.m @@ -34,4 +34,10 @@ RCT_EXPORT_METHOD(getScriptText:(RCTResponseSenderBlock)successCallback } } +- (NSDictionary *)constantsToExport +{ + NSString *URL = self.bridge.bundleURL.absoluteString ?: @""; + return @{@"scriptURL": URL}; +} + @end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 9af3a6ceb..9acaff806 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -448,26 +448,12 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); NSMutableArray *frames = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *areNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *parentsAreNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableArray *onLayoutEvents = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; for (RCTShadowView *shadowView in viewsWithNewFrames) { [frameReactTags addObject:shadowView.reactTag]; [frames addObject:[NSValue valueWithCGRect:shadowView.frame]]; [areNew addObject:@(shadowView.isNewView)]; [parentsAreNew addObject:@(shadowView.superview.isNewView)]; - id event = (id)kCFNull; - if (shadowView.onLayout) { - event = @{ - @"target": shadowView.reactTag, - @"layout": @{ - @"x": @(shadowView.frame.origin.x), - @"y": @(shadowView.frame.origin.y), - @"width": @(shadowView.frame.size.width), - @"height": @(shadowView.frame.size.height), - }, - }; - } - [onLayoutEvents addObject:event]; } for (RCTShadowView *shadowView in viewsWithNewFrames) { @@ -483,7 +469,20 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); for (RCTShadowView *shadowView in viewsWithNewFrames) { RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager]; RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView]; - if (block) [updateBlocks addObject:block]; + if (shadowView.onLayout) { + CGRect frame = shadowView.frame; + shadowView.onLayout(@{ + @"layout": @{ + @"x": @(frame.origin.x), + @"y": @(frame.origin.y), + @"width": @(frame.size.width), + @"height": @(frame.size.height), + }, + }); + } + if (block) { + [updateBlocks addObject:block]; + } } // Perform layout (possibly animated) @@ -494,7 +493,6 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); NSNumber *reactTag = frameReactTags[ii]; UIView *view = viewRegistry[reactTag]; CGRect frame = [frames[ii] CGRectValue]; - id event = onLayoutEvents[ii]; BOOL isNew = [areNew[ii] boolValue]; RCTAnimation *updateAnimation = isNew ? nil : _layoutAnimation.updateAnimation; @@ -503,9 +501,6 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); void (^completion)(BOOL) = ^(BOOL finished) { completionsCalled++; - if (event != (id)kCFNull) { - [self.bridge.eventDispatcher sendInputEventWithName:@"layout" body:event]; - } if (callback && completionsCalled == frames.count - 1) { callback(@[@(finished)]); } @@ -1128,67 +1123,68 @@ RCT_EXPORT_METHOD(clearJSResponder) }]; } -- (NSDictionary *)bubblingEventsConfig -{ - NSMutableDictionary *customBubblingEventTypesConfigs = [NSMutableDictionary new]; - for (RCTComponentData *componentData in _componentDataByName.allValues) { - RCTViewManager *manager = componentData.manager; - if (RCTClassOverridesInstanceMethod([manager class], @selector(customBubblingEventTypes))) { - NSArray *events = [manager customBubblingEventTypes]; - if (RCT_DEBUG) { - RCTAssert(!events || [events isKindOfClass:[NSArray class]], - @"customBubblingEventTypes must return an array, but %@ returned %@", - [manager class], [events class]); - } - for (NSString *eventName in events) { - NSString *topName = RCTNormalizeInputEventName(eventName); - if (!customBubblingEventTypesConfigs[topName]) { - NSString *bubbleName = [topName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"]; - customBubblingEventTypesConfigs[topName] = @{ - @"phasedRegistrationNames": @{ - @"bubbled": bubbleName, - @"captured": [bubbleName stringByAppendingString:@"Capture"], - } - }; - } - } - } - }; - - return customBubblingEventTypesConfigs; -} - -- (NSDictionary *)directEventsConfig -{ - NSMutableDictionary *customDirectEventTypes = [NSMutableDictionary new]; - for (RCTComponentData *componentData in _componentDataByName.allValues) { - RCTViewManager *manager = componentData.manager; - if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) { - NSArray *events = [manager customDirectEventTypes]; - if (RCT_DEBUG) { - RCTAssert(!events || [events isKindOfClass:[NSArray class]], - @"customDirectEventTypes must return an array, but %@ returned %@", - [manager class], [events class]); - } - for (NSString *eventName in events) { - NSString *topName = RCTNormalizeInputEventName(eventName); - if (!customDirectEventTypes[topName]) { - customDirectEventTypes[topName] = @{ - @"registrationName": [topName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"], - }; - } - } - } - }; - - return customDirectEventTypes; -} - - (NSDictionary *)constantsToExport { - NSMutableDictionary *allJSConstants = [@{ - @"customBubblingEventTypes": [self bubblingEventsConfig], - @"customDirectEventTypes": [self directEventsConfig], + NSMutableDictionary *allJSConstants = [NSMutableDictionary new]; + NSMutableDictionary *directEvents = [NSMutableDictionary new]; + NSMutableDictionary *bubblingEvents = [NSMutableDictionary new]; + + [_componentDataByName enumerateKeysAndObjectsUsingBlock: + ^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) { + + RCTViewManager *manager = componentData.manager; + NSMutableDictionary *constantsNamespace = + [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; + + // Add custom constants + // TODO: should these be inherited? + NSDictionary *constants = RCTClassOverridesInstanceMethod([manager class], @selector(constantsToExport)) ? [manager constantsToExport] : nil; + if (constants.count) { + RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name); + // add an additional 'Constants' namespace for each class + constantsNamespace[@"Constants"] = constants; + } + + // Add native props + NSDictionary *viewConfig = [componentData viewConfig]; + constantsNamespace[@"NativeProps"] = viewConfig[@"propTypes"]; + + // Add direct events + for (NSString *eventName in viewConfig[@"directEvents"]) { + if (!directEvents[eventName]) { + directEvents[eventName] = @{ + @"registrationName": [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"], + }; + } + if (RCT_DEBUG && bubblingEvents[eventName]) { + RCTLogError(@"Component '%@' re-registered bubbling event '%@' as a " + "direct event", componentData.name, eventName); + } + } + + // Add bubbling events + for (NSString *eventName in viewConfig[@"bubblingEvents"]) { + if (!bubblingEvents[eventName]) { + NSString *bubbleName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"]; + bubblingEvents[eventName] = @{ + @"phasedRegistrationNames": @{ + @"bubbled": bubbleName, + @"captured": [bubbleName stringByAppendingString:@"Capture"], + } + }; + } + if (RCT_DEBUG && directEvents[eventName]) { + RCTLogError(@"Component '%@' re-registered direct event '%@' as a " + "bubbling event", componentData.name, eventName); + } + } + + allJSConstants[name] = [constantsNamespace copy]; + }]; + + [allJSConstants addEntriesFromDictionary:@{ + @"customBubblingEventTypes": bubblingEvents, + @"customDirectEventTypes": directEvents, @"Dimensions": @{ @"window": @{ @"width": @(RCTScreenSize().width), @@ -1200,28 +1196,8 @@ RCT_EXPORT_METHOD(clearJSResponder) @"height": @(RCTScreenSize().height), }, }, - } mutableCopy]; - - [_componentDataByName enumerateKeysAndObjectsUsingBlock: - ^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) { - RCTViewManager *manager = componentData.manager; - NSMutableDictionary *constantsNamespace = - [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; - - // Add custom constants - // TODO: should these be inherited? - NSDictionary *constants = RCTClassOverridesInstanceMethod([manager class], @selector(constantsToExport)) ? [manager constantsToExport] : nil; - if (constants.count) { - RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name); - // add an additional 'Constants' namespace for each class - constantsNamespace[@"Constants"] = constants; - } - - // Add native props - constantsNamespace[@"NativeProps"] = [componentData viewConfig]; - - allJSConstants[name] = [constantsNamespace copy]; }]; + return allJSConstants; } diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 97a278353..ce803446d 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */; }; 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */; }; + 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */; }; 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; }; 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; }; 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; }; @@ -104,6 +105,8 @@ 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControl.m; sourceTree = ""; }; 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControlManager.h; sourceTree = ""; }; 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControlManager.m; sourceTree = ""; }; + 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePicker.h; sourceTree = ""; }; + 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePicker.m; sourceTree = ""; }; 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; }; 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; }; 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; }; @@ -345,6 +348,8 @@ 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */, 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */, 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */, + 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */, + 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */, 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */, 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */, 14435CE11AAC4AE100FC20F4 /* RCTMap.h */, @@ -597,6 +602,7 @@ 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */, 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */, 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */, + 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */, 14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */, 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */, 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */, diff --git a/React/Views/RCTComponent.h b/React/Views/RCTComponent.h index bc73ac8d2..215acb1d0 100644 --- a/React/Views/RCTComponent.h +++ b/React/Views/RCTComponent.h @@ -9,6 +9,13 @@ #import +/** + * These block types can be used for mapping input event handlers from JS to view + * properties. Unlike JS method callbacks, these can be called multiple times. + */ +typedef void (^RCTDirectEventBlock)(NSDictionary *body); +typedef void (^RCTBubblingEventBlock)(NSDictionary *body); + /** * Logical node in a tree of application components. Both `ShadowView` and * `UIView` conforms to this. Allows us to write utilities that reason about diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index b01b0268e..c6fd94127 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -13,6 +13,7 @@ #import "RCTBridge.h" #import "RCTShadowView.h" +#import "RCTUtils.h" #import "RCTViewManager.h" typedef void (^RCTPropBlock)(id view, id json); @@ -140,77 +141,95 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) // Build setter block void (^setterBlock)(id target, id source, id json) = nil; - NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type]; - switch (typeSignature.methodReturnType[0]) { + if (type == NSSelectorFromString(@"RCTBubblingEventBlock:") || + type == NSSelectorFromString(@"RCTDirectEventBlock:")) { -#define RCT_CASE(_value, _type) \ - case _value: { \ - _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ - _type (*get)(id, SEL) = (typeof(get))objc_msgSend; \ - void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ - setterBlock = ^(id target, id source, id json) { \ - set(target, setter, json ? convert([RCTConvert class], type, json) : get(source, getter)); \ - }; \ - break; \ - } + // Special case for event handlers + __weak RCTViewManager *weakManager = _manager; + setterBlock = ^(id target, __unused id source, id json) { + __weak id weakTarget = target; + ((void (*)(id, SEL, id))objc_msgSend)(target, setter, [RCTConvert BOOL:json] ? ^(NSDictionary *body) { + body = [NSMutableDictionary dictionaryWithDictionary:body]; + ((NSMutableDictionary *)body)[@"target"] = weakTarget.reactTag; + [weakManager.bridge.eventDispatcher sendInputEventWithName:RCTNormalizeInputEventName(name) body:body]; + } : nil); + }; - RCT_CASE(_C_SEL, SEL) - RCT_CASE(_C_CHARPTR, const char *) - RCT_CASE(_C_CHR, char) - RCT_CASE(_C_UCHR, unsigned char) - RCT_CASE(_C_SHT, short) - RCT_CASE(_C_USHT, unsigned short) - RCT_CASE(_C_INT, int) - RCT_CASE(_C_UINT, unsigned int) - RCT_CASE(_C_LNG, long) - RCT_CASE(_C_ULNG, unsigned long) - RCT_CASE(_C_LNG_LNG, long long) - RCT_CASE(_C_ULNG_LNG, unsigned long long) - RCT_CASE(_C_FLT, float) - RCT_CASE(_C_DBL, double) - RCT_CASE(_C_BOOL, BOOL) - RCT_CASE(_C_PTR, void *) - RCT_CASE(_C_ID, id) + } else { - case _C_STRUCT_B: - default: { + // Ordinary property handlers + NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type]; + switch (typeSignature.methodReturnType[0]) { - NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; - typeInvocation.selector = type; - typeInvocation.target = [RCTConvert class]; + #define RCT_CASE(_value, _type) \ + case _value: { \ + _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ + _type (*get)(id, SEL) = (typeof(get))objc_msgSend; \ + void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ + setterBlock = ^(id target, id source, id json) { \ + set(target, setter, json ? convert([RCTConvert class], type, json) : get(source, getter)); \ + }; \ + break; \ + } - __block NSInvocation *sourceInvocation = nil; - __block NSInvocation *targetInvocation = nil; + RCT_CASE(_C_SEL, SEL) + RCT_CASE(_C_CHARPTR, const char *) + RCT_CASE(_C_CHR, char) + RCT_CASE(_C_UCHR, unsigned char) + RCT_CASE(_C_SHT, short) + RCT_CASE(_C_USHT, unsigned short) + RCT_CASE(_C_INT, int) + RCT_CASE(_C_UINT, unsigned int) + RCT_CASE(_C_LNG, long) + RCT_CASE(_C_ULNG, unsigned long) + RCT_CASE(_C_LNG_LNG, long long) + RCT_CASE(_C_ULNG_LNG, unsigned long long) + RCT_CASE(_C_FLT, float) + RCT_CASE(_C_DBL, double) + RCT_CASE(_C_BOOL, BOOL) + RCT_CASE(_C_PTR, void *) + RCT_CASE(_C_ID, id) - setterBlock = ^(id target, id source, id json) { \ + case _C_STRUCT_B: + default: { - // Get value - void *value = malloc(typeSignature.methodReturnLength); - if (json) { - [typeInvocation setArgument:&json atIndex:2]; - [typeInvocation invoke]; - [typeInvocation getReturnValue:value]; - } else { - if (!sourceInvocation && source) { - NSMethodSignature *signature = [source methodSignatureForSelector:getter]; - sourceInvocation = [NSInvocation invocationWithMethodSignature:signature]; - sourceInvocation.selector = getter; + NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; + typeInvocation.selector = type; + typeInvocation.target = [RCTConvert class]; + + __block NSInvocation *sourceInvocation = nil; + __block NSInvocation *targetInvocation = nil; + + setterBlock = ^(id target, id source, id json) { \ + + // Get value + void *value = malloc(typeSignature.methodReturnLength); + if (json) { + [typeInvocation setArgument:&json atIndex:2]; + [typeInvocation invoke]; + [typeInvocation getReturnValue:value]; + } else { + if (!sourceInvocation && source) { + NSMethodSignature *signature = [source methodSignatureForSelector:getter]; + sourceInvocation = [NSInvocation invocationWithMethodSignature:signature]; + sourceInvocation.selector = getter; + } + [sourceInvocation invokeWithTarget:source]; + [sourceInvocation getReturnValue:value]; } - [sourceInvocation invokeWithTarget:source]; - [sourceInvocation getReturnValue:value]; - } - // Set value - if (!targetInvocation && target) { - NSMethodSignature *signature = [target methodSignatureForSelector:setter]; - targetInvocation = [NSInvocation invocationWithMethodSignature:signature]; - targetInvocation.selector = setter; - } - [targetInvocation setArgument:value atIndex:2]; - [targetInvocation invokeWithTarget:target]; - free(value); - }; - break; + // Set value + if (!targetInvocation && target) { + NSMethodSignature *signature = [target methodSignatureForSelector:setter]; + targetInvocation = [NSInvocation invocationWithMethodSignature:signature]; + targetInvocation.selector = setter; + } + [targetInvocation setArgument:value atIndex:2]; + [targetInvocation invokeWithTarget:target]; + free(value); + }; + break; + } } } @@ -292,9 +311,35 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) - (NSDictionary *)viewConfig { Class managerClass = [_manager class]; - NSMutableDictionary *propTypes = [NSMutableDictionary new]; + + NSMutableArray *directEvents = [NSMutableArray new]; + if (RCTClassOverridesInstanceMethod(managerClass, @selector(customDirectEventTypes))) { + NSArray *events = [_manager customDirectEventTypes]; + if (RCT_DEBUG) { + RCTAssert(!events || [events isKindOfClass:[NSArray class]], + @"customDirectEventTypes must return an array, but %@ returned %@", + managerClass, [events class]); + } + for (NSString *event in events) { + [directEvents addObject:RCTNormalizeInputEventName(event)]; + } + } + + NSMutableArray *bubblingEvents = [NSMutableArray new]; + if (RCTClassOverridesInstanceMethod(managerClass, @selector(customBubblingEventTypes))) { + NSArray *events = [_manager customBubblingEventTypes]; + if (RCT_DEBUG) { + RCTAssert(!events || [events isKindOfClass:[NSArray class]], + @"customBubblingEventTypes must return an array, but %@ returned %@", + managerClass, [events class]); + } + for (NSString *event in events) { + [bubblingEvents addObject:RCTNormalizeInputEventName(event)]; + } + } unsigned int count = 0; + NSMutableDictionary *propTypes = [NSMutableDictionary new]; Method *methods = class_copyMethodList(object_getClass(managerClass), &count); for (unsigned int i = 0; i < count; i++) { Method method = methods[i]; @@ -309,13 +354,41 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) RCTLogError(@"Property '%@' of component '%@' redefined from '%@' " "to '%@'", name, _name, propTypes[name], type); } - propTypes[name] = type; + + if ([type isEqualToString:@"RCTBubblingEventBlock"]) { + [bubblingEvents addObject:RCTNormalizeInputEventName(name)]; + propTypes[name] = @"BOOL"; + } else if ([type isEqualToString:@"RCTDirectEventBlock"]) { + [directEvents addObject:RCTNormalizeInputEventName(name)]; + propTypes[name] = @"BOOL"; + } else { + propTypes[name] = type; + } } } } free(methods); - return propTypes; + if (RCT_DEBUG) { + for (NSString *event in directEvents) { + if ([bubblingEvents containsObject:event]) { + RCTLogError(@"Component '%@' registered '%@' as both a bubbling event " + "and a direct event", _name, event); + } + } + for (NSString *event in bubblingEvents) { + if ([directEvents containsObject:event]) { + RCTLogError(@"Component '%@' registered '%@' as both a bubbling event " + "and a direct event", _name, event); + } + } + } + + return @{ + @"propTypes" : propTypes, + @"directEvents" : directEvents, + @"bubblingEvents" : bubblingEvents, + }; } @end diff --git a/React/Views/RCTDatePicker.h b/React/Views/RCTDatePicker.h new file mode 100644 index 000000000..f036651b4 --- /dev/null +++ b/React/Views/RCTDatePicker.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface RCTDatePicker : UIDatePicker + +@end diff --git a/React/Views/RCTDatePicker.m b/React/Views/RCTDatePicker.m new file mode 100644 index 000000000..0e392dcec --- /dev/null +++ b/React/Views/RCTDatePicker.m @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTDatePicker.h" + +#import "RCTUtils.h" +#import "UIView+React.h" + +@interface RCTDatePicker () + +@property (nonatomic, copy) RCTBubblingEventBlock onChange; + +@end + +@implementation RCTDatePicker + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + [self addTarget:self action:@selector(didChange) + forControlEvents:UIControlEventValueChanged]; + } + return self; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) + +- (void)didChange +{ + if (_onChange) { + _onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0) }); + } +} + +@end diff --git a/React/Views/RCTDatePickerManager.m b/React/Views/RCTDatePickerManager.m index f2bef39e3..ef9515e5a 100644 --- a/React/Views/RCTDatePickerManager.m +++ b/React/Views/RCTDatePickerManager.m @@ -10,6 +10,7 @@ #import "RCTDatePickerManager.h" #import "RCTBridge.h" +#import "RCTDatePicker.h" #import "RCTEventDispatcher.h" #import "UIView+React.h" @@ -30,14 +31,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - // TODO: we crash here if the RCTDatePickerManager is released - // while the UIDatePicker is still sending onChange events. To - // fix this we should maybe subclass UIDatePicker and make it - // be its own event target. - UIDatePicker *picker = [UIDatePicker new]; - [picker addTarget:self action:@selector(onChange:) - forControlEvents:UIControlEventValueChanged]; - return picker; + return [RCTDatePicker new]; } RCT_EXPORT_VIEW_PROPERTY(date, NSDate) @@ -47,15 +41,6 @@ RCT_EXPORT_VIEW_PROPERTY(minuteInterval, NSInteger) RCT_REMAP_VIEW_PROPERTY(mode, datePickerMode, UIDatePickerMode) RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone) -- (void)onChange:(UIDatePicker *)sender -{ - NSDictionary *event = @{ - @"target": sender.reactTag, - @"timestamp": @(sender.date.timeIntervalSince1970 * 1000.0) - }; - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event]; -} - - (NSDictionary *)constantsToExport { UIDatePicker *view = [UIDatePicker new]; diff --git a/React/Views/RCTMap.h b/React/Views/RCTMap.h index 41cc13a12..65e6aec94 100644 --- a/React/Views/RCTMap.h +++ b/React/Views/RCTMap.h @@ -11,13 +11,12 @@ #import #import "RCTConvert+MapKit.h" +#import "RCTComponent.h" extern const CLLocationDegrees RCTMapDefaultSpan; extern const NSTimeInterval RCTMapRegionChangeObserveInterval; extern const CGFloat RCTMapZoomBoundBuffer; -@class RCTEventDispatcher; - @interface RCTMap: MKMapView @property (nonatomic, assign) BOOL followUserLocation; @@ -28,6 +27,9 @@ extern const CGFloat RCTMapZoomBoundBuffer; @property (nonatomic, strong) NSTimer *regionChangeObserveTimer; @property (nonatomic, strong) NSMutableArray *annotationIds; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; +@property (nonatomic, copy) RCTBubblingEventBlock onPress; + - (void)setAnnotations:(RCTPointAnnotationArray *)annotations; @end diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m index 130446df0..f4ee06f98 100644 --- a/React/Views/RCTMapManager.m +++ b/React/Views/RCTMapManager.m @@ -46,6 +46,8 @@ RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat) RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets) RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType) RCT_EXPORT_VIEW_PROPERTY(annotations, RCTPointAnnotationArray) +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) { [view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES]; @@ -53,35 +55,27 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) #pragma mark MKMapViewDelegate - - -- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view +- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view { - if (![view.annotation isKindOfClass:[MKUserLocation class]]) { + if (mapView.onPress && [view.annotation isKindOfClass:[RCTPointAnnotation class]]) { RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation; - NSString *title = view.annotation.title ?: @""; - NSString *subtitle = view.annotation.subtitle ?: @""; - - NSDictionary *event = @{ - @"target": mapView.reactTag, - @"action": @"annotation-click", - @"annotation": @{ - @"id": annotation.identifier, - @"title": title, - @"subtitle": subtitle, - @"latitude": @(annotation.coordinate.latitude), - @"longitude": @(annotation.coordinate.longitude) - } - }; - - [self.bridge.eventDispatcher sendInputEventWithName:@"press" body:event]; + mapView.onPress(@{ + @"action": @"annotation-click", + @"annotation": @{ + @"id": annotation.identifier, + @"title": annotation.title ?: @"", + @"subtitle": annotation.subtitle ?: @"", + @"latitude": @(annotation.coordinate.latitude), + @"longitude": @(annotation.coordinate.longitude) + } + }); } } - (MKAnnotationView *)mapView:(__unused MKMapView *)mapView viewForAnnotation:(RCTPointAnnotation *)annotation { - if ([annotation isKindOfClass:[MKUserLocation class]]) { + if (![annotation isKindOfClass:[RCTPointAnnotation class]]) { return nil; } @@ -103,23 +97,20 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) return annotationView; } -- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control +- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { - // Pass to js - RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation; - NSString *side = (control == view.leftCalloutAccessoryView) ? @"left" : @"right"; + if (mapView.onPress) { - NSDictionary *event = @{ - @"target": mapView.reactTag, - @"side": side, + // Pass to js + RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation; + mapView.onPress(@{ + @"side": (control == view.leftCalloutAccessoryView) ? @"left" : @"right", @"action": @"callout-click", @"annotationId": annotation.identifier - }; - - [self.bridge.eventDispatcher sendInputEventWithName:@"press" body:event]; + }); + } } - - (void)mapView:(RCTMap *)mapView didUpdateUserLocation:(MKUserLocation *)location { if (mapView.followUserLocation) { @@ -205,24 +196,24 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) - (void)_emitRegionChangeEvent:(RCTMap *)mapView continuous:(BOOL)continuous { - MKCoordinateRegion region = mapView.region; - if (!CLLocationCoordinate2DIsValid(region.center)) { - return; - } - -#define FLUSH_NAN(value) (isnan(value) ? 0 : value) - - NSDictionary *event = @{ - @"target": mapView.reactTag, - @"continuous": @(continuous), - @"region": @{ - @"latitude": @(FLUSH_NAN(region.center.latitude)), - @"longitude": @(FLUSH_NAN(region.center.longitude)), - @"latitudeDelta": @(FLUSH_NAN(region.span.latitudeDelta)), - @"longitudeDelta": @(FLUSH_NAN(region.span.longitudeDelta)), + if (mapView.onChange) { + MKCoordinateRegion region = mapView.region; + if (!CLLocationCoordinate2DIsValid(region.center)) { + return; } - }; - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event]; + + #define FLUSH_NAN(value) (isnan(value) ? 0 : value) + + mapView.onChange(@{ + @"continuous": @(continuous), + @"region": @{ + @"latitude": @(FLUSH_NAN(region.center.latitude)), + @"longitude": @(FLUSH_NAN(region.center.longitude)), + @"latitudeDelta": @(FLUSH_NAN(region.span.latitudeDelta)), + @"longitudeDelta": @(FLUSH_NAN(region.span.longitudeDelta)), + } + }); + } } @end diff --git a/React/Views/RCTNavItem.h b/React/Views/RCTNavItem.h index 9b75673c3..418d5c5c9 100644 --- a/React/Views/RCTNavItem.h +++ b/React/Views/RCTNavItem.h @@ -9,6 +9,8 @@ #import +#import "RCTComponent.h" + @interface RCTNavItem : UIView @property (nonatomic, copy) NSString *title; @@ -29,4 +31,7 @@ @property (nonatomic, readonly) UIBarButtonItem *leftButtonItem; @property (nonatomic, readonly) UIBarButtonItem *rightButtonItem; +@property (nonatomic, copy) RCTBubblingEventBlock onNavLeftButtonTap; +@property (nonatomic, copy) RCTBubblingEventBlock onNavRightButtonTap; + @end diff --git a/React/Views/RCTNavItem.m b/React/Views/RCTNavItem.m index f875e6aa5..5e4043f84 100644 --- a/React/Views/RCTNavItem.m +++ b/React/Views/RCTNavItem.m @@ -63,15 +63,18 @@ { if (!_leftButtonItem) { if (_leftButtonIcon) { - _leftButtonItem = [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _leftButtonItem = + [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavLeftButtonTapped)]; + } else if (_leftButtonTitle.length) { - _leftButtonItem = [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _leftButtonItem = + [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavLeftButtonTapped)]; } else { _leftButtonItem = nil; } @@ -79,6 +82,13 @@ return _leftButtonItem; } +- (void)handleNavLeftButtonTapped +{ + if (_onNavLeftButtonTap) { + _onNavLeftButtonTap(nil); + } +} + - (void)setRightButtonTitle:(NSString *)rightButtonTitle { _rightButtonTitle = rightButtonTitle; @@ -95,15 +105,18 @@ { if (!_rightButtonItem) { if (_rightButtonIcon) { - _rightButtonItem = [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _rightButtonItem = + [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavRightButtonTapped)]; + } else if (_rightButtonTitle.length) { - _rightButtonItem = [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _rightButtonItem = + [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavRightButtonTapped)]; } else { _rightButtonItem = nil; } @@ -111,4 +124,11 @@ return _rightButtonItem; } +- (void)handleNavRightButtonTapped +{ + if (_onNavRightButtonTap) { + _onNavRightButtonTap(nil); + } +} + @end diff --git a/React/Views/RCTNavItemManager.m b/React/Views/RCTNavItemManager.m index f785934af..040b7ffce 100644 --- a/React/Views/RCTNavItemManager.m +++ b/React/Views/RCTNavItemManager.m @@ -39,4 +39,7 @@ RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage) RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage) RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString) +RCT_EXPORT_VIEW_PROPERTY(onNavLeftButtonTap, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onNavRightButtonTap, RCTBubblingEventBlock) + @end diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index fcbc14225..a79fe0992 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -193,6 +193,9 @@ NSInteger kNeverProgressed = -10000; @interface RCTNavigator() +@property (nonatomic, copy) RCTDirectEventBlock onNavigationProgress; +@property (nonatomic, copy) RCTBubblingEventBlock onNavigationComplete; + @property (nonatomic, assign) NSInteger previousRequestedTopOfStack; // Previous views are only mainted in order to detect incorrect @@ -308,12 +311,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) return; } _mostRecentProgress = nextProgress; - [_bridge.eventDispatcher sendInputEventWithName:@"navigationProgress" body:@{ - @"fromIndex": @(_currentlyTransitioningFrom), - @"toIndex": @(_currentlyTransitioningTo), - @"progress": @(nextProgress), - @"target": self.reactTag - }]; + if (_onNavigationProgress) { + _onNavigationProgress(@{ + @"fromIndex": @(_currentlyTransitioningFrom), + @"toIndex": @(_currentlyTransitioningTo), + @"progress": @(nextProgress), + }); + } } } @@ -416,10 +420,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)handleTopOfStackChanged { - [_bridge.eventDispatcher sendInputEventWithName:@"navigationComplete" body:@{ - @"target":self.reactTag, - @"stackLength":@(_navigationController.viewControllers.count) - }]; + if (_onNavigationComplete) { + _onNavigationComplete(@{ + @"stackLength":@(_navigationController.viewControllers.count) + }); + } } - (void)dispatchFakeScrollEvent @@ -502,7 +507,7 @@ BOOL jsGettingtooSlow = if (jsGettingAhead) { if (reactPushOne) { UIView *lastView = _currentViews.lastObject; - RCTWrapperViewController *vc = [[RCTWrapperViewController alloc] initWithNavItem:(RCTNavItem *)lastView eventDispatcher:_bridge.eventDispatcher]; + RCTWrapperViewController *vc = [[RCTWrapperViewController alloc] initWithNavItem:(RCTNavItem *)lastView]; vc.navigationListener = self; _numberOfViewControllerMovesToIgnore = 1; [_navigationController pushViewController:vc animated:(currentReactCount > 1)]; diff --git a/React/Views/RCTNavigatorManager.m b/React/Views/RCTNavigatorManager.m index 2627d0f64..0e900a14c 100644 --- a/React/Views/RCTNavigatorManager.m +++ b/React/Views/RCTNavigatorManager.m @@ -25,22 +25,8 @@ RCT_EXPORT_MODULE() } RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger) - -- (NSArray *)customBubblingEventTypes -{ - return @[ - @"navigationComplete", - @"navLeftButtonTap", - @"navRightButtonTap", - ]; -} - -- (NSArray *)customDirectEventTypes -{ - return @[ - @"navigationProgress", - ]; -} +RCT_EXPORT_VIEW_PROPERTY(onNavigationProgress, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onNavigationComplete, RCTBubblingEventBlock) // TODO: remove error callbacks RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(nonnull NSNumber *)reactTag diff --git a/React/Views/RCTPicker.h b/React/Views/RCTPicker.h index 9f6a486de..704fe7587 100644 --- a/React/Views/RCTPicker.h +++ b/React/Views/RCTPicker.h @@ -9,13 +9,6 @@ #import -@class RCTEventDispatcher; - @interface RCTPicker : UIPickerView -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - -@property (nonatomic, copy) NSArray *items; -@property (nonatomic, assign) NSInteger selectedIndex; - @end diff --git a/React/Views/RCTPicker.m b/React/Views/RCTPicker.m index 09c7247d2..1f256cc48 100644 --- a/React/Views/RCTPicker.m +++ b/React/Views/RCTPicker.m @@ -9,37 +9,28 @@ #import "RCTPicker.h" -#import "RCTConvert.h" -#import "RCTEventDispatcher.h" #import "RCTUtils.h" #import "UIView+React.h" -const NSInteger UNINITIALIZED_INDEX = -1; - @interface RCTPicker() +@property (nonatomic, copy) NSArray *items; +@property (nonatomic, assign) NSInteger selectedIndex; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; + @end @implementation RCTPicker -{ - RCTEventDispatcher *_eventDispatcher; - NSArray *_items; - NSInteger _selectedIndex; -} -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - RCTAssertParam(eventDispatcher); - - if ((self = [super initWithFrame:CGRectZero])) { - _eventDispatcher = eventDispatcher; - _selectedIndex = UNINITIALIZED_INDEX; + if ((self = [super initWithFrame:frame])) { + _selectedIndex = NSNotFound; self.delegate = self; } return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)setItems:(NSArray *)items @@ -51,7 +42,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)setSelectedIndex:(NSInteger)selectedIndex { if (_selectedIndex != selectedIndex) { - BOOL animated = _selectedIndex != UNINITIALIZED_INDEX; // Don't animate the initial value + BOOL animated = _selectedIndex != NSNotFound; // Don't animate the initial value _selectedIndex = selectedIndex; dispatch_async(dispatch_get_main_queue(), ^{ [self selectRow:selectedIndex inComponent:0 animated:animated]; @@ -94,13 +85,12 @@ numberOfRowsInComponent:(__unused NSInteger)component didSelectRow:(NSInteger)row inComponent:(__unused NSInteger)component { _selectedIndex = row; - NSDictionary *event = @{ - @"target": self.reactTag, - @"newIndex": @(row), - @"newValue": [self valueForRow:row] - }; - - [_eventDispatcher sendInputEventWithName:@"change" body:event]; + if (_onChange) { + _onChange(@{ + @"newIndex": @(row), + @"newValue": [self valueForRow:row] + }); + } } @end diff --git a/React/Views/RCTPickerManager.m b/React/Views/RCTPickerManager.m index 6de76bcb7..8d1e18120 100644 --- a/React/Views/RCTPickerManager.m +++ b/React/Views/RCTPickerManager.m @@ -19,7 +19,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTPicker alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTPicker new]; } RCT_EXPORT_VIEW_PROPERTY(items, NSDictionaryArray) diff --git a/React/Views/RCTSegmentedControl.h b/React/Views/RCTSegmentedControl.h index 3e95735bd..500a236e9 100644 --- a/React/Views/RCTSegmentedControl.h +++ b/React/Views/RCTSegmentedControl.h @@ -9,13 +9,12 @@ #import -@class RCTEventDispatcher; +#import "RCTComponent.h" @interface RCTSegmentedControl : UISegmentedControl -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - @property (nonatomic, copy) NSArray *values; @property (nonatomic, assign) NSInteger selectedIndex; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; @end diff --git a/React/Views/RCTSegmentedControl.m b/React/Views/RCTSegmentedControl.m index 1b28bacc7..543de9c4f 100644 --- a/React/Views/RCTSegmentedControl.m +++ b/React/Views/RCTSegmentedControl.m @@ -14,17 +14,13 @@ #import "UIView+React.h" @implementation RCTSegmentedControl -{ - RCTEventDispatcher *_eventDispatcher; -} -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - if ((self = [super initWithFrame:CGRectZero])) { - _eventDispatcher = eventDispatcher; + if ((self = [super initWithFrame:frame])) { _selectedIndex = self.selectedSegmentIndex; - [self addTarget:self action:@selector(onChange:) - forControlEvents:UIControlEventValueChanged]; + [self addTarget:self action:@selector(didChange) + forControlEvents:UIControlEventValueChanged]; } return self; } @@ -45,14 +41,14 @@ super.selectedSegmentIndex = selectedIndex; } -- (void)onChange:(UISegmentedControl *)sender +- (void)didChange { - NSDictionary *event = @{ - @"target": self.reactTag, - @"value": [self titleForSegmentAtIndex:sender.selectedSegmentIndex], - @"selectedSegmentIndex": @(sender.selectedSegmentIndex) - }; - [_eventDispatcher sendInputEventWithName:@"change" body:event]; + if (_onChange) { + _onChange(@{ + @"value": [self titleForSegmentAtIndex:_selectedIndex], + @"selectedSegmentIndex": @(_selectedIndex) + }); + } } @end diff --git a/React/Views/RCTSegmentedControlManager.m b/React/Views/RCTSegmentedControlManager.m index 1c5621e7a..cbe9f4a4b 100644 --- a/React/Views/RCTSegmentedControlManager.m +++ b/React/Views/RCTSegmentedControlManager.m @@ -19,7 +19,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTSegmentedControl alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTSegmentedControl new]; } RCT_EXPORT_VIEW_PROPERTY(values, NSStringArray) @@ -27,6 +27,7 @@ RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger) RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(momentary, BOOL) RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL) +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) - (NSDictionary *)constantsToExport { diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 7211b17a7..9aa7f0299 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -39,7 +39,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry); @property (nonatomic, copy) NSString *viewName; @property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children @property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle; -@property (nonatomic, assign) BOOL onLayout; +@property (nonatomic, copy) RCTDirectEventBlock onLayout; /** * isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is diff --git a/React/Views/RCTSlider.h b/React/Views/RCTSlider.h index 916419a29..664b0689b 100644 --- a/React/Views/RCTSlider.h +++ b/React/Views/RCTSlider.h @@ -9,6 +9,10 @@ #import +#import "RCTComponent.h" + @interface RCTSlider : UISlider +@property (nonatomic, copy) RCTBubblingEventBlock onChange; + @end diff --git a/React/Views/RCTSliderManager.m b/React/Views/RCTSliderManager.m index 9ce7699d1..694e8855b 100644 --- a/React/Views/RCTSliderManager.m +++ b/React/Views/RCTSliderManager.m @@ -27,25 +27,24 @@ RCT_EXPORT_MODULE() return slider; } -static void RCTSendSliderEvent(RCTSliderManager *self, UISlider *sender, BOOL continuous) +static void RCTSendSliderEvent(RCTSlider *sender, BOOL continuous) { - NSDictionary *event = @{ - @"target": sender.reactTag, - @"value": @(sender.value), - @"continuous": @(continuous), - }; - - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event]; + if (sender.onChange) { + sender.onChange(@{ + @"value": @(sender.value), + @"continuous": @(continuous), + }); + } } -- (void)sliderValueChanged:(UISlider *)sender +- (void)sliderValueChanged:(RCTSlider *)sender { - RCTSendSliderEvent(self, sender, YES); + RCTSendSliderEvent(sender, YES); } -- (void)sliderTouchEnd:(UISlider *)sender +- (void)sliderTouchEnd:(RCTSlider *)sender { - RCTSendSliderEvent(self, sender, NO); + RCTSendSliderEvent(sender, NO); } RCT_EXPORT_VIEW_PROPERTY(value, float); @@ -53,5 +52,6 @@ RCT_EXPORT_VIEW_PROPERTY(minimumValue, float); RCT_EXPORT_VIEW_PROPERTY(maximumValue, float); RCT_EXPORT_VIEW_PROPERTY(minimumTrackTintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(maximumTrackTintColor, UIColor); +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock); @end diff --git a/React/Views/RCTSwitch.h b/React/Views/RCTSwitch.h index 5c01abf49..f94bd5052 100644 --- a/React/Views/RCTSwitch.h +++ b/React/Views/RCTSwitch.h @@ -10,8 +10,11 @@ #import +#import "RCTComponent.h" + @interface RCTSwitch : UISwitch @property (nonatomic, assign) BOOL wasOn; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; @end diff --git a/React/Views/RCTSwitchManager.m b/React/Views/RCTSwitchManager.m index 42a54210c..7fe065baa 100644 --- a/React/Views/RCTSwitchManager.m +++ b/React/Views/RCTSwitchManager.m @@ -30,11 +30,9 @@ RCT_EXPORT_MODULE() - (void)onChange:(RCTSwitch *)sender { if (sender.wasOn != sender.on) { - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:@{ - @"target": sender.reactTag, - @"value": @(sender.on) - }]; - + if (sender.onChange) { + sender.onChange(@{ @"value": @(sender.on) }); + } sender.wasOn = sender.on; } } @@ -43,6 +41,7 @@ RCT_EXPORT_VIEW_PROPERTY(onTintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(thumbTintColor, UIColor); RCT_REMAP_VIEW_PROPERTY(value, on, BOOL); +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock); RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RCTSwitch) { if (json) { diff --git a/React/Views/RCTTabBar.h b/React/Views/RCTTabBar.h index 5c24b9039..ba0bbd7e6 100644 --- a/React/Views/RCTTabBar.h +++ b/React/Views/RCTTabBar.h @@ -9,14 +9,10 @@ #import -@class RCTEventDispatcher; - @interface RCTTabBar : UIView @property (nonatomic, strong) UIColor *tintColor; @property (nonatomic, strong) UIColor *barTintColor; @property (nonatomic, assign) BOOL translucent; -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - @end diff --git a/React/Views/RCTTabBar.m b/React/Views/RCTTabBar.m index 75e017a98..13d60f113 100644 --- a/React/Views/RCTTabBar.m +++ b/React/Views/RCTTabBar.m @@ -25,17 +25,13 @@ @implementation RCTTabBar { BOOL _tabsChanged; - RCTEventDispatcher *_eventDispatcher; UITabBarController *_tabController; NSMutableArray *_tabViews; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - RCTAssertParam(eventDispatcher); - - if ((self = [super initWithFrame:CGRectZero])) { - _eventDispatcher = eventDispatcher; + if ((self = [super initWithFrame:frame])) { _tabViews = [NSMutableArray new]; _tabController = [UITabBarController new]; _tabController.delegate = self; @@ -44,7 +40,6 @@ return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (UIViewController *)reactViewController @@ -100,8 +95,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) for (RCTTabBarItem *tab in [self reactSubviews]) { UIViewController *controller = tab.reactViewController; if (!controller) { - controller = [[RCTWrapperViewController alloc] initWithContentView:tab - eventDispatcher:_eventDispatcher]; + controller = [[RCTWrapperViewController alloc] initWithContentView:tab]; } [viewControllers addObject:controller]; } @@ -154,7 +148,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) { NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController]; RCTTabBarItem *tab = [self reactSubviews][index]; - [_eventDispatcher sendInputEventWithName:@"press" body:@{@"target": tab.reactTag}]; + if (tab.onPress) tab.onPress(nil); return NO; } diff --git a/React/Views/RCTTabBarItem.h b/React/Views/RCTTabBarItem.h index 8fe6d8efb..def1abf6c 100644 --- a/React/Views/RCTTabBarItem.h +++ b/React/Views/RCTTabBarItem.h @@ -9,10 +9,13 @@ #import +#import "RCTComponent.h" + @interface RCTTabBarItem : UIView @property (nonatomic, copy) id icon; @property (nonatomic, assign, getter=isSelected) BOOL selected; @property (nonatomic, readonly) UITabBarItem *barItem; +@property (nonatomic, copy) RCTBubblingEventBlock onPress; @end diff --git a/React/Views/RCTTabBarItemManager.m b/React/Views/RCTTabBarItemManager.m index f91153efd..a926a54f3 100644 --- a/React/Views/RCTTabBarItemManager.m +++ b/React/Views/RCTTabBarItemManager.m @@ -21,10 +21,11 @@ RCT_EXPORT_MODULE() return [RCTTabBarItem new]; } -RCT_EXPORT_VIEW_PROPERTY(selected, BOOL); -RCT_EXPORT_VIEW_PROPERTY(icon, id); -RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, UIImage); -RCT_REMAP_VIEW_PROPERTY(badge, barItem.badgeValue, NSString); +RCT_EXPORT_VIEW_PROPERTY(selected, BOOL) +RCT_EXPORT_VIEW_PROPERTY(icon, id) +RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, UIImage) +RCT_REMAP_VIEW_PROPERTY(badge, barItem.badgeValue, NSString) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem) { view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title; diff --git a/React/Views/RCTTabBarManager.m b/React/Views/RCTTabBarManager.m index 7b9616246..b5d79f886 100644 --- a/React/Views/RCTTabBarManager.m +++ b/React/Views/RCTTabBarManager.m @@ -18,7 +18,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTTabBar alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTTabBar new]; } RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index 6e7019f58..1222dc9cf 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -11,17 +11,20 @@ #import +#import "RCTComponent.h" #import "RCTPointerEvents.h" @protocol RCTAutoInsetsProtocol; @class RCTView; -typedef void (^RCTViewEventHandler)(RCTView *view); @interface RCTView : UIView -@property (nonatomic, copy) RCTViewEventHandler accessibilityTapHandler; -@property (nonatomic, copy) RCTViewEventHandler magicTapHandler; +/** + * Accessibility event handlers + */ +@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityTap; +@property (nonatomic, copy) RCTDirectEventBlock onMagicTap; /** * Used to control how touch events are processed. diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index d242faa44..85ff2ecf4 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -167,8 +167,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) - (BOOL)accessibilityActivate { - if (self.accessibilityTapHandler) { - self.accessibilityTapHandler(self); + if (_onAccessibilityTap) { + _onAccessibilityTap(nil); return YES; } else { return NO; @@ -177,8 +177,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) - (BOOL)accessibilityPerformMagicTap { - if (self.magicTapHandler) { - self.magicTapHandler(self); + if (_onMagicTap) { + _onMagicTap(nil); return YES; } else { return NO; diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index f3c370b94..35c2a4d4f 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -11,10 +11,12 @@ #import "RCTBridgeModule.h" #import "RCTConvert.h" +#import "RCTComponent.h" +#import "RCTDefines.h" +#import "RCTEventDispatcher.h" #import "RCTLog.h" @class RCTBridge; -@class RCTEventDispatcher; @class RCTShadowView; @class RCTSparseArray; @class RCTUIManager; @@ -37,7 +39,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v * return a fresh instance each time. The view module MUST NOT cache the returned * view and return the same instance for subsequent calls. */ -- (UIView *)view; +- (UIView *)view; /** * This method instantiates a native view using the props passed into the component. @@ -57,6 +59,8 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v - (RCTShadowView *)shadowView; /** + * DEPRECATED: declare properties of type RCTBubblingEventBlock instead + * * Returns an array of names of events that can be sent by native views. This * should return bubbling, directly-dispatched event types. The event name * should not include a prefix such as 'on' or 'top', as this will be applied @@ -69,6 +73,8 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v - (NSArray *)customBubblingEventTypes; /** + * DEPRECATED: declare properties of type RCTDirectEventBlock instead + * * Returns an array of names of events that can be sent by native views. This * should return non-bubbling, directly-dispatched event types. The event name * should not include a prefix such as 'on' or 'top', as this will be applied diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index b4562f689..c2cf93676 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -54,7 +54,7 @@ RCT_EXPORT_MODULE() return _bridge.uiManager.methodQueue; } -- (UIView *)viewWithProps:(NSDictionary *)props +- (UIView *)viewWithProps:(__unused NSDictionary *)props { return [self view]; } @@ -76,7 +76,6 @@ RCT_EXPORT_MODULE() // Generic events @"press", @"change", - @"change", @"focus", @"blur", @"submitEditing", @@ -92,11 +91,7 @@ RCT_EXPORT_MODULE() - (NSArray *)customDirectEventTypes { - return @[ - @"layout", - @"accessibilityTap", - @"magicTap", - ]; + return @[]; } - (NSDictionary *)constantsToExport @@ -195,27 +190,8 @@ RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView) view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth; } } -RCT_CUSTOM_VIEW_PROPERTY(onAccessibilityTap, BOOL, __unused RCTView) -{ - view.accessibilityTapHandler = [self eventHandlerWithName:@"accessibilityTap" json:json]; -} -RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, __unused RCTView) -{ - view.magicTapHandler = [self eventHandlerWithName:@"magicTap" json:json]; -} - -- (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json -{ - RCTViewEventHandler handler = nil; - if ([RCTConvert BOOL:json]) { - __weak RCTViewManager *weakSelf = self; - handler = ^(RCTView *tappedView) { - NSDictionary *body = @{ @"target": tappedView.reactTag }; - [weakSelf.bridge.eventDispatcher sendInputEventWithName:eventName body:body]; - }; - } - return handler; -} +RCT_EXPORT_VIEW_PROPERTY(onAccessibilityTap, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onMagicTap, RCTDirectEventBlock) #define RCT_VIEW_BORDER_PROPERTY(SIDE) \ RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, CGFloat, RCTView) \ @@ -291,6 +267,6 @@ RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t) RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t) RCT_EXPORT_SHADOW_PROPERTY(position, css_position_type_t) -RCT_EXPORT_SHADOW_PROPERTY(onLayout, BOOL) +RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock) @end diff --git a/React/Views/RCTWebView.h b/React/Views/RCTWebView.h index 9a028ea49..fdb192a39 100644 --- a/React/Views/RCTWebView.h +++ b/React/Views/RCTWebView.h @@ -17,8 +17,6 @@ */ extern NSString *const RCTJSNavigationScheme; -@class RCTEventDispatcher; - @interface RCTWebView : RCTView @property (nonatomic, strong) NSURL *URL; @@ -26,8 +24,6 @@ extern NSString *const RCTJSNavigationScheme; @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, copy) NSString *injectedJavaScript; -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - - (void)goForward; - (void)goBack; - (void)reload; diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index 196284d24..4838179a5 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -22,24 +22,24 @@ NSString *const RCTJSNavigationScheme = @"react-js-navigation"; @interface RCTWebView () +@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; +@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; +@property (nonatomic, copy) RCTDirectEventBlock onLoadingError; + @end @implementation RCTWebView { - RCTEventDispatcher *_eventDispatcher; UIWebView *_webView; NSString *_injectedJavaScript; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - RCTAssertParam(eventDispatcher); - - if ((self = [super initWithFrame:CGRectZero])) { + if ((self = [super initWithFrame:frame])) { super.backgroundColor = [UIColor clearColor]; _automaticallyAdjustContentInsets = YES; _contentInset = UIEdgeInsetsZero; - _eventDispatcher = eventDispatcher; _webView = [[UIWebView alloc] initWithFrame:self.bounds]; _webView.delegate = self; [self addSubview:_webView]; @@ -47,7 +47,6 @@ NSString *const RCTJSNavigationScheme = @"react-js-navigation"; return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)goForward @@ -123,13 +122,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (NSMutableDictionary *)baseEvent { - NSURL *url = _webView.request.URL; - NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary: @{ - @"target": self.reactTag, - @"url": url ? url.absoluteString : @"", + @"url": _webView.request.URL.absoluteString ?: @"", @"loading" : @(_webView.loading), - @"title": title, + @"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"], @"canGoBack": @(_webView.canGoBack), @"canGoForward" : @(_webView.canGoForward), }]; @@ -139,19 +135,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) #pragma mark - UIWebViewDelegate methods - - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { - // We have this check to filter out iframe requests and whatnot - BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; - if (isTopFrame) { - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary: @{ - @"url": (request.URL).absoluteString, - @"navigationType": @(navigationType) - }]; - [_eventDispatcher sendInputEventWithName:@"loadingStart" body:event]; + if (_onLoadingStart) { + // We have this check to filter out iframe requests and whatnot + BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; + if (isTopFrame) { + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"url": (request.URL).absoluteString, + @"navigationType": @(navigationType) + }]; + _onLoadingStart(event); + } } // JS Navigation handler @@ -160,21 +157,24 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error { - if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { - // NSURLErrorCancelled is reported when a page has a redirect OR if you load - // a new URL in the WebView before the previous one came back. We can just - // ignore these since they aren't real errors. - // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os - return; - } + if (_onLoadingError) { - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary: @{ - @"domain": error.domain, - @"code": @(error.code), - @"description": error.localizedDescription, - }]; - [_eventDispatcher sendInputEventWithName:@"loadingError" body:event]; + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { + // NSURLErrorCancelled is reported when a page has a redirect OR if you load + // a new URL in the WebView before the previous one came back. We can just + // ignore these since they aren't real errors. + // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os + return; + } + + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"domain": error.domain, + @"code": @(error.code), + @"description": error.localizedDescription, + }]; + _onLoadingError(event); + } } - (void)webViewDidFinishLoad:(UIWebView *)webView @@ -184,8 +184,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) } // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. - if (!webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { - [_eventDispatcher sendInputEventWithName:@"loadingFinish" body:[self baseEvent]]; + if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { + _onLoadingFinish([self baseEvent]); } } diff --git a/React/Views/RCTWebViewManager.m b/React/Views/RCTWebViewManager.m index 1fd2cfe3f..8779a970b 100644 --- a/React/Views/RCTWebViewManager.m +++ b/React/Views/RCTWebViewManager.m @@ -20,7 +20,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTWebView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTWebView new]; } RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL); @@ -31,15 +31,9 @@ RCT_REMAP_VIEW_PROPERTY(scalesPageToFit, _webView.scalesPageToFit, BOOL); RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString); RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets); RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL); - -- (NSArray *)customDirectEventTypes -{ - return @[ - @"loadingStart", - @"loadingFinish", - @"loadingError", - ]; -} +RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock); - (NSDictionary *)constantsToExport { @@ -80,7 +74,6 @@ RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) }]; } - RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { diff --git a/React/Views/RCTWrapperViewController.h b/React/Views/RCTWrapperViewController.h index 09e647891..7b24ae7d5 100644 --- a/React/Views/RCTWrapperViewController.h +++ b/React/Views/RCTWrapperViewController.h @@ -11,7 +11,6 @@ #import "RCTViewControllerProtocol.h" -@class RCTEventDispatcher; @class RCTNavItem; @class RCTWrapperViewController; @@ -24,11 +23,8 @@ didMoveToNavigationController:(UINavigationController *)navigationController; @interface RCTWrapperViewController : UIViewController -- (instancetype)initWithContentView:(UIView *)contentView - eventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - -- (instancetype)initWithNavItem:(RCTNavItem *)navItem - eventDispatcher:(RCTEventDispatcher *)eventDispatcher; +- (instancetype)initWithContentView:(UIView *)contentView NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithNavItem:(RCTNavItem *)navItem; @property (nonatomic, weak) id navigationListener; @property (nonatomic, strong) RCTNavItem *navItem; diff --git a/React/Views/RCTWrapperViewController.m b/React/Views/RCTWrapperViewController.m index 00af3b53d..92707e683 100644 --- a/React/Views/RCTWrapperViewController.m +++ b/React/Views/RCTWrapperViewController.m @@ -21,7 +21,6 @@ { UIView *_wrapperView; UIView *_contentView; - RCTEventDispatcher *_eventDispatcher; CGFloat _previousTopLayout; CGFloat _previousBottomLayout; } @@ -30,23 +29,19 @@ @synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide; - (instancetype)initWithContentView:(UIView *)contentView - eventDispatcher:(RCTEventDispatcher *)eventDispatcher { RCTAssertParam(contentView); - RCTAssertParam(eventDispatcher); if ((self = [super initWithNibName:nil bundle:nil])) { _contentView = contentView; - _eventDispatcher = eventDispatcher; self.automaticallyAdjustsScrollViewInsets = NO; } return self; } - (instancetype)initWithNavItem:(RCTNavItem *)navItem - eventDispatcher:(RCTEventDispatcher *)eventDispatcher { - if ((self = [self initWithContentView:navItem eventDispatcher:eventDispatcher])) { + if ((self = [self initWithContentView:navItem])) { _navItem = navItem; } return self; @@ -101,14 +96,8 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view) UINavigationItem *item = self.navigationItem; item.title = _navItem.title; item.backBarButtonItem = _navItem.backButtonItem; - if ((item.leftBarButtonItem = _navItem.leftButtonItem)) { - item.leftBarButtonItem.target = self; - item.leftBarButtonItem.action = @selector(handleNavLeftButtonTapped); - } - if ((item.rightBarButtonItem = _navItem.rightButtonItem)) { - item.rightBarButtonItem.target = self; - item.rightBarButtonItem.action = @selector(handleNavRightButtonTapped); - } + item.leftBarButtonItem = _navItem.leftButtonItem; + item.rightBarButtonItem = _navItem.rightButtonItem; } } @@ -122,18 +111,6 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view) self.view = _wrapperView; } -- (void)handleNavLeftButtonTapped -{ - [_eventDispatcher sendInputEventWithName:@"navLeftButtonTap" - body:@{@"target":_navItem.reactTag}]; -} - -- (void)handleNavRightButtonTapped -{ - [_eventDispatcher sendInputEventWithName:@"navRightButtonTap" - body:@{@"target":_navItem.reactTag}]; -} - - (void)didMoveToParentViewController:(UIViewController *)parent { // There's no clear setter for navigation controllers, but did move to parent diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index 18bd5a747..1cbbc16da 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -23,7 +23,7 @@ - (void)setReactTag:(NSNumber *)reactTag { - objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_COPY_NONATOMIC); + objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isReactRootView diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 000000000..9e5c55c36 --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,5759 @@ +{ + "name": "react-native", + "version": "0.8.0", + "dependencies": { + "absolute-path": { + "version": "0.0.0", + "from": "absolute-path@0.0.0", + "resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz" + }, + "babel": { + "version": "5.8.21", + "from": "babel@5.8.21", + "resolved": "https://registry.npmjs.org/babel/-/babel-5.8.21.tgz", + "dependencies": { + "chokidar": { + "version": "1.0.5", + "from": "chokidar@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.0.5.tgz", + "dependencies": { + "anymatch": { + "version": "1.3.0", + "from": "anymatch@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", + "dependencies": { + "micromatch": { + "version": "2.2.0", + "from": "micromatch@>=2.1.5 <3.0.0", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.2.0.tgz", + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "from": "arr-diff@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "dependencies": { + "arr-flatten": { + "version": "1.0.1", + "from": "arr-flatten@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz" + }, + "array-slice": { + "version": "0.2.3", + "from": "array-slice@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz" + } + } + }, + "array-unique": { + "version": "0.2.1", + "from": "array-unique@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz" + }, + "braces": { + "version": "1.8.1", + "from": "braces@>=1.8.0 <2.0.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.1.tgz", + "dependencies": { + "expand-range": { + "version": "1.8.1", + "from": "expand-range@>=1.8.1 <2.0.0", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.1.tgz", + "dependencies": { + "fill-range": { + "version": "2.2.2", + "from": "fill-range@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.2.tgz", + "dependencies": { + "is-number": { + "version": "1.1.2", + "from": "is-number@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-1.1.2.tgz" + }, + "isobject": { + "version": "1.0.2", + "from": "isobject@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-1.0.2.tgz" + }, + "randomatic": { + "version": "1.1.0", + "from": "randomatic@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.0.tgz" + }, + "repeat-string": { + "version": "1.5.2", + "from": "repeat-string@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.5.2.tgz" + } + } + } + } + }, + "lazy-cache": { + "version": "0.2.3", + "from": "lazy-cache@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.3.tgz" + }, + "preserve": { + "version": "0.2.0", + "from": "preserve@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" + }, + "repeat-element": { + "version": "1.1.2", + "from": "repeat-element@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + } + } + }, + "expand-brackets": { + "version": "0.1.4", + "from": "expand-brackets@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.4.tgz" + }, + "extglob": { + "version": "0.3.1", + "from": "extglob@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.1.tgz", + "dependencies": { + "ansi-green": { + "version": "0.1.1", + "from": "ansi-green@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", + "dependencies": { + "ansi-wrap": { + "version": "0.1.0", + "from": "ansi-wrap@0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz" + } + } + }, + "is-extglob": { + "version": "1.0.0", + "from": "is-extglob@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + }, + "success-symbol": { + "version": "0.1.0", + "from": "success-symbol@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz" + } + } + }, + "filename-regex": { + "version": "2.0.0", + "from": "filename-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz" + }, + "kind-of": { + "version": "1.1.0", + "from": "kind-of@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz" + }, + "object.omit": { + "version": "1.1.0", + "from": "object.omit@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-1.1.0.tgz", + "dependencies": { + "for-own": { + "version": "0.1.3", + "from": "for-own@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.3.tgz", + "dependencies": { + "for-in": { + "version": "0.1.4", + "from": "for-in@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.4.tgz" + } + } + }, + "isobject": { + "version": "1.0.2", + "from": "isobject@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-1.0.2.tgz" + } + } + }, + "parse-glob": { + "version": "3.0.2", + "from": "parse-glob@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.2.tgz", + "dependencies": { + "glob-base": { + "version": "0.2.0", + "from": "glob-base@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.2.0.tgz" + }, + "is-dotfile": { + "version": "1.0.1", + "from": "is-dotfile@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.1.tgz" + }, + "is-extglob": { + "version": "1.0.0", + "from": "is-extglob@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + } + }, + "regex-cache": { + "version": "0.4.2", + "from": "regex-cache@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.2.tgz", + "dependencies": { + "is-equal-shallow": { + "version": "0.1.3", + "from": "is-equal-shallow@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" + }, + "is-primitive": { + "version": "2.0.0", + "from": "is-primitive@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } + } + } + } + } + } + }, + "arrify": { + "version": "1.0.0", + "from": "arrify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.0.tgz" + }, + "async-each": { + "version": "0.1.6", + "from": "async-each@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-0.1.6.tgz" + }, + "glob-parent": { + "version": "1.2.0", + "from": "glob-parent@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-1.2.0.tgz" + }, + "is-binary-path": { + "version": "1.0.1", + "from": "is-binary-path@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "dependencies": { + "binary-extensions": { + "version": "1.3.1", + "from": "binary-extensions@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.3.1.tgz" + } + } + }, + "is-glob": { + "version": "1.1.3", + "from": "is-glob@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-1.1.3.tgz" + }, + "readdirp": { + "version": "1.4.0", + "from": "readdirp@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-1.4.0.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.12 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.26-2 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "fsevents": { + "version": "0.3.8", + "from": "fsevents@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.3.8.tgz", + "dependencies": { + "nan": { + "version": "2.0.8", + "from": "nan@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.8.tgz" + } + } + } + } + }, + "commander": { + "version": "2.8.1", + "from": "commander@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + } + }, + "convert-source-map": { + "version": "1.1.1", + "from": "convert-source-map@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.1.tgz" + }, + "fs-readdir-recursive": { + "version": "0.1.2", + "from": "fs-readdir-recursive@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz" + }, + "glob": { + "version": "5.0.14", + "from": "glob@>=5.0.5 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.14.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "output-file-sync": { + "version": "1.1.1", + "from": "output-file-sync@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "path-exists": { + "version": "1.0.0", + "from": "path-exists@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "slash": { + "version": "1.0.0", + "from": "slash@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" + } + } + }, + "babel-core": { + "version": "5.8.21", + "from": "babel-core@5.8.21", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-5.8.21.tgz", + "dependencies": { + "babel-plugin-constant-folding": { + "version": "1.0.1", + "from": "babel-plugin-constant-folding@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz" + }, + "babel-plugin-dead-code-elimination": { + "version": "1.0.2", + "from": "babel-plugin-dead-code-elimination@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz" + }, + "babel-plugin-eval": { + "version": "1.0.1", + "from": "babel-plugin-eval@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz" + }, + "babel-plugin-inline-environment-variables": { + "version": "1.0.1", + "from": "babel-plugin-inline-environment-variables@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz" + }, + "babel-plugin-jscript": { + "version": "1.0.4", + "from": "babel-plugin-jscript@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz" + }, + "babel-plugin-member-expression-literals": { + "version": "1.0.1", + "from": "babel-plugin-member-expression-literals@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz" + }, + "babel-plugin-property-literals": { + "version": "1.0.1", + "from": "babel-plugin-property-literals@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz" + }, + "babel-plugin-proto-to-assign": { + "version": "1.0.4", + "from": "babel-plugin-proto-to-assign@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz" + }, + "babel-plugin-react-constant-elements": { + "version": "1.0.3", + "from": "babel-plugin-react-constant-elements@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz" + }, + "babel-plugin-react-display-name": { + "version": "1.0.3", + "from": "babel-plugin-react-display-name@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz" + }, + "babel-plugin-remove-console": { + "version": "1.0.1", + "from": "babel-plugin-remove-console@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz" + }, + "babel-plugin-remove-debugger": { + "version": "1.0.1", + "from": "babel-plugin-remove-debugger@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz" + }, + "babel-plugin-runtime": { + "version": "1.0.7", + "from": "babel-plugin-runtime@>=1.0.7 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz" + }, + "babel-plugin-undeclared-variables-check": { + "version": "1.0.2", + "from": "babel-plugin-undeclared-variables-check@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz", + "dependencies": { + "leven": { + "version": "1.0.2", + "from": "leven@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz" + } + } + }, + "babel-plugin-undefined-to-void": { + "version": "1.1.6", + "from": "babel-plugin-undefined-to-void@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz" + }, + "babylon": { + "version": "5.8.23", + "from": "babylon@>=5.8.21 <6.0.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-5.8.23.tgz" + }, + "bluebird": { + "version": "2.9.34", + "from": "bluebird@>=2.9.33 <3.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz" + }, + "convert-source-map": { + "version": "1.1.1", + "from": "convert-source-map@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.1.tgz" + }, + "core-js": { + "version": "1.1.3", + "from": "core-js@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.3.tgz" + }, + "debug": { + "version": "2.2.0", + "from": "debug@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + }, + "detect-indent": { + "version": "3.0.1", + "from": "detect-indent@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + } + } + }, + "esutils": { + "version": "2.0.2", + "from": "esutils@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" + }, + "fs-readdir-recursive": { + "version": "0.1.2", + "from": "fs-readdir-recursive@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz" + }, + "globals": { + "version": "6.4.1", + "from": "globals@>=6.4.0 <7.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-6.4.1.tgz" + }, + "home-or-tmp": { + "version": "1.0.0", + "from": "home-or-tmp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-1.0.0.tgz", + "dependencies": { + "os-tmpdir": { + "version": "1.0.1", + "from": "os-tmpdir@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz" + }, + "user-home": { + "version": "1.1.1", + "from": "user-home@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz" + } + } + }, + "is-integer": { + "version": "1.0.6", + "from": "is-integer@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.6.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + }, + "js-tokens": { + "version": "1.0.1", + "from": "js-tokens@1.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.1.tgz" + }, + "json5": { + "version": "0.4.0", + "from": "json5@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz" + }, + "line-numbers": { + "version": "0.2.0", + "from": "line-numbers@0.2.0", + "resolved": "https://registry.npmjs.org/line-numbers/-/line-numbers-0.2.0.tgz", + "dependencies": { + "left-pad": { + "version": "0.0.3", + "from": "left-pad@0.0.3", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-0.0.3.tgz" + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.10.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.3 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "output-file-sync": { + "version": "1.1.1", + "from": "output-file-sync@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "path-exists": { + "version": "1.0.0", + "from": "path-exists@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "regenerator": { + "version": "0.8.35", + "from": "regenerator@0.8.35", + "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.35.tgz", + "dependencies": { + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + }, + "defs": { + "version": "1.1.0", + "from": "defs@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.0.tgz", + "dependencies": { + "alter": { + "version": "0.2.0", + "from": "alter@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "dependencies": { + "stable": { + "version": "0.1.5", + "from": "stable@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" + } + } + }, + "ast-traverse": { + "version": "0.1.1", + "from": "ast-traverse@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz" + }, + "breakable": { + "version": "1.0.0", + "from": "breakable@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz" + }, + "esprima-fb": { + "version": "8001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=8001.1001.0-dev-harmony-fb <8001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-8001.1001.0-dev-harmony-fb.tgz" + }, + "simple-fmt": { + "version": "0.1.0", + "from": "simple-fmt@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" + }, + "simple-is": { + "version": "0.2.0", + "from": "simple-is@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" + }, + "stringmap": { + "version": "0.2.2", + "from": "stringmap@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" + }, + "stringset": { + "version": "0.2.1", + "from": "stringset@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" + }, + "tryor": { + "version": "0.1.2", + "from": "tryor@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" + } + } + }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1.0-dev-harmony-fb <15001.2.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz" + }, + "recast": { + "version": "0.10.24", + "from": "recast@0.10.24", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.24.tgz", + "dependencies": { + "ast-types": { + "version": "0.8.5", + "from": "ast-types@0.8.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.5.tgz" + } + } + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <2.4.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "regexpu": { + "version": "1.2.0", + "from": "regexpu@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/regexpu/-/regexpu-1.2.0.tgz", + "dependencies": { + "recast": { + "version": "0.10.32", + "from": "recast@>=0.10.6 <0.11.0", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.32.tgz", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz" + }, + "ast-types": { + "version": "0.8.11", + "from": "ast-types@0.8.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.11.tgz" + } + } + }, + "regenerate": { + "version": "1.2.1", + "from": "regenerate@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.2.1.tgz" + }, + "regjsgen": { + "version": "0.2.0", + "from": "regjsgen@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz" + }, + "regjsparser": { + "version": "0.1.5", + "from": "regjsparser@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "dependencies": { + "jsesc": { + "version": "0.5.0", + "from": "jsesc@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + } + } + } + } + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + }, + "resolve": { + "version": "1.1.6", + "from": "resolve@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz" + }, + "shebang-regex": { + "version": "1.0.0", + "from": "shebang-regex@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + }, + "slash": { + "version": "1.0.0", + "from": "slash@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "source-map-support": { + "version": "0.2.10", + "from": "source-map-support@>=0.2.10 <0.3.0", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz", + "dependencies": { + "source-map": { + "version": "0.1.32", + "from": "source-map@0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + } + } + }, + "to-fast-properties": { + "version": "1.0.1", + "from": "to-fast-properties@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.1.tgz" + }, + "trim-right": { + "version": "1.0.1", + "from": "trim-right@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" + }, + "try-resolve": { + "version": "1.0.1", + "from": "try-resolve@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz" + } + } + }, + "bser": { + "version": "1.0.0", + "from": "bser@1.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.0.tgz", + "dependencies": { + "node-int64": { + "version": "0.4.0", + "from": "node-int64@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + } + } + }, + "chalk": { + "version": "1.0.0", + "from": "chalk@1.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.0.0.tgz", + "dependencies": { + "ansi-styles": { + "version": "2.1.0", + "from": "ansi-styles@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.1.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + }, + "has-ansi": { + "version": "1.0.3", + "from": "has-ansi@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-1.0.3.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + }, + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + } + } + }, + "strip-ansi": { + "version": "2.0.1", + "from": "strip-ansi@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + } + } + }, + "supports-color": { + "version": "1.3.1", + "from": "supports-color@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz" + } + } + }, + "connect": { + "version": "2.8.3", + "from": "connect@2.8.3", + "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.3.tgz", + "dependencies": { + "qs": { + "version": "0.6.5", + "from": "qs@0.6.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" + }, + "formidable": { + "version": "1.0.14", + "from": "formidable@1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" + }, + "cookie-signature": { + "version": "1.0.1", + "from": "cookie-signature@1.0.1", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" + }, + "buffer-crc32": { + "version": "0.2.1", + "from": "buffer-crc32@0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" + }, + "cookie": { + "version": "0.1.0", + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + }, + "send": { + "version": "0.1.2", + "from": "send@0.1.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.2.tgz", + "dependencies": { + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.9 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "range-parser": { + "version": "0.0.4", + "from": "range-parser@0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" + } + } + }, + "bytes": { + "version": "0.2.0", + "from": "bytes@0.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" + }, + "fresh": { + "version": "0.1.0", + "from": "fresh@0.1.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.1.0.tgz" + }, + "pause": { + "version": "0.0.1", + "from": "pause@0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + }, + "uid2": { + "version": "0.0.2", + "from": "uid2@0.0.2", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" + }, + "methods": { + "version": "0.0.1", + "from": "methods@0.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" + } + } + }, + "debug": { + "version": "2.1.0", + "from": "debug@2.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz", + "dependencies": { + "ms": { + "version": "0.6.2", + "from": "ms@0.6.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + } + } + }, + "graceful-fs": { + "version": "4.1.2", + "from": "graceful-fs@4.1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.2.tgz" + }, + "image-size": { + "version": "0.3.5", + "from": "image-size@0.3.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.3.5.tgz" + }, + "immutable": { + "version": "3.7.5", + "from": "immutable@>=3.7.4 <4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.5.tgz" + }, + "joi": { + "version": "5.1.0", + "from": "joi@5.1.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-5.1.0.tgz", + "dependencies": { + "hoek": { + "version": "2.14.0", + "from": "hoek@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.14.0.tgz" + }, + "topo": { + "version": "1.0.3", + "from": "topo@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-1.0.3.tgz" + }, + "isemail": { + "version": "1.1.1", + "from": "isemail@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.1.1.tgz" + }, + "moment": { + "version": "2.10.6", + "from": "moment@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.10.6.tgz" + } + } + }, + "jstransform": { + "version": "11.0.1", + "from": "jstransform@11.0.1", + "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.1.tgz", + "dependencies": { + "base62": { + "version": "1.1.0", + "from": "base62@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/base62/-/base62-1.1.0.tgz" + }, + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.1 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "recast": { + "version": "0.10.32", + "from": "recast@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.32.tgz", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz" + }, + "ast-types": { + "version": "0.8.11", + "from": "ast-types@0.8.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.11.tgz" + } + } + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1.0-dev-harmony-fb <15002.0.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz" + }, + "object-assign": { + "version": "2.1.1", + "from": "object-assign@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + } + } + }, + "module-deps": { + "version": "3.5.6", + "from": "module-deps@3.5.6", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-3.5.6.tgz", + "dependencies": { + "JSONStream": { + "version": "0.7.4", + "from": "JSONStream@>=0.7.1 <0.8.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.7.4.tgz", + "dependencies": { + "jsonparse": { + "version": "0.0.5", + "from": "jsonparse@0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.2.7 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "browser-resolve": { + "version": "1.9.1", + "from": "browser-resolve@>=1.3.1 <2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.9.1.tgz", + "dependencies": { + "resolve": { + "version": "1.1.6", + "from": "resolve@1.1.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz" + } + } + }, + "concat-stream": { + "version": "1.4.10", + "from": "concat-stream@>=1.4.5 <1.5.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.10.tgz", + "dependencies": { + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + } + } + }, + "detective": { + "version": "3.1.0", + "from": "detective@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-3.1.0.tgz", + "dependencies": { + "escodegen": { + "version": "1.1.0", + "from": "escodegen@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.1.0.tgz", + "dependencies": { + "esprima": { + "version": "1.0.4", + "from": "esprima@>=1.0.4 <1.1.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + }, + "estraverse": { + "version": "1.5.1", + "from": "estraverse@>=1.5.0 <1.6.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz" + }, + "esutils": { + "version": "1.0.0", + "from": "esutils@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz" + } + } + }, + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "from": "esprima-fb@3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz" + } + } + }, + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimist": { + "version": "0.2.0", + "from": "minimist@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz" + }, + "parents": { + "version": "1.0.1", + "from": "parents@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "dependencies": { + "path-platform": { + "version": "0.11.15", + "from": "path-platform@>=0.11.15 <0.12.0", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz" + } + } + }, + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.0.27-1 <2.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + }, + "resolve": { + "version": "0.7.4", + "from": "resolve@>=0.7.2 <0.8.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.7.4.tgz" + }, + "shallow-copy": { + "version": "0.0.1", + "from": "shallow-copy@0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz" + }, + "stream-combiner2": { + "version": "1.0.2", + "from": "stream-combiner2@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.0.2.tgz", + "dependencies": { + "through2": { + "version": "0.5.1", + "from": "through2@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + }, + "xtend": { + "version": "3.0.0", + "from": "xtend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz" + } + } + } + } + }, + "subarg": { + "version": "0.0.1", + "from": "subarg@0.0.1", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-0.0.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.7 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + }, + "through2": { + "version": "0.4.2", + "from": "through2@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + }, + "xtend": { + "version": "2.1.2", + "from": "xtend@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "dependencies": { + "object-keys": { + "version": "0.4.0", + "from": "object-keys@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" + } + } + } + } + } + } + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + }, + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.1 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + }, + "progress": { + "version": "1.1.8", + "from": "progress@>=1.1.8 <2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz" + }, + "promise": { + "version": "7.0.4", + "from": "promise@>=7.0.3 <8.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.0.4.tgz", + "dependencies": { + "asap": { + "version": "2.0.3", + "from": "asap@>=2.0.3 <2.1.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz" + } + } + }, + "react-timer-mixin": { + "version": "0.13.2", + "from": "react-timer-mixin@>=0.13.1 <0.14.0", + "resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.2.tgz" + }, + "react-tools": { + "version": "0.14.0-beta1", + "from": "git://github.com/facebook/react.git#b4e74e38e43ac53af8acd62c78c9213be0194245", + "resolved": "git://github.com/facebook/react.git#b4e74e38e43ac53af8acd62c78c9213be0194245", + "dependencies": { + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "recast": { + "version": "0.10.32", + "from": "recast@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.32.tgz", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.4 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "ast-types": { + "version": "0.8.11", + "from": "ast-types@0.8.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.11.tgz" + } + } + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + } + } + }, + "rebound": { + "version": "0.0.12", + "from": "rebound@>=0.0.12 <0.0.13", + "resolved": "https://registry.npmjs.org/rebound/-/rebound-0.0.12.tgz" + }, + "regenerator": { + "version": "0.8.36", + "from": "regenerator@0.8.36", + "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.36.tgz", + "dependencies": { + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + }, + "defs": { + "version": "1.1.0", + "from": "defs@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.0.tgz", + "dependencies": { + "alter": { + "version": "0.2.0", + "from": "alter@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "dependencies": { + "stable": { + "version": "0.1.5", + "from": "stable@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" + } + } + }, + "ast-traverse": { + "version": "0.1.1", + "from": "ast-traverse@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz" + }, + "breakable": { + "version": "1.0.0", + "from": "breakable@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz" + }, + "esprima-fb": { + "version": "8001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=8001.1001.0-dev-harmony-fb <8001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-8001.1001.0-dev-harmony-fb.tgz" + }, + "simple-fmt": { + "version": "0.1.0", + "from": "simple-fmt@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" + }, + "simple-is": { + "version": "0.2.0", + "from": "simple-is@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" + }, + "stringmap": { + "version": "0.2.2", + "from": "stringmap@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" + }, + "stringset": { + "version": "0.2.1", + "from": "stringset@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" + }, + "tryor": { + "version": "0.1.2", + "from": "tryor@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" + } + } + }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1.0-dev-harmony-fb <15001.2.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz" + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "recast": { + "version": "0.10.25", + "from": "recast@0.10.25", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.25.tgz", + "dependencies": { + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "ast-types": { + "version": "0.8.5", + "from": "ast-types@0.8.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.5.tgz" + } + } + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <2.4.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "sane": { + "version": "1.2.0", + "from": "sane@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-1.2.0.tgz", + "dependencies": { + "exec-sh": { + "version": "0.2.0", + "from": "exec-sh@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", + "dependencies": { + "merge": { + "version": "1.2.0", + "from": "merge@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz" + } + } + }, + "fb-watchman": { + "version": "1.6.0", + "from": "fb-watchman@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.6.0.tgz", + "dependencies": { + "bser": { + "version": "1.0.2", + "from": "bser@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", + "dependencies": { + "node-int64": { + "version": "0.4.0", + "from": "node-int64@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + } + } + } + } + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.14 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "walker": { + "version": "1.0.7", + "from": "walker@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "dependencies": { + "makeerror": { + "version": "1.0.11", + "from": "makeerror@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "dependencies": { + "tmpl": { + "version": "1.0.4", + "from": "tmpl@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz" + } + } + } + } + }, + "watch": { + "version": "0.10.0", + "from": "watch@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz" + } + } + }, + "semver": { + "version": "4.3.6", + "from": "semver@>=4.3.6 <5.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + }, + "source-map": { + "version": "0.1.31", + "from": "source-map@0.1.31", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "stacktrace-parser": { + "version": "0.1.3", + "from": "stacktrace-parser@0.1.3", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.3.tgz" + }, + "uglify-js": { + "version": "2.4.16", + "from": "uglify-js@2.4.16", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.16.tgz", + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "source-map": { + "version": "0.1.34", + "from": "source-map@0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "optimist": { + "version": "0.3.7", + "from": "optimist@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "from": "uglify-to-browserify@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" + } + } + }, + "underscore": { + "version": "1.7.0", + "from": "underscore@1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "wordwrap": { + "version": "1.0.0", + "from": "wordwrap@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" + }, + "worker-farm": { + "version": "1.3.1", + "from": "worker-farm@>=1.3.1 <2.0.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.3.1.tgz", + "dependencies": { + "errno": { + "version": "0.1.4", + "from": "errno@>=0.1.1 <0.2.0-0", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "dependencies": { + "prr": { + "version": "0.0.0", + "from": "prr@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "ws": { + "version": "0.8.0", + "from": "ws@0.8.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-0.8.0.tgz", + "dependencies": { + "options": { + "version": "0.0.6", + "from": "options@>=0.0.5", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" + }, + "ultron": { + "version": "1.0.2", + "from": "ultron@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + }, + "bufferutil": { + "version": "1.2.1", + "from": "bufferutil@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.2.1.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "2.0.8", + "from": "nan@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.8.tgz" + } + } + }, + "utf-8-validate": { + "version": "1.2.1", + "from": "utf-8-validate@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.2.1.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "2.0.8", + "from": "nan@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.8.tgz" + } + } + } + } + }, + "yargs": { + "version": "1.3.2", + "from": "yargs@1.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.2.tgz" + }, + "yeoman-environment": { + "version": "1.2.7", + "from": "yeoman-environment@>=1.2.7 <2.0.0", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-1.2.7.tgz", + "dependencies": { + "diff": { + "version": "1.4.0", + "from": "diff@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + }, + "globby": { + "version": "2.1.0", + "from": "globby@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-2.1.0.tgz", + "dependencies": { + "array-union": { + "version": "1.0.1", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.1.tgz", + "dependencies": { + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + } + } + }, + "async": { + "version": "1.4.2", + "from": "async@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.4.2.tgz" + }, + "glob": { + "version": "5.0.14", + "from": "glob@>=5.0.3 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.14.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "grouped-queue": { + "version": "0.3.0", + "from": "grouped-queue@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.0.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "setimmediate": { + "version": "1.0.2", + "from": "setimmediate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz" + } + } + }, + "inquirer": { + "version": "0.8.5", + "from": "inquirer@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + }, + "cli-width": { + "version": "1.0.1", + "from": "cli-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.0.1.tgz" + }, + "figures": { + "version": "1.3.5", + "from": "figures@>=1.3.5 <2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.3.5.tgz" + }, + "readline2": { + "version": "0.1.1", + "from": "readline2@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", + "dependencies": { + "mute-stream": { + "version": "0.0.4", + "from": "mute-stream@0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" + }, + "strip-ansi": { + "version": "2.0.1", + "from": "strip-ansi@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz" + } + } + }, + "rx": { + "version": "2.5.3", + "from": "rx@>=2.4.3 <3.0.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "log-symbols": { + "version": "1.0.2", + "from": "log-symbols@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz" + }, + "mem-fs": { + "version": "1.1.0", + "from": "mem-fs@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.0.tgz", + "dependencies": { + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.3 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "vinyl": { + "version": "0.4.6", + "from": "vinyl@>=0.4.3 <0.5.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "dependencies": { + "clone": { + "version": "0.2.0", + "from": "clone@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + }, + "vinyl-file": { + "version": "1.2.1", + "from": "vinyl-file@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-1.2.1.tgz", + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "from": "strip-bom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "dependencies": { + "is-utf8": { + "version": "0.2.0", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" + } + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "from": "strip-bom-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "dependencies": { + "first-chunk-stream": { + "version": "1.0.0", + "from": "first-chunk-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" + } + } + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + } + } + } + } + }, + "text-table": { + "version": "0.2.0", + "from": "text-table@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + }, + "untildify": { + "version": "2.1.0", + "from": "untildify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.1", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" + } + } + } + } + }, + "yeoman-generator": { + "version": "0.20.3", + "from": "yeoman-generator@>=0.20.2 <0.21.0", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-0.20.3.tgz", + "dependencies": { + "async": { + "version": "1.4.2", + "from": "async@>=1.4.2 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.4.2.tgz" + }, + "class-extend": { + "version": "0.1.1", + "from": "class-extend@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/class-extend/-/class-extend-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "cli-table": { + "version": "0.3.1", + "from": "cli-table@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "dependencies": { + "colors": { + "version": "1.0.3", + "from": "colors@1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" + } + } + }, + "cross-spawn": { + "version": "2.0.0", + "from": "cross-spawn@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-2.0.0.tgz", + "dependencies": { + "cross-spawn-async": { + "version": "2.0.0", + "from": "cross-spawn-async@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.6.5 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "which": { + "version": "1.1.1", + "from": "which@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + } + } + } + } + }, + "spawn-sync": { + "version": "1.0.13", + "from": "spawn-sync@>=1.0.13 <2.0.0", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.13.tgz", + "dependencies": { + "concat-stream": { + "version": "1.5.0", + "from": "concat-stream@>=1.4.7 <2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "os-shim": { + "version": "0.1.3", + "from": "os-shim@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz" + } + } + } + } + }, + "dargs": { + "version": "4.0.1", + "from": "dargs@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + }, + "dateformat": { + "version": "1.0.11", + "from": "dateformat@>=1.0.11 <2.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.11.tgz", + "dependencies": { + "get-stdin": { + "version": "5.0.0", + "from": "get-stdin@*", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.0.tgz" + }, + "meow": { + "version": "3.3.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + } + } + }, + "detect-conflict": { + "version": "1.0.0", + "from": "detect-conflict@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.0.tgz" + }, + "diff": { + "version": "2.1.0", + "from": "diff@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.1.0.tgz" + }, + "download": { + "version": "4.2.0", + "from": "download@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/download/-/download-4.2.0.tgz", + "dependencies": { + "caw": { + "version": "1.1.0", + "from": "caw@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/caw/-/caw-1.1.0.tgz", + "dependencies": { + "get-proxy": { + "version": "1.0.1", + "from": "get-proxy@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.0.1.tgz", + "dependencies": { + "rc": { + "version": "0.5.5", + "from": "rc@>=0.5.5 <0.6.0", + "resolved": "https://registry.npmjs.org/rc/-/rc-0.5.5.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.7 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + }, + "deep-extend": { + "version": "0.2.11", + "from": "deep-extend@>=0.2.5 <0.3.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.2.11.tgz" + }, + "strip-json-comments": { + "version": "0.1.3", + "from": "strip-json-comments@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz" + }, + "ini": { + "version": "1.3.4", + "from": "ini@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + } + } + } + } + }, + "is-obj": { + "version": "1.0.0", + "from": "is-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "tunnel-agent": { + "version": "0.4.1", + "from": "tunnel-agent@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.1.tgz" + } + } + }, + "concat-stream": { + "version": "1.5.0", + "from": "concat-stream@>=1.4.7 <2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "each-async": { + "version": "1.1.1", + "from": "each-async@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", + "dependencies": { + "onetime": { + "version": "1.0.0", + "from": "onetime@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.0.0.tgz" + }, + "set-immediate-shim": { + "version": "1.0.1", + "from": "set-immediate-shim@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + } + } + }, + "filenamify": { + "version": "1.2.0", + "from": "filenamify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.0.tgz", + "dependencies": { + "filename-reserved-regex": { + "version": "1.0.0", + "from": "filename-reserved-regex@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz" + }, + "strip-outer": { + "version": "1.0.0", + "from": "strip-outer@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.0.tgz", + "dependencies": { + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + } + } + }, + "trim-repeated": { + "version": "1.0.0", + "from": "trim-repeated@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "dependencies": { + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + } + } + } + } + }, + "got": { + "version": "2.9.2", + "from": "got@>=2.3.2 <3.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-2.9.2.tgz", + "dependencies": { + "duplexify": { + "version": "3.4.2", + "from": "duplexify@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.4.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "from": "end-of-stream@1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "infinity-agent": { + "version": "2.0.3", + "from": "infinity-agent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/infinity-agent/-/infinity-agent-2.0.3.tgz" + }, + "is-stream": { + "version": "1.0.1", + "from": "is-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.0.1.tgz" + }, + "lowercase-keys": { + "version": "1.0.0", + "from": "lowercase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" + }, + "nested-error-stacks": { + "version": "1.0.1", + "from": "nested-error-stacks@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.1.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "prepend-http": { + "version": "1.0.3", + "from": "prepend-http@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.3.tgz" + }, + "read-all-stream": { + "version": "2.2.0", + "from": "read-all-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-2.2.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "statuses": { + "version": "1.2.1", + "from": "statuses@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + }, + "timed-out": { + "version": "2.0.0", + "from": "timed-out@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz" + } + } + }, + "gulp-decompress": { + "version": "1.1.0", + "from": "gulp-decompress@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.1.0.tgz", + "dependencies": { + "archive-type": { + "version": "3.0.1", + "from": "archive-type@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-3.0.1.tgz", + "dependencies": { + "file-type": { + "version": "2.10.2", + "from": "file-type@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-2.10.2.tgz", + "dependencies": { + "meow": { + "version": "3.3.0", + "from": "meow@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + } + } + } + } + }, + "decompress": { + "version": "2.3.0", + "from": "decompress@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-2.3.0.tgz", + "dependencies": { + "buffer-to-vinyl": { + "version": "1.0.1", + "from": "buffer-to-vinyl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.0.1.tgz", + "dependencies": { + "file-type": { + "version": "2.10.2", + "from": "file-type@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-2.10.2.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "uuid": { + "version": "2.0.1", + "from": "uuid@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz" + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + } + } + }, + "decompress-tar": { + "version": "3.1.0", + "from": "decompress-tar@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", + "dependencies": { + "is-tar": { + "version": "1.0.0", + "from": "is-tar@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz" + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "tar-stream": { + "version": "1.2.1", + "from": "tar-stream@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.2.1.tgz", + "dependencies": { + "bl": { + "version": "1.0.0", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.0.tgz" + }, + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "decompress-tarbz2": { + "version": "3.1.0", + "from": "decompress-tarbz2@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", + "dependencies": { + "is-bzip2": { + "version": "1.0.0", + "from": "is-bzip2@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz" + }, + "seek-bzip": { + "version": "1.0.5", + "from": "seek-bzip@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "dependencies": { + "commander": { + "version": "2.8.1", + "from": "commander@>=2.8.1 <2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + } + } + } + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "tar-stream": { + "version": "1.2.1", + "from": "tar-stream@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.2.1.tgz", + "dependencies": { + "bl": { + "version": "1.0.0", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.0.tgz" + }, + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "decompress-targz": { + "version": "3.1.0", + "from": "decompress-targz@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", + "dependencies": { + "is-gzip": { + "version": "1.0.0", + "from": "is-gzip@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz" + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "tar-stream": { + "version": "1.2.1", + "from": "tar-stream@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.2.1.tgz", + "dependencies": { + "bl": { + "version": "1.0.0", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.0.tgz" + }, + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "decompress-unzip": { + "version": "3.3.0", + "from": "decompress-unzip@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.3.0.tgz", + "dependencies": { + "is-zip": { + "version": "1.0.0", + "from": "is-zip@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz" + }, + "stat-mode": { + "version": "0.2.1", + "from": "stat-mode@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.1.tgz" + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + }, + "yauzl": { + "version": "2.3.1", + "from": "yauzl@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.3.1.tgz", + "dependencies": { + "fd-slicer": { + "version": "1.0.1", + "from": "fd-slicer@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + }, + "pend": { + "version": "1.2.0", + "from": "pend@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + } + } + } + } + }, + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "meow": { + "version": "3.3.0", + "from": "meow@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "vinyl-assign": { + "version": "1.2.0", + "from": "vinyl-assign@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.0.tgz", + "dependencies": { + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + } + } + }, + "gulp-util": { + "version": "3.0.6", + "from": "gulp-util@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.6.tgz", + "dependencies": { + "array-differ": { + "version": "1.0.0", + "from": "array-differ@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz" + }, + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + }, + "beeper": { + "version": "1.1.0", + "from": "beeper@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.0.tgz" + }, + "lodash._reescape": { + "version": "3.0.0", + "from": "lodash._reescape@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz" + }, + "lodash._reevaluate": { + "version": "3.0.0", + "from": "lodash._reevaluate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "from": "lodash._reinterpolate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" + }, + "lodash.template": { + "version": "3.6.2", + "from": "lodash.template@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "dependencies": { + "lodash._basecopy": { + "version": "3.0.1", + "from": "lodash._basecopy@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz" + }, + "lodash._basetostring": { + "version": "3.0.1", + "from": "lodash._basetostring@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz" + }, + "lodash._basevalues": { + "version": "3.0.0", + "from": "lodash._basevalues@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "from": "lodash._isiterateecall@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz" + }, + "lodash.escape": { + "version": "3.0.0", + "from": "lodash.escape@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.0.0.tgz" + }, + "lodash.keys": { + "version": "3.1.2", + "from": "lodash.keys@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "dependencies": { + "lodash._getnative": { + "version": "3.9.1", + "from": "lodash._getnative@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz" + }, + "lodash.isarguments": { + "version": "3.0.4", + "from": "lodash.isarguments@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.0.4.tgz" + }, + "lodash.isarray": { + "version": "3.0.4", + "from": "lodash.isarray@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz" + } + } + }, + "lodash.restparam": { + "version": "3.6.1", + "from": "lodash.restparam@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz" + }, + "lodash.templatesettings": { + "version": "3.1.0", + "from": "lodash.templatesettings@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.0.tgz" + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "multipipe": { + "version": "0.1.2", + "from": "multipipe@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.1.9 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + } + } + } + } + }, + "gulp-rename": { + "version": "1.2.2", + "from": "gulp-rename@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz" + }, + "is-url": { + "version": "1.2.1", + "from": "is-url@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.1.tgz" + }, + "object-assign": { + "version": "2.1.1", + "from": "object-assign@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz" + }, + "read-all-stream": { + "version": "3.0.1", + "from": "read-all-stream@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.0.1.tgz", + "dependencies": { + "pinkie-promise": { + "version": "1.0.0", + "from": "pinkie-promise@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "dependencies": { + "pinkie": { + "version": "1.0.0", + "from": "pinkie@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "stream-combiner2": { + "version": "1.0.2", + "from": "stream-combiner2@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.0.2.tgz", + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.1.9 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "through2": { + "version": "0.5.1", + "from": "through2@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "3.0.0", + "from": "xtend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz" + } + } + } + } + }, + "vinyl": { + "version": "0.4.6", + "from": "vinyl@>=0.4.3 <0.5.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "dependencies": { + "clone": { + "version": "0.2.0", + "from": "clone@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + }, + "vinyl-fs": { + "version": "1.0.0", + "from": "vinyl-fs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-1.0.0.tgz", + "dependencies": { + "duplexify": { + "version": "3.4.2", + "from": "duplexify@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.4.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "from": "end-of-stream@1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "glob-stream": { + "version": "4.1.1", + "from": "glob-stream@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-4.1.1.tgz", + "dependencies": { + "glob": { + "version": "4.5.3", + "from": "glob@>=4.3.1 <5.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "from": "ordered-read-streams@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz" + }, + "glob2base": { + "version": "0.0.12", + "from": "glob2base@>=0.0.12 <0.0.13", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "dependencies": { + "find-index": { + "version": "0.1.1", + "from": "find-index@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz" + } + } + }, + "unique-stream": { + "version": "2.2.0", + "from": "unique-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.0.tgz", + "dependencies": { + "through2-filter": { + "version": "2.0.0", + "from": "through2-filter@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "dependencies": { + "through2": { + "version": "2.0.0", + "from": "through2@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + } + } + }, + "glob-watcher": { + "version": "0.0.8", + "from": "glob-watcher@>=0.0.8 <0.0.9", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.8.tgz", + "dependencies": { + "gaze": { + "version": "0.5.1", + "from": "gaze@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.1.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "from": "globule@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "from": "lodash@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + }, + "glob": { + "version": "3.1.21", + "from": "glob@>=3.1.21 <3.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.2", + "from": "inherits@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz" + } + } + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + } + } + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "merge-stream": { + "version": "0.1.8", + "from": "merge-stream@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-0.1.8.tgz" + }, + "strip-bom": { + "version": "1.0.0", + "from": "strip-bom@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "dependencies": { + "first-chunk-stream": { + "version": "1.0.0", + "from": "first-chunk-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" + }, + "is-utf8": { + "version": "0.2.0", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "ware": { + "version": "1.3.0", + "from": "ware@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", + "dependencies": { + "wrap-fn": { + "version": "0.1.4", + "from": "wrap-fn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.4.tgz", + "dependencies": { + "co": { + "version": "3.1.0", + "from": "co@3.1.0", + "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz" + } + } + } + } + } + } + }, + "findup-sync": { + "version": "0.2.1", + "from": "findup-sync@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz", + "dependencies": { + "glob": { + "version": "4.3.5", + "from": "glob@>=4.3.0 <4.4.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + } + } + }, + "github-username": { + "version": "2.0.0", + "from": "github-username@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-2.0.0.tgz", + "dependencies": { + "gh-got": { + "version": "1.1.0", + "from": "gh-got@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-1.1.0.tgz", + "dependencies": { + "got": { + "version": "3.3.1", + "from": "got@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz", + "dependencies": { + "duplexify": { + "version": "3.4.2", + "from": "duplexify@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.4.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "from": "end-of-stream@1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "infinity-agent": { + "version": "2.0.3", + "from": "infinity-agent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/infinity-agent/-/infinity-agent-2.0.3.tgz" + }, + "is-redirect": { + "version": "1.0.0", + "from": "is-redirect@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz" + }, + "is-stream": { + "version": "1.0.1", + "from": "is-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.0.1.tgz" + }, + "lowercase-keys": { + "version": "1.0.0", + "from": "lowercase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" + }, + "nested-error-stacks": { + "version": "1.0.1", + "from": "nested-error-stacks@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.1.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "prepend-http": { + "version": "1.0.3", + "from": "prepend-http@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.3.tgz" + }, + "read-all-stream": { + "version": "3.0.1", + "from": "read-all-stream@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.0.1.tgz", + "dependencies": { + "pinkie-promise": { + "version": "1.0.0", + "from": "pinkie-promise@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "dependencies": { + "pinkie": { + "version": "1.0.0", + "from": "pinkie@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "timed-out": { + "version": "2.0.0", + "from": "timed-out@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz" + } + } + }, + "object-assign": { + "version": "2.1.1", + "from": "object-assign@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz" + } + } + }, + "meow": { + "version": "3.3.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + } + } + }, + "glob": { + "version": "5.0.14", + "from": "glob@>=5.0.3 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.14.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "gruntfile-editor": { + "version": "1.0.0", + "from": "gruntfile-editor@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gruntfile-editor/-/gruntfile-editor-1.0.0.tgz", + "dependencies": { + "ast-query": { + "version": "1.0.1", + "from": "ast-query@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ast-query/-/ast-query-1.0.1.tgz", + "dependencies": { + "escodegen": { + "version": "1.6.1", + "from": "escodegen@>=1.6.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.6.1.tgz", + "dependencies": { + "estraverse": { + "version": "1.9.3", + "from": "estraverse@>=1.9.1 <2.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz" + }, + "esutils": { + "version": "1.1.6", + "from": "esutils@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz" + }, + "esprima": { + "version": "1.2.5", + "from": "esprima@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz" + }, + "optionator": { + "version": "0.5.0", + "from": "optionator@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz", + "dependencies": { + "prelude-ls": { + "version": "1.1.2", + "from": "prelude-ls@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + }, + "deep-is": { + "version": "0.1.3", + "from": "deep-is@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" + }, + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + }, + "type-check": { + "version": "0.3.1", + "from": "type-check@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz" + }, + "levn": { + "version": "0.2.5", + "from": "levn@>=0.2.5 <0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz" + }, + "fast-levenshtein": { + "version": "1.0.7", + "from": "fast-levenshtein@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz" + } + } + }, + "source-map": { + "version": "0.1.43", + "from": "source-map@>=0.1.40 <0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + } + } + }, + "esprima": { + "version": "2.6.0", + "from": "esprima@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.6.0.tgz" + }, + "traverse": { + "version": "0.6.6", + "from": "traverse@>=0.6.6 <0.7.0", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz" + } + } + } + } + }, + "html-wiring": { + "version": "1.2.0", + "from": "html-wiring@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/html-wiring/-/html-wiring-1.2.0.tgz", + "dependencies": { + "cheerio": { + "version": "0.19.0", + "from": "cheerio@>=0.19.0 <0.20.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.19.0.tgz", + "dependencies": { + "css-select": { + "version": "1.0.0", + "from": "css-select@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.0.0.tgz", + "dependencies": { + "css-what": { + "version": "1.0.0", + "from": "css-what@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-1.0.0.tgz" + }, + "domutils": { + "version": "1.4.3", + "from": "domutils@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + } + } + }, + "boolbase": { + "version": "1.0.0", + "from": "boolbase@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + }, + "nth-check": { + "version": "1.0.1", + "from": "nth-check@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz" + } + } + }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + }, + "htmlparser2": { + "version": "3.8.3", + "from": "htmlparser2@>=3.8.1 <3.9.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "dependencies": { + "domhandler": { + "version": "2.3.0", + "from": "domhandler@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "from": "domutils@>=1.5.0 <1.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz" + }, + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "entities": { + "version": "1.0.0", + "from": "entities@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + } + } + }, + "dom-serializer": { + "version": "0.1.0", + "from": "dom-serializer@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "from": "domelementtype@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + } + } + } + } + }, + "detect-newline": { + "version": "1.0.3", + "from": "detect-newline@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-1.0.3.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + } + } + } + } + }, + "inquirer": { + "version": "0.8.5", + "from": "inquirer@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + }, + "cli-width": { + "version": "1.0.1", + "from": "cli-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.0.1.tgz" + }, + "figures": { + "version": "1.3.5", + "from": "figures@>=1.3.5 <2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.3.5.tgz" + }, + "readline2": { + "version": "0.1.1", + "from": "readline2@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", + "dependencies": { + "mute-stream": { + "version": "0.0.4", + "from": "mute-stream@0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" + }, + "strip-ansi": { + "version": "2.0.1", + "from": "strip-ansi@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz" + } + } + }, + "rx": { + "version": "2.5.3", + "from": "rx@>=2.4.3 <3.0.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "istextorbinary": { + "version": "1.0.2", + "from": "istextorbinary@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz", + "dependencies": { + "textextensions": { + "version": "1.0.1", + "from": "textextensions@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.1.tgz" + }, + "binaryextensions": { + "version": "1.0.0", + "from": "binaryextensions@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.0.tgz" + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "mem-fs-editor": { + "version": "2.0.4", + "from": "mem-fs-editor@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-2.0.4.tgz", + "dependencies": { + "commondir": { + "version": "1.0.1", + "from": "commondir@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + }, + "ejs": { + "version": "2.3.3", + "from": "ejs@>=2.3.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.3.3.tgz" + }, + "globby": { + "version": "2.1.0", + "from": "globby@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-2.1.0.tgz", + "dependencies": { + "array-union": { + "version": "1.0.1", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.1.tgz", + "dependencies": { + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "multimatch": { + "version": "2.0.0", + "from": "multimatch@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.0.0.tgz", + "dependencies": { + "array-differ": { + "version": "1.0.0", + "from": "array-differ@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz" + }, + "array-union": { + "version": "1.0.1", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.1.tgz", + "dependencies": { + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + } + } + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + } + } + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + } + } + }, + "mime": { + "version": "1.3.4", + "from": "mime@>=1.2.9 <2.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "nopt": { + "version": "3.0.3", + "from": "nopt@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.3.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.7", + "from": "abbrev@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz" + } + } + }, + "path-exists": { + "version": "1.0.0", + "from": "path-exists@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "pretty-bytes": { + "version": "2.0.1", + "from": "pretty-bytes@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-2.0.1.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "meow": { + "version": "3.3.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz" + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + }, + "read-chunk": { + "version": "1.0.1", + "from": "read-chunk@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz" + }, + "rimraf": { + "version": "2.4.3", + "from": "rimraf@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.3.tgz" + }, + "run-async": { + "version": "0.1.0", + "from": "run-async@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "shelljs": { + "version": "0.5.3", + "from": "shelljs@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz" + }, + "sinon": { + "version": "1.16.1", + "from": "sinon@>=1.9.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.16.1.tgz", + "dependencies": { + "formatio": { + "version": "1.1.1", + "from": "formatio@1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz" + }, + "util": { + "version": "0.10.3", + "from": "util@>=0.10.3 <1.0.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "lolex": { + "version": "1.3.1", + "from": "lolex@1.3.1", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.1.tgz" + }, + "samsam": { + "version": "1.1.2", + "from": "samsam@1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" + } + } + }, + "text-table": { + "version": "0.2.0", + "from": "text-table@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + }, + "through2": { + "version": "2.0.0", + "from": "through2@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "underscore.string": { + "version": "3.2.1", + "from": "underscore.string@>=3.0.3 <4.0.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.1.tgz" + }, + "user-home": { + "version": "2.0.0", + "from": "user-home@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.1", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" + } + } + }, + "xdg-basedir": { + "version": "2.0.0", + "from": "xdg-basedir@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.1", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" + } + } + }, + "yeoman-assert": { + "version": "2.0.0", + "from": "yeoman-assert@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/yeoman-assert/-/yeoman-assert-2.0.0.tgz" + }, + "yeoman-welcome": { + "version": "1.0.1", + "from": "yeoman-welcome@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/yeoman-welcome/-/yeoman-welcome-1.0.1.tgz" + } + } + } + } +} diff --git a/packager/react-packager/src/Server/__tests__/Server-test.js b/packager/react-packager/src/Server/__tests__/Server-test.js index da8e803f5..d9ec6dbe6 100644 --- a/packager/react-packager/src/Server/__tests__/Server-test.js +++ b/packager/react-packager/src/Server/__tests__/Server-test.js @@ -126,7 +126,7 @@ describe('processRequest', () => { expect(Bundler.prototype.bundle).toBeCalledWith( 'index.js', true, - 'index.map', + 'index.map?platform=ios', true, 'ios', ); @@ -271,7 +271,7 @@ describe('processRequest', () => { expect(Bundler.prototype.bundle).toBeCalledWith( 'path/to/foo.js', false, - '/path/to/foo.map', + '/path/to/foo.map?dev=false&runModule=false', false, undefined ); diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index 1ad6d862b..d727fd52a 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -429,8 +429,11 @@ class Server { return true; }).join('.') + '.js'; + const sourceMapUrlObj = _.clone(urlObj); + sourceMapUrlObj.pathname = pathname.replace(/\.bundle$/, '.map'); + return { - sourceMapUrl: pathname.replace(/\.bundle$/, '.map'), + sourceMapUrl: url.format(sourceMapUrlObj), entryFile: entryFile, dev: this._getBoolOptionFromQuery(urlObj.query, 'dev', true), minify: this._getBoolOptionFromQuery(urlObj.query, 'minify'), diff --git a/packager/react-packager/src/SocketInterface/SocketServer.js b/packager/react-packager/src/SocketInterface/SocketServer.js index d3cb752d1..b292b2bfa 100644 --- a/packager/react-packager/src/SocketInterface/SocketServer.js +++ b/packager/react-packager/src/SocketInterface/SocketServer.js @@ -15,7 +15,7 @@ const debug = require('debug')('ReactPackager:SocketServer'); const fs = require('fs'); const net = require('net'); -const MAX_IDLE_TIME = 10 * 60 * 1000; +const MAX_IDLE_TIME = 30 * 1000; class SocketServer { constructor(sockPath, options) { @@ -118,7 +118,7 @@ class SocketServer { this._deathTimer = setTimeout(() => { if (this._jobs <= 0) { debug('server dying', process.pid); - process.exit(1); + process.exit(); } this._dieEventually(); }, MAX_IDLE_TIME); diff --git a/packager/react-packager/src/lib/getCacheFilePath.js b/packager/react-packager/src/lib/getCacheFilePath.js index d5ef19b31..1d8585297 100644 --- a/packager/react-packager/src/lib/getCacheFilePath.js +++ b/packager/react-packager/src/lib/getCacheFilePath.js @@ -12,7 +12,7 @@ const crypto = require('crypto'); const path = require('path'); const tmpdir = require('os').tmpDir(); -function getCacheFilePath(args) { +function getCacheFilePath(...args) { args = Array.prototype.slice.call(args); const prefix = args.shift();