Merge pull request #1184 from frantic/sync-2015-05-06

Updates from Wed 6 May
This commit is contained in:
Alexander Kotliarskyi 2015-05-06 18:01:01 -07:00
commit 07937620c0
19 changed files with 273 additions and 148 deletions

View File

@ -17,6 +17,7 @@
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* SampleAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleAppTests.m */; }; 00E356F31AD99517003FC87E /* SampleAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleAppTests.m */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
@ -82,6 +83,13 @@
remoteGlobalIDString = 13B07F861A680F5B00A75B9A; remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = SampleApp; remoteInfo = SampleApp;
}; };
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTSettings;
};
146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 146834031AC3E56700842450 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */;
@ -117,6 +125,7 @@
00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356EE1AD99517003FC87E /* SampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = "<group>"; }; 00E356F21AD99517003FC87E /* SampleAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAppTests.m; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* SampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = "<group>"; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = "<group>"; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = "<group>"; };
@ -142,6 +151,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
146834051AC3E58100842450 /* libReact.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */,
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */,
00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */, 00481BE81AC0C86700671115 /* libRCTWebSocketDebugger.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */, 00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */,
@ -230,6 +240,14 @@
name = "Supporting Files"; name = "Supporting Files";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
139105B71AF99BAD00B5F7CC /* Products */ = {
isa = PBXGroup;
children = (
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */,
);
name = Products;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* SampleApp */ = { 13B07FAE1A68108700A75B9A /* SampleApp */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -263,14 +281,15 @@
832341AE1AAA6A7D00B99B32 /* Libraries */ = { 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */, 00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */,
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */, 00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */,
); );
@ -395,6 +414,10 @@
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
}, },
{
ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */;
ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */;
},
{ {
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
@ -470,6 +493,13 @@
remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTSettings.a;
remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
146834041AC3E56700842450 /* libReact.a */ = { 146834041AC3E56700842450 /* libReact.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;

View File

@ -24,7 +24,7 @@ var SampleApp = React.createClass({
</Text> </Text>
<Text style={styles.instructions}> <Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'} Press Cmd+R to reload,{'\n'}
Cmd+Control+Z for dev menu Cmd+D or shake for dev menu
</Text> </Text>
</View> </View>
); );

View File

@ -13,12 +13,14 @@
var LayoutPropTypes = require('LayoutPropTypes'); var LayoutPropTypes = require('LayoutPropTypes');
var ReactPropTypes = require('ReactPropTypes'); var ReactPropTypes = require('ReactPropTypes');
var TransformPropTypes = require('TransformPropTypes');
/** /**
* Warning: Some of these properties may not be supported in all releases. * Warning: Some of these properties may not be supported in all releases.
*/ */
var ViewStylePropTypes = { var ViewStylePropTypes = {
...LayoutPropTypes, ...LayoutPropTypes,
...TransformPropTypes,
backgroundColor: ReactPropTypes.string, backgroundColor: ReactPropTypes.string,
borderColor: ReactPropTypes.string, borderColor: ReactPropTypes.string,
borderTopColor: ReactPropTypes.string, borderTopColor: ReactPropTypes.string,
@ -34,15 +36,6 @@ var ViewStylePropTypes = {
), ),
shadowOpacity: ReactPropTypes.number, shadowOpacity: ReactPropTypes.number,
shadowRadius: ReactPropTypes.number, shadowRadius: ReactPropTypes.number,
transform: ReactPropTypes.arrayOf(ReactPropTypes.object),
transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number),
// DEPRECATED
rotation: ReactPropTypes.number,
scaleX: ReactPropTypes.number,
scaleY: ReactPropTypes.number,
translateX: ReactPropTypes.number,
translateY: ReactPropTypes.number,
}; };
module.exports = ViewStylePropTypes; module.exports = ViewStylePropTypes;

View File

@ -227,15 +227,17 @@ var Navigator = React.createClass({
renderScene: PropTypes.func.isRequired, renderScene: PropTypes.func.isRequired,
/** /**
* Provide a single "route" to start on. A route is an arbitrary object * Specify a route to start on. A route is an object that the navigator
* that the navigator will use to identify each scene before rendering. * will use to identify each scene to render. `initialRoute` must be
* Either initialRoute or initialRouteStack is required. * a route in the `initialRouteStack` if both props are provided. The
* `initialRoute` will default to the last item in the `initialRouteStack`.
*/ */
initialRoute: PropTypes.object, initialRoute: PropTypes.object,
/** /**
* Provide a set of routes to initially mount the scenes for. Required if no * Provide a set of routes to initially mount. Required if no initialRoute
* initialRoute is provided * is provided. Otherwise, it will default to an array containing only the
* `initialRoute`
*/ */
initialRouteStack: PropTypes.arrayOf(PropTypes.object), initialRouteStack: PropTypes.arrayOf(PropTypes.object),
@ -295,21 +297,18 @@ var Navigator = React.createClass({
}, },
getInitialState: function() { getInitialState: function() {
var routeStack = this.props.initialRouteStack || []; var routeStack = this.props.initialRouteStack || [this.props.initialRoute];
var initialRouteIndex = 0; invariant(
if (this.props.initialRoute && routeStack.length) { routeStack.length >= 1,
'Navigator requires props.initialRoute or props.initialRouteStack.'
);
var initialRouteIndex = routeStack.length - 1;
if (this.props.initialRoute) {
initialRouteIndex = routeStack.indexOf(this.props.initialRoute); initialRouteIndex = routeStack.indexOf(this.props.initialRoute);
invariant( invariant(
initialRouteIndex !== -1, initialRouteIndex !== -1,
'initialRoute is not in initialRouteStack.' 'initialRoute is not in initialRouteStack.'
); );
} else if (this.props.initialRoute) {
routeStack = [this.props.initialRoute];
} else {
invariant(
routeStack.length >= 1,
'Navigator requires props.initialRoute or props.initialRouteStack.'
);
} }
return { return {
sceneConfigStack: routeStack.map( sceneConfigStack: routeStack.map(
@ -689,7 +688,8 @@ var Navigator = React.createClass({
var enabledSceneNativeProps = { var enabledSceneNativeProps = {
left: sceneStyle.left, left: sceneStyle.left,
}; };
if (sceneIndex !== this.state.transitionFromIndex) { if (sceneIndex !== this.state.transitionFromIndex &&
sceneIndex !== this.state.presentedIndex) {
// If we are not in a transition from this index, make sure opacity is 0 // If we are not in a transition from this index, make sure opacity is 0
// to prevent the enabled scene from flashing over the presented scene // to prevent the enabled scene from flashing over the presented scene
enabledSceneNativeProps.opacity = 0; enabledSceneNativeProps.opacity = 0;

View File

@ -14,9 +14,11 @@
var ImageResizeMode = require('ImageResizeMode'); var ImageResizeMode = require('ImageResizeMode');
var LayoutPropTypes = require('LayoutPropTypes'); var LayoutPropTypes = require('LayoutPropTypes');
var ReactPropTypes = require('ReactPropTypes'); var ReactPropTypes = require('ReactPropTypes');
var TransformPropTypes = require('TransformPropTypes');
var ImageStylePropTypes = { var ImageStylePropTypes = {
...LayoutPropTypes, ...LayoutPropTypes,
...TransformPropTypes,
resizeMode: ReactPropTypes.oneOf(Object.keys(ImageResizeMode)), resizeMode: ReactPropTypes.oneOf(Object.keys(ImageResizeMode)),
backgroundColor: ReactPropTypes.string, backgroundColor: ReactPropTypes.string,
borderColor: ReactPropTypes.string, borderColor: ReactPropTypes.string,

View File

@ -66,7 +66,7 @@ class XMLHttpRequestBase {
getResponseHeader(header: string): ?string { getResponseHeader(header: string): ?string {
if (this.responseHeaders) { if (this.responseHeaders) {
var value = this.responseHeaders[header]; var value = this.responseHeaders[header.toLowerCase()];
return value !== undefined ? value : null; return value !== undefined ? value : null;
} }
return null; return null;
@ -132,7 +132,12 @@ class XMLHttpRequestBase {
return; return;
} }
this.status = status; this.status = status;
this.responseHeaders = responseHeaders || {}; // Headers should be case-insensitive
var lcResponseHeaders = {};
for (var header in responseHeaders) {
lcResponseHeaders[header.toLowerCase()] = responseHeaders[header];
}
this.responseHeaders = lcResponseHeaders;
this.responseText = responseText; this.responseText = responseText;
this._setReadyState(this.DONE); this._setReadyState(this.DONE);
this._sendLoad(); this._sendLoad();

View File

@ -12,6 +12,7 @@
#import "RCTBridge.h" #import "RCTBridge.h"
#import "RCTConvert.h" #import "RCTConvert.h"
#import "RCTEventDispatcher.h" #import "RCTEventDispatcher.h"
#import "RCTUtils.h"
@implementation RCTSettingsManager @implementation RCTSettingsManager
{ {
@ -53,13 +54,15 @@ RCT_EXPORT_MODULE()
return; return;
} }
[_bridge.eventDispatcher sendDeviceEventWithName:@"settingsUpdated" body:[_defaults dictionaryRepresentation]]; [_bridge.eventDispatcher
sendDeviceEventWithName:@"settingsUpdated"
body:RCTJSONClean([_defaults dictionaryRepresentation])];
} }
- (NSDictionary *)constantsToExport - (NSDictionary *)constantsToExport
{ {
return @{ return @{
@"settings": [_defaults dictionaryRepresentation] @"settings": RCTJSONClean([_defaults dictionaryRepresentation])
}; };
} }

View File

@ -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.
*
* @providesModule TransformPropTypes
* @flow
*/
'use strict';
var ReactPropTypes = require('ReactPropTypes');
var TransformPropTypes = {
transform: ReactPropTypes.arrayOf(ReactPropTypes.object),
transformMatrix: ReactPropTypes.arrayOf(ReactPropTypes.number),
// DEPRECATED
rotation: ReactPropTypes.number,
scaleX: ReactPropTypes.number,
scaleY: ReactPropTypes.number,
translateX: ReactPropTypes.number,
translateY: ReactPropTypes.number,
};
module.exports = TransformPropTypes;

View File

@ -39,6 +39,7 @@
+ (NSString *)NSString:(id)json; + (NSString *)NSString:(id)json;
+ (NSNumber *)NSNumber:(id)json; + (NSNumber *)NSNumber:(id)json;
+ (NSData *)NSData:(id)json; + (NSData *)NSData:(id)json;
+ (NSIndexSet *)NSIndexSet:(id)json;
+ (NSURL *)NSURL:(id)json; + (NSURL *)NSURL:(id)json;
+ (NSURLRequest *)NSURLRequest:(id)json; + (NSURLRequest *)NSURLRequest:(id)json;
@ -76,6 +77,7 @@
+ (UIImage *)UIImage:(id)json; + (UIImage *)UIImage:(id)json;
+ (CGImageRef)CGImage:(id)json; + (CGImageRef)CGImage:(id)json;
+ (UIFont *)UIFont:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json; + (UIFont *)UIFont:(UIFont *)font withSize:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json; + (UIFont *)UIFont:(UIFont *)font withWeight:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json; + (UIFont *)UIFont:(UIFont *)font withStyle:(id)json;

View File

@ -64,6 +64,20 @@ RCT_CONVERTER(NSString *, NSString, description)
return [[self NSString:json] dataUsingEncoding:NSUTF8StringEncoding]; return [[self NSString:json] dataUsingEncoding:NSUTF8StringEncoding];
} }
+ (NSIndexSet *)NSIndexSet:(id)json
{
json = [self NSNumberArray:json];
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
for (NSNumber *number in json) {
NSInteger index = number.integerValue;
if (RCT_DEBUG && index < 0) {
RCTLogError(@"Invalid index value %zd. Indices must be positive.", index);
}
[indexSet addIndex:index];
}
return indexSet;
}
+ (NSURL *)NSURL:(id)json + (NSURL *)NSURL:(id)json
{ {
NSString *path = [self NSString:json]; NSString *path = [self NSString:json];
@ -679,6 +693,16 @@ static BOOL RCTFontIsCondensed(UIFont *font)
return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0; return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0;
} }
+ (UIFont *)UIFont:(id)json
{
json = [self NSDictionary:json];
return [self UIFont:nil
withFamily:json[@"fontFamily"]
size:json[@"fontSize"]
weight:json[@"fontWeight"]
style:json[@"fontStyle"]];
}
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json + (UIFont *)UIFont:(UIFont *)font withSize:(id)json
{ {
return [self UIFont:font withFamily:nil size:json weight:nil style:nil]; return [self UIFont:font withFamily:nil size:json weight:nil style:nil];
@ -728,11 +752,6 @@ static BOOL RCTFontIsCondensed(UIFont *font)
// Get font family // Get font family
familyName = [self NSString:family] ?: familyName; familyName = [self NSString:family] ?: familyName;
// Get font style
if (style) {
isItalic = [self RCTFontStyle:style];
}
// Gracefully handle being given a font name rather than font family, for // Gracefully handle being given a font name rather than font family, for
// example: "Helvetica Light Oblique" rather than just "Helvetica". // example: "Helvetica Light Oblique" rather than just "Helvetica".
if ([UIFont fontNamesForFamilyName:familyName].count == 0) { if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
@ -751,6 +770,11 @@ static BOOL RCTFontIsCondensed(UIFont *font)
} }
} }
// Get font style
if (style) {
isItalic = [self RCTFontStyle:style];
}
// Get font weight // Get font weight
if (weight) { if (weight) {
fontWeight = [self RCTFontWeight:weight]; fontWeight = [self RCTFontWeight:weight];
@ -758,13 +782,7 @@ static BOOL RCTFontIsCondensed(UIFont *font)
// Get the closest font that matches the given weight for the fontFamily // Get the closest font that matches the given weight for the fontFamily
UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize]; UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize];
CGFloat closestWeight; CGFloat closestWeight = INFINITY;
if (font && [font.familyName isEqualToString: familyName]) {
closestWeight = RCTWeightOfFont(font);
} else {
closestWeight = INFINITY;
}
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) { for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
UIFont *match = [UIFont fontWithName:name size:fontSize]; UIFont *match = [UIFont fontWithName:name size:fontSize];
@ -834,7 +852,9 @@ static id RCTConvertPropertyListValue(id json)
{ {
if (!json || json == (id)kCFNull) { if (!json || json == (id)kCFNull) {
return nil; return nil;
} else if ([json isKindOfClass:[NSDictionary class]]) { }
if ([json isKindOfClass:[NSDictionary class]]) {
__block BOOL copy = NO; __block BOOL copy = NO;
NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]]; NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]];
[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, BOOL *stop) { [json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, BOOL *stop) {
@ -845,7 +865,9 @@ static id RCTConvertPropertyListValue(id json)
copy |= value != jsonValue; copy |= value != jsonValue;
}]; }];
return copy ? values : json; return copy ? values : json;
} else if ([json isKindOfClass:[NSArray class]]) { }
if ([json isKindOfClass:[NSArray class]]) {
__block BOOL copy = NO; __block BOOL copy = NO;
__block NSArray *values = json; __block NSArray *values = json;
[json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) { [json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) {
@ -860,15 +882,17 @@ static id RCTConvertPropertyListValue(id json)
for (NSInteger i = 0; i < idx; i++) { for (NSInteger i = 0; i < idx; i++) {
[(NSMutableArray *)values addObject:json[i]]; [(NSMutableArray *)values addObject:json[i]];
} }
[(NSMutableArray *)values addObject:value]; if (value) {
[(NSMutableArray *)values addObject:value];
}
copy = YES; copy = YES;
} }
}]; }];
return values; return values;
} else {
// All other JSON types are supported by property lists
return json;
} }
// All other JSON types are supported by property lists
return json;
} }
+ (NSPropertyList)NSPropertyList:(id)json + (NSPropertyList)NSPropertyList:(id)json

View File

@ -66,9 +66,7 @@ RCT_EXPORT_MODULE()
// We're swizzling here because it's poor form to override methods in a category, // We're swizzling here because it's poor form to override methods in a category,
// however UIWindow doesn't actually implement motionEnded:withEvent:, so there's // however UIWindow doesn't actually implement motionEnded:withEvent:, so there's
// no need to call the original implementation. // no need to call the original implementation.
#if RCT_DEV
RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:)); RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:));
#endif
} }
- (instancetype)init - (instancetype)init
@ -121,8 +119,6 @@ RCT_EXPORT_MODULE()
- (void)updateSettings - (void)updateSettings
{ {
_settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]];
__weak RCTDevMenu *weakSelf = self; __weak RCTDevMenu *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
RCTDevMenu *strongSelf = weakSelf; RCTDevMenu *strongSelf = weakSelf;
@ -130,6 +126,8 @@ RCT_EXPORT_MODULE()
return; return;
} }
strongSelf->_settings = [NSMutableDictionary dictionaryWithDictionary:[strongSelf->_defaults objectForKey:RCTDevMenuSettingsKey]];
strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue]; strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue];
strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue]; strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue];
strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue]; strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue];

View File

@ -19,6 +19,9 @@
RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error); RCT_EXTERN NSString *RCTJSONStringify(id jsonObject, NSError **error);
RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error); RCT_EXTERN id RCTJSONParse(NSString *jsonString, NSError **error);
// Strip non JSON-safe values from an object graph
RCT_EXTERN id RCTJSONClean(id object);
// Get MD5 hash of a string (TODO: currently unused. Remove?) // Get MD5 hash of a string (TODO: currently unused. Remove?)
RCT_EXTERN NSString *RCTMD5Hash(NSString *string); RCT_EXTERN NSString *RCTMD5Hash(NSString *string);

View File

@ -20,7 +20,7 @@
NSString *RCTJSONStringify(id jsonObject, NSError **error) NSString *RCTJSONStringify(id jsonObject, NSError **error)
{ {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:error]; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:(NSJSONWritingOptions)NSJSONReadingAllowFragments error:error];
return jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] : nil; return jsonData ? [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding] : nil;
} }
@ -42,6 +42,57 @@ id RCTJSONParse(NSString *jsonString, NSError **error)
return [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:error]; return [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:error];
} }
id RCTJSONClean(id object)
{
static dispatch_once_t onceToken;
static NSSet *validLeafTypes;
dispatch_once(&onceToken, ^{
validLeafTypes = [[NSSet alloc] initWithArray:@[
[NSString class],
[NSMutableString class],
[NSNumber class],
[NSNull class],
]];
});
if ([validLeafTypes containsObject:[object classForCoder]]) {
return object;
}
if ([object isKindOfClass:[NSDictionary class]]) {
__block BOOL copy = NO;
NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[object count]];
[object enumerateKeysAndObjectsUsingBlock:^(NSString *key, id item, BOOL *stop) {
id value = RCTJSONClean(item);
values[key] = value;
copy |= value != item;
}];
return copy ? values : object;
}
if ([object isKindOfClass:[NSArray class]]) {
__block BOOL copy = NO;
__block NSArray *values = object;
[object enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
id value = RCTJSONClean(item);
if (copy) {
[(NSMutableArray *)values addObject:value];
} else if (value != item) {
// Converted value is different, so we'll need to copy the array
values = [[NSMutableArray alloc] initWithCapacity:values.count];
for (NSInteger i = 0; i < idx; i++) {
[(NSMutableArray *)values addObject:object[i]];
}
[(NSMutableArray *)values addObject:value];
copy = YES;
}
}];
return values;
}
return (id)kCFNull;
}
NSString *RCTMD5Hash(NSString *string) NSString *RCTMD5Hash(NSString *string)
{ {
const char *str = [string UTF8String]; const char *str = [string UTF8String];

View File

@ -45,6 +45,6 @@
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, assign) NSTimeInterval scrollEventThrottle; @property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
@property (nonatomic, assign) BOOL centerContent; @property (nonatomic, assign) BOOL centerContent;
@property (nonatomic, copy) NSArray *stickyHeaderIndices; @property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
@end @end

View File

@ -28,8 +28,8 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
*/ */
@interface RCTCustomScrollView : UIScrollView<UIGestureRecognizerDelegate> @interface RCTCustomScrollView : UIScrollView<UIGestureRecognizerDelegate>
@property (nonatomic, copy, readwrite) NSArray *stickyHeaderIndices; @property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
@property (nonatomic, readwrite, assign) BOOL centerContent; @property (nonatomic, assign) BOOL centerContent;
@end @end
@ -155,97 +155,72 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
[super setContentOffset:contentOffset]; [super setContentOffset:contentOffset];
} }
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
[self dockClosestSectionHeader];
}
- (void)dockClosestSectionHeader - (void)dockClosestSectionHeader
{ {
UIView *contentView = [self contentView]; UIView *contentView = [self contentView];
if (_stickyHeaderIndices.count == 0 || !contentView) { CGFloat scrollTop = self.bounds.origin.y + self.contentInset.top;
// Find the section headers that need to be docked
__block UIView *previousHeader = nil;
__block UIView *currentHeader = nil;
__block UIView *nextHeader = nil;
NSInteger subviewCount = contentView.reactSubviews.count;
[_stickyHeaderIndices enumerateIndexesWithOptions:0 usingBlock:^(NSUInteger idx, BOOL *stop) {
if (idx >= subviewCount) {
RCTLogError(@"Sticky header index %zd was outside the range {0, %zd}", idx, subviewCount);
return;
}
UIView *header = contentView.reactSubviews[idx];
// If nextHeader not yet found, search for docked headers
if (!nextHeader) {
CGFloat height = header.bounds.size.height;
CGFloat top = header.center.y - height * header.layer.anchorPoint.y;
if (top > scrollTop) {
nextHeader = header;
} else {
previousHeader = currentHeader;
currentHeader = header;
}
}
// Reset transforms for header views
header.transform = CGAffineTransformIdentity;
header.layer.zPosition = ZINDEX_DEFAULT;
}];
// If no docked header, bail out
if (!currentHeader) {
return; return;
} }
// find the section header that needs to be docked // Adjust current header to hug the top of the screen
NSInteger firstIndexInView = [[_stickyHeaderIndices firstObject] integerValue] + 1; CGFloat currentFrameHeight = currentHeader.bounds.size.height;
CGRect scrollBounds = self.bounds; CGFloat currentFrameTop = currentHeader.center.y - currentFrameHeight * currentHeader.layer.anchorPoint.y;
scrollBounds.origin.x += self.contentInset.left; CGFloat yOffset = scrollTop - currentFrameTop;
scrollBounds.origin.y += self.contentInset.top; if (nextHeader) {
// The next header nudges the current header out of the way when it reaches
NSInteger i = 0; // the top of the screen
for (UIView *subview in contentView.reactSubviews) { CGFloat nextFrameHeight = nextHeader.bounds.size.height;
CGRect rowFrame = [RCTCustomScrollView _calculateUntransformedFrame:subview]; CGFloat nextFrameTop = nextHeader.center.y - nextFrameHeight * nextHeader.layer.anchorPoint.y;
if (CGRectIntersectsRect(scrollBounds, rowFrame)) { CGFloat overlap = currentFrameHeight - (nextFrameTop - scrollTop);
firstIndexInView = i; yOffset -= MAX(0, overlap);
break;
}
i++;
} }
NSInteger stickyHeaderii = 0; currentHeader.transform = CGAffineTransformMakeTranslation(0, yOffset);
for (NSNumber *stickyHeaderI in _stickyHeaderIndices) { currentHeader.layer.zPosition = ZINDEX_STICKY_HEADER;
if ([stickyHeaderI integerValue] > firstIndexInView) {
break;
}
stickyHeaderii++;
}
stickyHeaderii = MAX(0, stickyHeaderii - 1);
// Set up transforms for the various section headers
NSInteger currentlyDockedIndex = [_stickyHeaderIndices[stickyHeaderii] integerValue];
NSInteger previouslyDockedIndex = stickyHeaderii > 0 ? [_stickyHeaderIndices[stickyHeaderii-1] integerValue] : -1;
NSInteger nextDockedIndex = (stickyHeaderii < _stickyHeaderIndices.count - 1) ?
[_stickyHeaderIndices[stickyHeaderii + 1] integerValue] : -1;
UIView *currentHeader = contentView.reactSubviews[currentlyDockedIndex];
UIView *previousHeader = previouslyDockedIndex >= 0 ? contentView.reactSubviews[previouslyDockedIndex] : nil;
CGRect curFrame = [RCTCustomScrollView _calculateUntransformedFrame:currentHeader];
if (previousHeader) { if (previousHeader) {
// the previous header is offset to sit right above the currentlyDockedHeader's initial position // The previous header sits right above the currentHeader's initial position
// (so it scrolls away nicely once the currentHeader locks into position) // so it scrolls away nicely once the currentHeader has locked into place
CGRect previousFrame = [RCTCustomScrollView _calculateUntransformedFrame:previousHeader]; CGFloat previousFrameHeight = previousHeader.bounds.size.height;
CGFloat yOffset = curFrame.origin.y - previousFrame.origin.y - previousFrame.size.height; CGFloat targetCenter = currentFrameTop - previousFrameHeight * (1.0 - previousHeader.layer.anchorPoint.y);
yOffset = targetCenter - previousHeader.center.y;
previousHeader.transform = CGAffineTransformMakeTranslation(0, yOffset); previousHeader.transform = CGAffineTransformMakeTranslation(0, yOffset);
previousHeader.layer.zPosition = ZINDEX_STICKY_HEADER;
} }
UIView *nextHeader = nextDockedIndex >= 0 ? contentView.reactSubviews[nextDockedIndex] : nil;
CGRect nextFrame = [RCTCustomScrollView _calculateUntransformedFrame:nextHeader];
if (curFrame.origin.y < scrollBounds.origin.y) {
// scrolled off (or being scrolled off) the top of the screen
CGFloat yOffset = 0;
if (nextHeader && nextFrame.origin.y < scrollBounds.origin.y + curFrame.size.height) {
// next frame is bumping me off if scrolling down (or i'm bumping the next one off if scrolling up)
yOffset = nextFrame.origin.y - curFrame.origin.y - curFrame.size.height;
} else {
// standard sticky header position
yOffset = scrollBounds.origin.y - curFrame.origin.y;
}
currentHeader.transform = CGAffineTransformMakeTranslation(0, yOffset);
currentHeader.layer.zPosition = ZINDEX_STICKY_HEADER;
} else {
// i'm the current header but in the viewport, so just scroll in normal position
currentHeader.transform = CGAffineTransformIdentity;
currentHeader.layer.zPosition = ZINDEX_DEFAULT;
}
// in our setup, 'next header' will always just scroll with the page
if (nextHeader) {
nextHeader.transform = CGAffineTransformIdentity;
nextHeader.layer.zPosition = ZINDEX_DEFAULT;
}
}
+ (CGRect)_calculateUntransformedFrame:(UIView *)view
{
CGRect frame = CGRectNull;
if (view) {
frame.size = view.bounds.size;
frame.origin = CGPointMake(view.layer.position.x - view.bounds.size.width * view.layer.anchorPoint.x, view.layer.position.y - view.bounds.size.height * view.layer.anchorPoint.y);
}
return frame;
} }
@end @end
@ -312,7 +287,7 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
_scrollView.centerContent = centerContent; _scrollView.centerContent = centerContent;
} }
- (void)setStickyHeaderIndices:(NSArray *)headerIndices - (void)setStickyHeaderIndices:(NSIndexSet *)headerIndices
{ {
RCTAssert(_scrollView.contentSize.width <= self.frame.size.width, RCTAssert(_scrollView.contentSize.width <= self.frame.size.width,
@"sticky headers are not supported with horizontal scrolled views"); @"sticky headers are not supported with horizontal scrolled views");
@ -340,14 +315,8 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
- (void)setContentInset:(UIEdgeInsets)contentInset - (void)setContentInset:(UIEdgeInsets)contentInset
{ {
CGPoint contentOffset = _scrollView.contentOffset;
_contentInset = contentInset; _contentInset = contentInset;
[RCTView autoAdjustInsetsForView:self [self setNeedsLayout];
withScrollView:_scrollView
updateOffset:NO];
_scrollView.contentOffset = contentOffset;
} }
- (void)scrollToOffset:(CGPoint)offset - (void)scrollToOffset:(CGPoint)offset
@ -390,6 +359,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView - (void)scrollViewDidScroll:(UIScrollView *)scrollView
{ {
[_scrollView dockClosestSectionHeader];
[self updateClippedSubviews]; [self updateClippedSubviews];
NSTimeInterval now = CACurrentMediaTime(); NSTimeInterval now = CACurrentMediaTime();

View File

@ -41,7 +41,7 @@ RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL) RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL) RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSNumberArray) RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices, NSIndexSet)
RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval) RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat) RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)

View File

@ -48,6 +48,7 @@
"chalk": "^1.0.0", "chalk": "^1.0.0",
"connect": "2.8.3", "connect": "2.8.3",
"debug": "~2.1.0", "debug": "~2.1.0",
"graceful-fs": "^3.0.6",
"image-size": "0.3.5", "image-size": "0.3.5",
"joi": "~5.1.0", "joi": "~5.1.0",
"jstransform": "10.1.0", "jstransform": "10.1.0",

View File

@ -10,7 +10,7 @@
on run argv on run argv
set theURL to item 1 of argv set theURL to item 1 of argv
tell application "Google Chrome" tell application "Chrome"
activate activate
if (count every window) = 0 then if (count every window) = 0 then

View File

@ -8,6 +8,8 @@
*/ */
'use strict'; 'use strict';
useGracefulFs();
var Activity = require('./src/Activity'); var Activity = require('./src/Activity');
var Server = require('./src/Server'); var Server = require('./src/Server');
@ -45,3 +47,16 @@ exports.getDependencies = function(options, main) {
return r.dependencies; return r.dependencies;
}); });
}; };
function useGracefulFs() {
var fs = require('fs');
var gracefulFs = require('graceful-fs');
// A bit sneaky but it's not straightforward to update all the
// modules we depend on.
Object.keys(fs).forEach(function(method) {
if (typeof fs[method] === 'function' && gracefulFs[method]) {
fs[method] = gracefulFs[method];
}
});
}