Lin Yiting

Init: Copy from git@github.com:facebookarchive/react-native-custom-components.git

  1 +LICENSE AGREEMENT
  2 +
  3 +For React Native Custom Components software
  4 +
  5 +Copyright (c) 2015, Facebook, Inc. All rights reserved.
  6 +
  7 +Facebook, Inc. (“Facebook”) owns all right, title and interest, including all intellectual property and other proprietary rights, in and to the React Native Custom Components software (the “Software”). Subject to your compliance with these terms, you are hereby granted a non-exclusive, worldwide, royalty-free copyright license to (1) use and copy the Software; and (2) reproduce and distribute the Software as part of your own software (“Your Software”). Facebook reserves all rights not expressly granted to you in this license agreement.
  8 +
  9 +THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED. IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1 +# React Native Legacy Custom Components
  2 +
  3 +This is a module for legacy "CustomComponents" to gracefully deprecate.
  4 +
  5 +## Navigator
  6 +
  7 +The navigator component in this module will behave identically as the one in old version of React native, with one exception:
  8 +
  9 +Latest documentation is available here: http://facebook.github.io/react-native/releases/0.43/docs/navigator.html
  10 +
  11 +
  12 +### Breaking Changes from react-native
  13 +
  14 +- Navigator.props.sceneStyle must be a plain object, not a stylesheet!
  15 +
  16 +(this breaking change is needed to avoid calling React Native's private APIs)
  1 +{
  2 + "name": "react-native-deprecated-custom-components",
  3 + "version": "0.1.1",
  4 + "description": "Deprecated custom components that originally shipped with React Native",
  5 + "repository": {
  6 + "type": "git",
  7 + "url": "git@github.com:facebookarchive/react-native-custom-components.git"
  8 + },
  9 + "main": "src/CustomComponents.js",
  10 + "dependencies": {
  11 + "fbjs": "~0.8.9",
  12 + "immutable": "~3.7.6",
  13 + "create-react-class": "15.6.0",
  14 + "prop-types": "^15.5.10",
  15 + "react-timer-mixin": "^0.13.2",
  16 + "rebound": "^0.0.13"
  17 + },
  18 + "peerDependencies": {
  19 + "react-native": "*"
  20 + }
  21 +}
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +
  27 +
  28 +const Navigator = require('./Navigator');
  29 +
  30 +module.exports = {
  31 + Navigator,
  32 +};
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @noflow
  26 + */
  27 +'use strict';
  28 +
  29 +const EventSubscription = require('./EventSubscription');
  30 +
  31 +import type EventEmitter from './EventEmitter';
  32 +import type EventSubscriptionVendor from './EventSubscriptionVendor';
  33 +
  34 +/**
  35 + * EmitterSubscription represents a subscription with listener and context data.
  36 + */
  37 +class EmitterSubscription extends EventSubscription {
  38 +
  39 + emitter: EventEmitter;
  40 + listener: Function;
  41 + context: ?Object;
  42 +
  43 + /**
  44 + * @param {EventEmitter} emitter - The event emitter that registered this
  45 + * subscription
  46 + * @param {EventSubscriptionVendor} subscriber - The subscriber that controls
  47 + * this subscription
  48 + * @param {function} listener - Function to invoke when the specified event is
  49 + * emitted
  50 + * @param {*} context - Optional context object to use when invoking the
  51 + * listener
  52 + */
  53 + constructor(
  54 + emitter: EventEmitter,
  55 + subscriber: EventSubscriptionVendor,
  56 + listener: Function,
  57 + context: ?Object
  58 + ) {
  59 + super(subscriber);
  60 + this.emitter = emitter;
  61 + this.listener = listener;
  62 + this.context = context;
  63 + }
  64 +
  65 + /**
  66 + * Removes this subscription from the emitter that registered it.
  67 + * Note: we're overriding the `remove()` method of EventSubscription here
  68 + * but deliberately not calling `super.remove()` as the responsibility
  69 + * for removing the subscription lies with the EventEmitter.
  70 + */
  71 + remove() {
  72 + this.emitter.removeSubscription(this);
  73 + }
  74 +}
  75 +
  76 +module.exports = EmitterSubscription;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @noflow
  26 + * @typecheck
  27 + */
  28 +'use strict';
  29 +
  30 +const EmitterSubscription = require('./EmitterSubscription');
  31 +const EventSubscriptionVendor = require('./EventSubscriptionVendor');
  32 +
  33 +const emptyFunction = require('fbjs/lib/emptyFunction');
  34 +const invariant = require('fbjs/lib/invariant');
  35 +
  36 +/**
  37 + * @class EventEmitter
  38 + * @description
  39 + * An EventEmitter is responsible for managing a set of listeners and publishing
  40 + * events to them when it is told that such events happened. In addition to the
  41 + * data for the given event it also sends a event control object which allows
  42 + * the listeners/handlers to prevent the default behavior of the given event.
  43 + *
  44 + * The emitter is designed to be generic enough to support all the different
  45 + * contexts in which one might want to emit events. It is a simple multicast
  46 + * mechanism on top of which extra functionality can be composed. For example, a
  47 + * more advanced emitter may use an EventHolder and EventFactory.
  48 + */
  49 +class EventEmitter {
  50 +
  51 + _subscriber: EventSubscriptionVendor;
  52 + _currentSubscription: ?EmitterSubscription;
  53 +
  54 + /**
  55 + * @constructor
  56 + *
  57 + * @param {EventSubscriptionVendor} subscriber - Optional subscriber instance
  58 + * to use. If omitted, a new subscriber will be created for the emitter.
  59 + */
  60 + constructor(subscriber: ?EventSubscriptionVendor) {
  61 + this._subscriber = subscriber || new EventSubscriptionVendor();
  62 + }
  63 +
  64 + /**
  65 + * Adds a listener to be invoked when events of the specified type are
  66 + * emitted. An optional calling context may be provided. The data arguments
  67 + * emitted will be passed to the listener function.
  68 + *
  69 + * TODO: Annotate the listener arg's type. This is tricky because listeners
  70 + * can be invoked with varargs.
  71 + *
  72 + * @param {string} eventType - Name of the event to listen to
  73 + * @param {function} listener - Function to invoke when the specified event is
  74 + * emitted
  75 + * @param {*} context - Optional context object to use when invoking the
  76 + * listener
  77 + */
  78 + addListener(
  79 + eventType: string, listener: Function, context: ?Object): EmitterSubscription {
  80 +
  81 + return (this._subscriber.addSubscription(
  82 + eventType,
  83 + new EmitterSubscription(this, this._subscriber, listener, context)
  84 + ) : any);
  85 + }
  86 +
  87 + /**
  88 + * Similar to addListener, except that the listener is removed after it is
  89 + * invoked once.
  90 + *
  91 + * @param {string} eventType - Name of the event to listen to
  92 + * @param {function} listener - Function to invoke only once when the
  93 + * specified event is emitted
  94 + * @param {*} context - Optional context object to use when invoking the
  95 + * listener
  96 + */
  97 + once(eventType: string, listener: Function, context: ?Object): EmitterSubscription {
  98 + return this.addListener(eventType, (...args) => {
  99 + this.removeCurrentListener();
  100 + listener.apply(context, args);
  101 + });
  102 + }
  103 +
  104 + /**
  105 + * Removes all of the registered listeners, including those registered as
  106 + * listener maps.
  107 + *
  108 + * @param {?string} eventType - Optional name of the event whose registered
  109 + * listeners to remove
  110 + */
  111 + removeAllListeners(eventType: ?string) {
  112 + this._subscriber.removeAllSubscriptions(eventType);
  113 + }
  114 +
  115 + /**
  116 + * Provides an API that can be called during an eventing cycle to remove the
  117 + * last listener that was invoked. This allows a developer to provide an event
  118 + * object that can remove the listener (or listener map) during the
  119 + * invocation.
  120 + *
  121 + * If it is called when not inside of an emitting cycle it will throw.
  122 + *
  123 + * @throws {Error} When called not during an eventing cycle
  124 + *
  125 + * @example
  126 + * var subscription = emitter.addListenerMap({
  127 + * someEvent: function(data, event) {
  128 + * console.log(data);
  129 + * emitter.removeCurrentListener();
  130 + * }
  131 + * });
  132 + *
  133 + * emitter.emit('someEvent', 'abc'); // logs 'abc'
  134 + * emitter.emit('someEvent', 'def'); // does not log anything
  135 + */
  136 + removeCurrentListener() {
  137 + invariant(
  138 + !!this._currentSubscription,
  139 + 'Not in an emitting cycle; there is no current subscription'
  140 + );
  141 + this.removeSubscription(this._currentSubscription);
  142 + }
  143 +
  144 + /**
  145 + * Removes a specific subscription. Called by the `remove()` method of the
  146 + * subscription itself to ensure any necessary cleanup is performed.
  147 + */
  148 + removeSubscription(subscription: EmitterSubscription) {
  149 + invariant(
  150 + subscription.emitter === this,
  151 + 'Subscription does not belong to this emitter.'
  152 + );
  153 + this._subscriber.removeSubscription(subscription);
  154 + }
  155 +
  156 + /**
  157 + * Returns an array of listeners that are currently registered for the given
  158 + * event.
  159 + *
  160 + * @param {string} eventType - Name of the event to query
  161 + * @returns {array}
  162 + */
  163 + listeners(eventType: string): [EmitterSubscription] {
  164 + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
  165 + return subscriptions
  166 + ? subscriptions.filter(emptyFunction.thatReturnsTrue).map(
  167 + function(subscription) {
  168 + return subscription.listener;
  169 + })
  170 + : [];
  171 + }
  172 +
  173 + /**
  174 + * Emits an event of the given type with the given data. All handlers of that
  175 + * particular type will be notified.
  176 + *
  177 + * @param {string} eventType - Name of the event to emit
  178 + * @param {...*} Arbitrary arguments to be passed to each registered listener
  179 + *
  180 + * @example
  181 + * emitter.addListener('someEvent', function(message) {
  182 + * console.log(message);
  183 + * });
  184 + *
  185 + * emitter.emit('someEvent', 'abc'); // logs 'abc'
  186 + */
  187 + emit(eventType: string) {
  188 + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
  189 + if (subscriptions) {
  190 + for (let i = 0, l = subscriptions.length; i < l; i++) {
  191 + const subscription = subscriptions[i];
  192 +
  193 + // The subscription may have been removed during this event loop.
  194 + if (subscription) {
  195 + this._currentSubscription = subscription;
  196 + subscription.listener.apply(
  197 + subscription.context,
  198 + Array.prototype.slice.call(arguments, 1)
  199 + );
  200 + }
  201 + }
  202 + this._currentSubscription = null;
  203 + }
  204 + }
  205 +
  206 + /**
  207 + * Removes the given listener for event of specific type.
  208 + *
  209 + * @param {string} eventType - Name of the event to emit
  210 + * @param {function} listener - Function to invoke when the specified event is
  211 + * emitted
  212 + *
  213 + * @example
  214 + * emitter.removeListener('someEvent', function(message) {
  215 + * console.log(message);
  216 + * }); // removes the listener if already registered
  217 + *
  218 + */
  219 + removeListener(eventType: String, listener) {
  220 + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any);
  221 + if (subscriptions) {
  222 + for (let i = 0, l = subscriptions.length; i < l; i++) {
  223 + const subscription = subscriptions[i];
  224 +
  225 + // The subscription may have been removed during this event loop.
  226 + // its listener matches the listener in method parameters
  227 + if (subscription && subscription.listener === listener) {
  228 + subscription.remove();
  229 + }
  230 + }
  231 + }
  232 + }
  233 +}
  234 +
  235 +module.exports = EventEmitter;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +import type EventSubscriptionVendor from './EventSubscriptionVendor';
  30 +
  31 +/**
  32 + * EventSubscription represents a subscription to a particular event. It can
  33 + * remove its own subscription.
  34 + */
  35 +class EventSubscription {
  36 +
  37 + eventType: string;
  38 + key: number;
  39 + subscriber: EventSubscriptionVendor;
  40 +
  41 + /**
  42 + * @param {EventSubscriptionVendor} subscriber the subscriber that controls
  43 + * this subscription.
  44 + */
  45 + constructor(subscriber: EventSubscriptionVendor) {
  46 + this.subscriber = subscriber;
  47 + }
  48 +
  49 + /**
  50 + * Removes this subscription from the subscriber that controls it.
  51 + */
  52 + remove() {
  53 + this.subscriber.removeSubscription(this);
  54 + }
  55 +}
  56 +
  57 +module.exports = EventSubscription;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +const invariant = require('fbjs/lib/invariant');
  30 +
  31 +import type EventSubscription from './EventSubscription';
  32 +
  33 +/**
  34 + * EventSubscriptionVendor stores a set of EventSubscriptions that are
  35 + * subscribed to a particular event type.
  36 + */
  37 +class EventSubscriptionVendor {
  38 +
  39 + _subscriptionsForType: Object;
  40 + _currentSubscription: ?EventSubscription;
  41 +
  42 + constructor() {
  43 + this._subscriptionsForType = {};
  44 + this._currentSubscription = null;
  45 + }
  46 +
  47 + /**
  48 + * Adds a subscription keyed by an event type.
  49 + *
  50 + * @param {string} eventType
  51 + * @param {EventSubscription} subscription
  52 + */
  53 + addSubscription(
  54 + eventType: string, subscription: EventSubscription): EventSubscription {
  55 + invariant(
  56 + subscription.subscriber === this,
  57 + 'The subscriber of the subscription is incorrectly set.');
  58 + if (!this._subscriptionsForType[eventType]) {
  59 + this._subscriptionsForType[eventType] = [];
  60 + }
  61 + const key = this._subscriptionsForType[eventType].length;
  62 + this._subscriptionsForType[eventType].push(subscription);
  63 + subscription.eventType = eventType;
  64 + subscription.key = key;
  65 + return subscription;
  66 + }
  67 +
  68 + /**
  69 + * Removes a bulk set of the subscriptions.
  70 + *
  71 + * @param {?string} eventType - Optional name of the event type whose
  72 + * registered supscriptions to remove, if null remove all subscriptions.
  73 + */
  74 + removeAllSubscriptions(eventType: ?string) {
  75 + if (eventType === undefined) {
  76 + this._subscriptionsForType = {};
  77 + } else {
  78 + delete this._subscriptionsForType[eventType];
  79 + }
  80 + }
  81 +
  82 + /**
  83 + * Removes a specific subscription. Instead of calling this function, call
  84 + * `subscription.remove()` directly.
  85 + *
  86 + * @param {object} subscription
  87 + */
  88 + removeSubscription(subscription: Object) {
  89 + const eventType = subscription.eventType;
  90 + const key = subscription.key;
  91 +
  92 + const subscriptionsForType = this._subscriptionsForType[eventType];
  93 + if (subscriptionsForType) {
  94 + delete subscriptionsForType[key];
  95 + }
  96 + }
  97 +
  98 + /**
  99 + * Returns the array of subscriptions that are currently registered for the
  100 + * given event type.
  101 + *
  102 + * Note: This array can be potentially sparse as subscriptions are deleted
  103 + * from it when they are removed.
  104 + *
  105 + * TODO: This returns a nullable array. wat?
  106 + *
  107 + * @param {string} eventType
  108 + * @returns {?array}
  109 + */
  110 + getSubscriptionsForType(eventType: string): ?[EventSubscription] {
  111 + return this._subscriptionsForType[eventType];
  112 + }
  113 +}
  114 +
  115 +module.exports = EventSubscriptionVendor;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +import {InteractionManager} from 'react-native';
  30 +
  31 +/**
  32 + * This mixin provides safe versions of InteractionManager start/end methods
  33 + * that ensures `clearInteractionHandle` is always called
  34 + * once per start, even if the component is unmounted.
  35 + */
  36 +var InteractionMixin = {
  37 + componentWillUnmount: function() {
  38 + while (this._interactionMixinHandles.length) {
  39 + InteractionManager.clearInteractionHandle(
  40 + this._interactionMixinHandles.pop()
  41 + );
  42 + }
  43 + },
  44 +
  45 + _interactionMixinHandles: ([]: Array<number>),
  46 +
  47 + createInteractionHandle: function() {
  48 + var handle = InteractionManager.createInteractionHandle();
  49 + this._interactionMixinHandles.push(handle);
  50 + return handle;
  51 + },
  52 +
  53 + clearInteractionHandle: function(clearHandle: number) {
  54 + InteractionManager.clearInteractionHandle(clearHandle);
  55 + this._interactionMixinHandles = this._interactionMixinHandles.filter(
  56 + handle => handle !== clearHandle
  57 + );
  58 + },
  59 +
  60 + /**
  61 + * Schedule work for after all interactions have completed.
  62 + *
  63 + * @param {function} callback
  64 + */
  65 + runAfterInteractions: function(callback: Function) {
  66 + InteractionManager.runAfterInteractions(callback);
  67 + },
  68 +};
  69 +
  70 +module.exports = InteractionMixin;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @noflow
  26 + */
  27 +'use strict';
  28 +
  29 +var NavigationEvent = require('./NavigationEvent');
  30 +var NavigationEventEmitter = require('./NavigationEventEmitter');
  31 +var NavigationTreeNode = require('./NavigationTreeNode');
  32 +
  33 +var emptyFunction = require('fbjs/lib/emptyFunction');
  34 +var invariant = require('fbjs/lib/invariant');
  35 +
  36 +import type EventSubscription from 'EventSubscription';
  37 +
  38 +var {
  39 + AT_TARGET,
  40 + BUBBLING_PHASE,
  41 + CAPTURING_PHASE,
  42 +} = NavigationEvent;
  43 +
  44 +// Event types that do not support event bubbling, capturing and
  45 +// reconciliation API (e.g event.preventDefault(), event.stopPropagation()).
  46 +var LegacyEventTypes = new Set([
  47 + 'willfocus',
  48 + 'didfocus',
  49 +]);
  50 +
  51 +/**
  52 + * Class that contains the info and methods for app navigation.
  53 + */
  54 +class NavigationContext {
  55 + __node: NavigationTreeNode;
  56 + _bubbleEventEmitter: ?NavigationEventEmitter;
  57 + _captureEventEmitter: ?NavigationEventEmitter;
  58 + _currentRoute: any;
  59 + _emitCounter: number;
  60 + _emitQueue: Array<any>;
  61 +
  62 + constructor() {
  63 + this._bubbleEventEmitter = new NavigationEventEmitter(this);
  64 + this._captureEventEmitter = new NavigationEventEmitter(this);
  65 + this._currentRoute = null;
  66 +
  67 + // Sets the protected property `__node`.
  68 + this.__node = new NavigationTreeNode(this);
  69 +
  70 + this._emitCounter = 0;
  71 + this._emitQueue = [];
  72 +
  73 + this.addListener('willfocus', this._onFocus);
  74 + this.addListener('didfocus', this._onFocus);
  75 + }
  76 +
  77 + /* $FlowFixMe - get/set properties not yet supported */
  78 + get parent(): ?NavigationContext {
  79 + var parent = this.__node.getParent();
  80 + return parent ? parent.getValue() : null;
  81 + }
  82 +
  83 + /* $FlowFixMe - get/set properties not yet supported */
  84 + get top(): ?NavigationContext {
  85 + var result = null;
  86 + var parentNode = this.__node.getParent();
  87 + while (parentNode) {
  88 + result = parentNode.getValue();
  89 + parentNode = parentNode.getParent();
  90 + }
  91 + return result;
  92 + }
  93 +
  94 + /* $FlowFixMe - get/set properties not yet supported */
  95 + get currentRoute(): any {
  96 + return this._currentRoute;
  97 + }
  98 +
  99 + appendChild(childContext: NavigationContext): void {
  100 + this.__node.appendChild(childContext.__node);
  101 + }
  102 +
  103 + addListener(
  104 + eventType: string,
  105 + listener: Function,
  106 + useCapture: ?boolean
  107 + ): EventSubscription {
  108 + if (LegacyEventTypes.has(eventType)) {
  109 + useCapture = false;
  110 + }
  111 +
  112 + var emitter = useCapture ?
  113 + this._captureEventEmitter :
  114 + this._bubbleEventEmitter;
  115 +
  116 + if (emitter) {
  117 + return emitter.addListener(eventType, listener, this);
  118 + } else {
  119 + return {remove: emptyFunction};
  120 + }
  121 + }
  122 +
  123 + emit(eventType: String, data: any, didEmitCallback: ?Function): void {
  124 + if (this._emitCounter > 0) {
  125 + // An event cycle that was previously created hasn't finished yet.
  126 + // Put this event cycle into the queue and will finish them later.
  127 + var args: any = Array.prototype.slice.call(arguments);
  128 + this._emitQueue.push(args);
  129 + return;
  130 + }
  131 +
  132 + this._emitCounter++;
  133 +
  134 + if (LegacyEventTypes.has(eventType)) {
  135 + // Legacy events does not support event bubbling and reconciliation.
  136 + this.__emit(
  137 + eventType,
  138 + data,
  139 + null,
  140 + {
  141 + defaultPrevented: false,
  142 + eventPhase: AT_TARGET,
  143 + propagationStopped: true,
  144 + target: this,
  145 + }
  146 + );
  147 + } else {
  148 + var targets = [this];
  149 + var parentTarget = this.parent;
  150 + while (parentTarget) {
  151 + targets.unshift(parentTarget);
  152 + parentTarget = parentTarget.parent;
  153 + }
  154 +
  155 + var propagationStopped = false;
  156 + var defaultPrevented = false;
  157 + var callback = (event) => {
  158 + propagationStopped = propagationStopped || event.isPropagationStopped();
  159 + defaultPrevented = defaultPrevented || event.defaultPrevented;
  160 + };
  161 +
  162 + // Capture phase
  163 + targets.some((currentTarget) => {
  164 + if (propagationStopped) {
  165 + return true;
  166 + }
  167 +
  168 + var extraInfo = {
  169 + defaultPrevented,
  170 + eventPhase: CAPTURING_PHASE,
  171 + propagationStopped,
  172 + target: this,
  173 + };
  174 +
  175 + currentTarget.__emit(eventType, data, callback, extraInfo);
  176 + }, this);
  177 +
  178 + // bubble phase
  179 + targets.reverse().some((currentTarget) => {
  180 + if (propagationStopped) {
  181 + return true;
  182 + }
  183 + var extraInfo = {
  184 + defaultPrevented,
  185 + eventPhase: BUBBLING_PHASE,
  186 + propagationStopped,
  187 + target: this,
  188 + };
  189 + currentTarget.__emit(eventType, data, callback, extraInfo);
  190 + }, this);
  191 + }
  192 +
  193 + if (didEmitCallback) {
  194 + var event = NavigationEvent.pool(eventType, this, data);
  195 + propagationStopped && event.stopPropagation();
  196 + defaultPrevented && event.preventDefault();
  197 + didEmitCallback.call(this, event);
  198 + event.dispose();
  199 + }
  200 +
  201 + this._emitCounter--;
  202 + while (this._emitQueue.length) {
  203 + var args: any = this._emitQueue.shift();
  204 + this.emit.apply(this, args);
  205 + }
  206 + }
  207 +
  208 + dispose(): void {
  209 + // clean up everything.
  210 + this._bubbleEventEmitter && this._bubbleEventEmitter.removeAllListeners();
  211 + this._captureEventEmitter && this._captureEventEmitter.removeAllListeners();
  212 + this._bubbleEventEmitter = null;
  213 + this._captureEventEmitter = null;
  214 + this._currentRoute = null;
  215 + }
  216 +
  217 + // This method `__method` is protected.
  218 + __emit(
  219 + eventType: String,
  220 + data: any,
  221 + didEmitCallback: ?Function,
  222 + extraInfo: Object,
  223 + ): void {
  224 + var emitter;
  225 + switch (extraInfo.eventPhase) {
  226 + case CAPTURING_PHASE: // phase = 1
  227 + emitter = this._captureEventEmitter;
  228 + break;
  229 +
  230 + case AT_TARGET: // phase = 2
  231 + emitter = this._bubbleEventEmitter;
  232 + break;
  233 +
  234 + case BUBBLING_PHASE: // phase = 3
  235 + emitter = this._bubbleEventEmitter;
  236 + break;
  237 +
  238 + default:
  239 + invariant(false, 'invalid event phase %s', extraInfo.eventPhase);
  240 + }
  241 +
  242 + if (extraInfo.target === this) {
  243 + // phase = 2
  244 + extraInfo.eventPhase = AT_TARGET;
  245 + }
  246 +
  247 + if (emitter) {
  248 + emitter.emit(
  249 + eventType,
  250 + data,
  251 + didEmitCallback,
  252 + extraInfo
  253 + );
  254 + }
  255 + }
  256 +
  257 + _onFocus(event: NavigationEvent): void {
  258 + invariant(
  259 + event.data && event.data.hasOwnProperty('route'),
  260 + 'event type "%s" should provide route',
  261 + event.type
  262 + );
  263 +
  264 + this._currentRoute = event.data.route;
  265 + }
  266 +}
  267 +
  268 +module.exports = NavigationContext;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +const invariant = require('fbjs/lib/invariant');
  30 +
  31 +class NavigationEventPool {
  32 + _list: Array<any>;
  33 +
  34 + constructor() {
  35 + this._list = [];
  36 + }
  37 +
  38 + get(type: string, currentTarget: Object, data: any): NavigationEvent {
  39 + let event;
  40 + if (this._list.length > 0) {
  41 + event = this._list.pop();
  42 + event.constructor.call(event, type, currentTarget, data);
  43 + } else {
  44 + event = new NavigationEvent(type, currentTarget, data);
  45 + }
  46 + return event;
  47 + }
  48 +
  49 + put(event: NavigationEvent) {
  50 + this._list.push(event);
  51 + }
  52 +}
  53 +
  54 +const _navigationEventPool = new NavigationEventPool();
  55 +
  56 +/**
  57 + * The NavigationEvent interface represents any event of the navigation.
  58 + * It contains common properties and methods to any event.
  59 + *
  60 + * == Important Properties ==
  61 + *
  62 + * - target:
  63 + * A reference to the navigation context that dispatched the event. It is
  64 + * different from event.currentTarget when the event handler is called during
  65 + * the bubbling or capturing phase of the event.
  66 + *
  67 + * - currentTarget:
  68 + * Identifies the current target for the event, as the event traverses the
  69 + * navigation context tree. It always refers to the navigation context the
  70 + * event handler has been attached to as opposed to event.target which
  71 + * identifies the navigation context on which the event occurred.
  72 + *
  73 + * - eventPhase:
  74 + * Returns an integer value which specifies the current evaluation phase of
  75 + * the event flow; possible values are listed in NavigationEvent phase
  76 + * constants below.
  77 + */
  78 +class NavigationEvent {
  79 + static AT_TARGET: number;
  80 + static BUBBLING_PHASE: number;
  81 + static CAPTURING_PHASE: number;
  82 + static NONE: number;
  83 +
  84 + _currentTarget: ?Object;
  85 + _data: any;
  86 + _defaultPrevented: boolean;
  87 + _disposed: boolean;
  88 + _propagationStopped: boolean;
  89 + _type: string;
  90 +
  91 + target: ?Object;
  92 +
  93 + // Returns an integer value which specifies the current evaluation phase of
  94 + // the event flow.
  95 + eventPhase: number;
  96 +
  97 + static pool(type: string, currentTarget: Object, data: any): NavigationEvent {
  98 + return _navigationEventPool.get(type, currentTarget, data);
  99 + }
  100 +
  101 + constructor(type: string, currentTarget: Object, data: any) {
  102 + this.target = currentTarget;
  103 + this.eventPhase = NavigationEvent.NONE;
  104 +
  105 + this._type = type;
  106 + this._currentTarget = currentTarget;
  107 + this._data = data;
  108 + this._defaultPrevented = false;
  109 + this._disposed = false;
  110 + this._propagationStopped = false;
  111 + }
  112 +
  113 + get type(): string {
  114 + return this._type;
  115 + }
  116 +
  117 + get currentTarget(): ?Object {
  118 + return this._currentTarget;
  119 + }
  120 +
  121 + get data(): any {
  122 + return this._data;
  123 + }
  124 +
  125 + get defaultPrevented(): boolean {
  126 + return this._defaultPrevented;
  127 + }
  128 +
  129 + preventDefault(): void {
  130 + this._defaultPrevented = true;
  131 + }
  132 +
  133 + stopPropagation(): void {
  134 + this._propagationStopped = true;
  135 + }
  136 +
  137 + stop(): void {
  138 + this.preventDefault();
  139 + this.stopPropagation();
  140 + }
  141 +
  142 + isPropagationStopped(): boolean {
  143 + return this._propagationStopped;
  144 + }
  145 +
  146 + /**
  147 + * Dispose the event.
  148 + * NavigationEvent shall be disposed after being emitted by
  149 + * `NavigationEventEmitter`.
  150 + */
  151 + dispose(): void {
  152 + invariant(!this._disposed, 'NavigationEvent is already disposed');
  153 + this._disposed = true;
  154 +
  155 + // Clean up.
  156 + this.target = null;
  157 + this.eventPhase = NavigationEvent.NONE;
  158 + this._type = '';
  159 + this._currentTarget = null;
  160 + this._data = null;
  161 + this._defaultPrevented = false;
  162 +
  163 + // Put this back to the pool to reuse the instance.
  164 + _navigationEventPool.put(this);
  165 + }
  166 +}
  167 +
  168 +/**
  169 + * Event phase constants.
  170 + * These values describe which phase the event flow is currently being
  171 + * evaluated.
  172 + */
  173 +
  174 +// No event is being processed at this time.
  175 +NavigationEvent.NONE = 0;
  176 +
  177 +// The event is being propagated through the currentTarget's ancestor objects.
  178 +NavigationEvent.CAPTURING_PHASE = 1;
  179 +
  180 +// The event has arrived at the event's currentTarget. Event listeners registered for
  181 +// this phase are called at this time.
  182 +NavigationEvent.AT_TARGET = 2;
  183 +
  184 +// The event is propagating back up through the currentTarget's ancestors in reverse
  185 +// order, starting with the parent. This is known as bubbling, and occurs only
  186 +// if event propagation isn't prevented. Event listeners registered for this
  187 +// phase are triggered during this process.
  188 +NavigationEvent.BUBBLING_PHASE = 3;
  189 +
  190 +module.exports = NavigationEvent;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +var EventEmitter = require('./EventEmitter');
  30 +var NavigationEvent = require('./NavigationEvent');
  31 +
  32 +type ExtraInfo = {
  33 + defaultPrevented: ?boolean,
  34 + eventPhase: ?number,
  35 + propagationStopped: ?boolean,
  36 + target: ?Object,
  37 +};
  38 +
  39 +class NavigationEventEmitter extends EventEmitter {
  40 + _emitQueue: Array<any>;
  41 + _emitting: boolean;
  42 + _target: Object;
  43 +
  44 + constructor(target: Object) {
  45 + super();
  46 + this._emitting = false;
  47 + this._emitQueue = [];
  48 + this._target = target;
  49 + }
  50 +
  51 + emit(
  52 + eventType: string,
  53 + data: any,
  54 + didEmitCallback: ?Function,
  55 + extraInfo: ?ExtraInfo
  56 + ): void {
  57 + if (this._emitting) {
  58 + // An event cycle that was previously created hasn't finished yet.
  59 + // Put this event cycle into the queue and will finish them later.
  60 + var args: any = Array.prototype.slice.call(arguments);
  61 + this._emitQueue.push(args);
  62 + return;
  63 + }
  64 +
  65 + this._emitting = true;
  66 +
  67 + var event = NavigationEvent.pool(eventType, this._target, data);
  68 +
  69 + if (extraInfo) {
  70 + if (extraInfo.target) {
  71 + event.target = extraInfo.target;
  72 + }
  73 +
  74 + if (extraInfo.eventPhase) {
  75 + event.eventPhase = extraInfo.eventPhase;
  76 + }
  77 +
  78 + if (extraInfo.defaultPrevented) {
  79 + event.preventDefault();
  80 + }
  81 +
  82 + if (extraInfo.propagationStopped) {
  83 + event.stopPropagation();
  84 + }
  85 + }
  86 +
  87 + // EventEmitter#emit only takes `eventType` as `String`. Casting `eventType`
  88 + // to `String` to make @flow happy.
  89 + super.emit(String(eventType), event);
  90 +
  91 + if (typeof didEmitCallback === 'function') {
  92 + didEmitCallback.call(this._target, event);
  93 + }
  94 + event.dispose();
  95 +
  96 + this._emitting = false;
  97 +
  98 + while (this._emitQueue.length) {
  99 + var args: any = this._emitQueue.shift();
  100 + this.emit.apply(this, args);
  101 + }
  102 + }
  103 +}
  104 +
  105 +module.exports = NavigationEventEmitter;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +var immutable = require('immutable');
  30 +var invariant = require('fbjs/lib/invariant');
  31 +
  32 +type IterationCallback = (route: any, index: number, key: string) => void;
  33 +
  34 +var {List, Set} = immutable;
  35 +
  36 +function isRouteEmpty(route: any): boolean {
  37 + return (route === undefined || route === null || route === '') || false;
  38 +}
  39 +
  40 +var _nextID = 0;
  41 +
  42 +class RouteNode {
  43 + key: string;
  44 + value: any;
  45 + constructor(route: any) {
  46 + // Key value gets bigger incrementally. Developer can compare the
  47 + // keys of two routes then know which route is added to the stack
  48 + // earlier.
  49 + this.key = String(_nextID++);
  50 +
  51 + this.value = route;
  52 + }
  53 +}
  54 +
  55 +var StackDiffRecord = immutable.Record({
  56 + key: null,
  57 + route: null,
  58 + index: null,
  59 +});
  60 +
  61 +/**
  62 + * The immutable route stack.
  63 + */
  64 +class RouteStack {
  65 + _index: number;
  66 +
  67 + _routeNodes: List<RouteNode>;
  68 +
  69 + constructor(index: number, routeNodes: List<RouteNode>) {
  70 + invariant(
  71 + routeNodes.size > 0,
  72 + 'size must not be empty'
  73 + );
  74 +
  75 + invariant(
  76 + index > -1 && index <= routeNodes.size - 1,
  77 + 'index out of bound'
  78 + );
  79 +
  80 + this._routeNodes = routeNodes;
  81 + this._index = index;
  82 + }
  83 +
  84 + get size(): number {
  85 + return this._routeNodes.size;
  86 + }
  87 +
  88 + get index(): number {
  89 + return this._index;
  90 + }
  91 +
  92 + toArray(): Array<any> {
  93 + var result = [];
  94 + var ii = 0;
  95 + var nodes = this._routeNodes;
  96 + while (ii < nodes.size) {
  97 + result.push(nodes.get(ii).value);
  98 + ii++;
  99 + }
  100 + return result;
  101 + }
  102 +
  103 + get(index: number): any {
  104 + if (index < 0 || index > this._routeNodes.size - 1) {
  105 + return null;
  106 + }
  107 + return this._routeNodes.get(index).value;
  108 + }
  109 +
  110 + /**
  111 + * Returns the key associated with the route.
  112 + * When a route is added to a stack, the stack creates a key for this route.
  113 + * The key will persist until the initial stack and its derived stack
  114 + * no longer contains this route.
  115 + */
  116 + keyOf(route: any): ?string {
  117 + if (isRouteEmpty(route)) {
  118 + return null;
  119 + }
  120 + var index = this.indexOf(route);
  121 + return index > -1 ?
  122 + this._routeNodes.get(index).key :
  123 + null;
  124 + }
  125 +
  126 + indexOf(route: any): number {
  127 + if (isRouteEmpty(route)) {
  128 + return -1;
  129 + }
  130 +
  131 + var finder = (node) => {
  132 + return (node: RouteNode).value === route;
  133 + };
  134 +
  135 + return this._routeNodes.findIndex(finder, this);
  136 + }
  137 +
  138 + slice(begin?: number, end?: number): RouteStack {
  139 + var routeNodes = this._routeNodes.slice(begin, end);
  140 + var index = Math.min(this._index, routeNodes.size - 1);
  141 + return this._update(index, routeNodes);
  142 + }
  143 +
  144 + /**
  145 + * Returns a new stack with the provided route appended,
  146 + * starting at this stack size.
  147 + */
  148 + push(route: any): RouteStack {
  149 +
  150 + invariant(
  151 + !isRouteEmpty(route),
  152 + 'Must supply route to push'
  153 + );
  154 +
  155 + invariant(this._routeNodes.indexOf(route) === -1, 'route must be unique');
  156 +
  157 + // When pushing, removes the rest of the routes past the current index.
  158 + var routeNodes = this._routeNodes.withMutations((list: List<RouteNode>) => {
  159 + list.slice(0, this._index + 1).push(new RouteNode(route));
  160 + });
  161 +
  162 + return this._update(routeNodes.size - 1, routeNodes);
  163 + }
  164 +
  165 + /**
  166 + * Returns a new stack a size ones less than this stack,
  167 + * excluding the last index in this stack.
  168 + */
  169 + pop(): RouteStack {
  170 + invariant(this._routeNodes.size > 1, 'should not pop routeNodes stack to empty');
  171 +
  172 + // When popping, removes the rest of the routes past the current index.
  173 + var routeNodes = this._routeNodes.slice(0, this._index);
  174 + return this._update(routeNodes.size - 1, routeNodes);
  175 + }
  176 +
  177 + jumpToIndex(index: number): RouteStack {
  178 + invariant(
  179 + index > -1 && index < this._routeNodes.size,
  180 + 'index out of bound'
  181 + );
  182 +
  183 + return this._update(index, this._routeNodes);
  184 + }
  185 +
  186 + /**
  187 + * Replace a route in the navigation stack.
  188 + *
  189 + * `index` specifies the route in the stack that should be replaced.
  190 + * If it's negative, it counts from the back.
  191 + */
  192 + replaceAtIndex(index: number, route: any): RouteStack {
  193 + invariant(
  194 + !isRouteEmpty(route),
  195 + 'Must supply route to replace'
  196 + );
  197 +
  198 + if (this.get(index) === route) {
  199 + return this;
  200 + }
  201 +
  202 + invariant(this.indexOf(route) === -1, 'route must be unique');
  203 +
  204 + if (index < 0) {
  205 + index += this._routeNodes.size;
  206 + }
  207 +
  208 + invariant(
  209 + index > -1 && index < this._routeNodes.size,
  210 + 'index out of bound'
  211 + );
  212 +
  213 + var routeNodes = this._routeNodes.set(index, new RouteNode(route));
  214 + return this._update(index, routeNodes);
  215 + }
  216 +
  217 + // Iterations
  218 + forEach(callback: IterationCallback, context: ?Object): void {
  219 + var ii = 0;
  220 + var nodes = this._routeNodes;
  221 + while (ii < nodes.size) {
  222 + var node = nodes.get(ii);
  223 + callback.call(context, node.value, ii, node.key);
  224 + ii++;
  225 + }
  226 + }
  227 +
  228 + mapToArray(callback: IterationCallback, context: ?Object): Array<any> {
  229 + var result = [];
  230 + this.forEach((route, index, key) => {
  231 + result.push(callback.call(context, route, index, key));
  232 + });
  233 + return result;
  234 + }
  235 +
  236 + /**
  237 + * Returns a Set excluding any routes contained within the stack given.
  238 + */
  239 + subtract(stack: RouteStack): Set<StackDiffRecord> {
  240 + var items = [];
  241 + this._routeNodes.forEach((node: RouteNode, index: number) => {
  242 + if (!stack._routeNodes.contains(node)) {
  243 + items.push(
  244 + new StackDiffRecord({
  245 + route: node.value,
  246 + index: index,
  247 + key: node.key,
  248 + })
  249 + );
  250 + }
  251 + });
  252 + return new Set(items);
  253 + }
  254 +
  255 + _update(index: number, routeNodes: List<RouteNode>): RouteStack {
  256 + if (this._index === index && this._routeNodes === routeNodes) {
  257 + return this;
  258 + }
  259 + return new RouteStack(index, routeNodes);
  260 + }
  261 +}
  262 +
  263 +/**
  264 + * The first class data structure for NavigationContext to manage the navigation
  265 + * stack of routes.
  266 + */
  267 +class NavigationRouteStack extends RouteStack {
  268 + constructor(index: number, routeNodes: Array<any>) {
  269 + // For now, `RouteStack` internally, uses an immutable `List` to keep
  270 + // track of routeNodes. Since using `List` is really just the implementation
  271 + // detail, we don't want to accept `routeNodes` as `list` from constructor
  272 + // for developer.
  273 + var nodes = routeNodes.map((route) => {
  274 + invariant(!isRouteEmpty(route), 'route must not be mepty');
  275 + return new RouteNode(route);
  276 + });
  277 +
  278 + super(index, new List(nodes));
  279 + }
  280 +}
  281 +
  282 +module.exports = NavigationRouteStack;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +var invariant = require('fbjs/lib/invariant');
  30 +var immutable = require('immutable');
  31 +
  32 +var {List} = immutable;
  33 +
  34 +/**
  35 + * Utility to build a tree of nodes.
  36 + * Note that this tree does not perform cyclic redundancy check
  37 + * while appending child node.
  38 + */
  39 +class NavigationTreeNode {
  40 + __parent: ?NavigationTreeNode;
  41 +
  42 + _children: List<NavigationTreeNode>;
  43 +
  44 + _value: any;
  45 +
  46 + constructor(value: any) {
  47 + this.__parent = null;
  48 + this._children = new List();
  49 + this._value = value;
  50 + }
  51 +
  52 + getValue(): any {
  53 + return this._value;
  54 + }
  55 +
  56 + getParent(): ?NavigationTreeNode {
  57 + return this.__parent;
  58 + }
  59 +
  60 + getChildrenCount(): number {
  61 + return this._children.size;
  62 + }
  63 +
  64 + getChildAt(index: number): ?NavigationTreeNode {
  65 + return index > -1 && index < this._children.size ?
  66 + this._children.get(index) :
  67 + null;
  68 + }
  69 +
  70 + appendChild(child: NavigationTreeNode): void {
  71 + if (child.__parent) {
  72 + child.__parent.removeChild(child);
  73 + }
  74 + child.__parent = this;
  75 + this._children = this._children.push(child);
  76 + }
  77 +
  78 + removeChild(child: NavigationTreeNode): void {
  79 + var index = this._children.indexOf(child);
  80 +
  81 + invariant(
  82 + index > -1,
  83 + 'The node to be removed is not a child of this node.'
  84 + );
  85 +
  86 + child.__parent = null;
  87 +
  88 + this._children = this._children.splice(index, 1);
  89 + }
  90 +
  91 + indexOf(child: NavigationTreeNode): number {
  92 + return this._children.indexOf(child);
  93 + }
  94 +
  95 + forEach(callback: Function, context: any): void {
  96 + this._children.forEach(callback, context);
  97 + }
  98 +
  99 + map(callback: Function, context: any): Array<NavigationTreeNode> {
  100 + return this._children.map(callback, context).toJS();
  101 + }
  102 +
  103 + some(callback: Function, context: any): boolean {
  104 + return this._children.some(callback, context);
  105 + }
  106 +}
  107 +
  108 +
  109 +module.exports = NavigationTreeNode;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 + /* eslint-disable no-extra-boolean-cast*/
  27 +'use strict';
  28 +
  29 +var buildStyleInterpolator = require('./buildStyleInterpolator');
  30 +
  31 +import {
  32 + Dimensions,
  33 + PanResponder,
  34 + NativeModules,
  35 + StyleSheet,
  36 + TVEventHandler,
  37 + View,
  38 + ViewPropTypes,
  39 +} from 'react-native';
  40 +
  41 +var AnimationsDebugModule = NativeModules.AnimationsDebugModule;
  42 +var InteractionMixin = require('./InteractionMixin');
  43 +var NavigationContext = require('./NavigationContext');
  44 +var NavigatorBreadcrumbNavigationBar = require('./NavigatorBreadcrumbNavigationBar');
  45 +var NavigatorNavigationBar = require('./NavigatorNavigationBar');
  46 +var NavigatorSceneConfigs = require('./NavigatorSceneConfigs');
  47 +var React = require('react');
  48 +var Subscribable = require('./Subscribable');
  49 +var TimerMixin = require('react-timer-mixin');
  50 +
  51 +var createReactClass = require('create-react-class');
  52 +var clamp = require('./clamp');
  53 +var invariant = require('fbjs/lib/invariant');
  54 +var rebound = require('rebound');
  55 +
  56 +var flattenStyle = require('./flattenStyle');
  57 +
  58 +var PropTypes = require('prop-types');
  59 +
  60 +// TODO: this is not ideal because there is no guarantee that the navigator
  61 +// is full screen, however we don't have a good way to measure the actual
  62 +// size of the navigator right now, so this is the next best thing.
  63 +var SCREEN_WIDTH = Dimensions.get('window').width;
  64 +var SCREEN_HEIGHT = Dimensions.get('window').height;
  65 +var SCENE_DISABLED_NATIVE_PROPS = {
  66 + pointerEvents: 'none',
  67 + style: {
  68 + top: SCREEN_HEIGHT,
  69 + bottom: -SCREEN_HEIGHT,
  70 + opacity: 0,
  71 + },
  72 +};
  73 +
  74 +var __uid = 0;
  75 +function getuid() {
  76 + return __uid++;
  77 +}
  78 +
  79 +function getRouteID(route) {
  80 + if (route === null || typeof route !== 'object') {
  81 + return String(route);
  82 + }
  83 +
  84 + var key = '__navigatorRouteID';
  85 +
  86 + if (!route.hasOwnProperty(key)) {
  87 + Object.defineProperty(route, key, {
  88 + enumerable: false,
  89 + configurable: false,
  90 + writable: false,
  91 + value: getuid(),
  92 + });
  93 + }
  94 + return route[key];
  95 +}
  96 +
  97 +
  98 +const BASE_SCENE_STYLE = {
  99 + position: 'absolute',
  100 + overflow: 'hidden',
  101 + left: 0,
  102 + right: 0,
  103 + bottom: 0,
  104 + top: 0,
  105 +};
  106 +
  107 +const DEFAULT_SCENE_STYLE = {
  108 + position: 'absolute',
  109 + left: 0,
  110 + right: 0,
  111 + bottom: 0,
  112 + top: 0,
  113 + transform: [
  114 + {translateX: 0},
  115 + {translateY: 0},
  116 + {scaleX: 1},
  117 + {scaleY: 1},
  118 + {rotate: '0deg'},
  119 + {skewX: '0deg'},
  120 + {skewY: '0deg'},
  121 + ],
  122 +};
  123 +
  124 +// styles moved to the top of the file so getDefaultProps can refer to it
  125 +var styles = StyleSheet.create({
  126 + container: {
  127 + flex: 1,
  128 + overflow: 'hidden',
  129 + },
  130 + defaultSceneStyle: DEFAULT_SCENE_STYLE,
  131 + baseScene: BASE_SCENE_STYLE,
  132 + disabledScene: {
  133 + top: SCREEN_HEIGHT,
  134 + bottom: -SCREEN_HEIGHT,
  135 + },
  136 + transitioner: {
  137 + flex: 1,
  138 + backgroundColor: 'transparent',
  139 + overflow: 'hidden',
  140 + }
  141 +});
  142 +
  143 +var GESTURE_ACTIONS = [
  144 + 'pop',
  145 + 'jumpBack',
  146 + 'jumpForward',
  147 +];
  148 +
  149 +/**
  150 + * `Navigator` handles the transition between different scenes in your app.
  151 + * It is implemented in JavaScript and is available on both iOS and Android. If
  152 + * you are targeting iOS only, you may also want to consider using
  153 + * [`NavigatorIOS`](docs/navigatorios.html) as it leverages native UIKit
  154 + * navigation.
  155 + *
  156 + * To set up the `Navigator` you provide one or more objects called routes,
  157 + * to identify each scene. You also provide a `renderScene` function that
  158 + * renders the scene for each route object.
  159 + *
  160 + * ```
  161 + * import React, { Component } from 'react';
  162 + * import { Text, Navigator, TouchableHighlight } from 'react-native';
  163 + *
  164 + * export default class NavAllDay extends Component {
  165 + * render() {
  166 + * return (
  167 + * <Navigator
  168 + * initialRoute={{ title: 'Awesome Scene', index: 0 }}
  169 + * renderScene={(route, navigator) =>
  170 + * <Text>Hello {route.title}!</Text>
  171 + * }
  172 + * style={{padding: 100}}
  173 + * />
  174 + * );
  175 + * }
  176 + * }
  177 + * ```
  178 + *
  179 + * In the above example, `initialRoute` is used to specify the first route. It
  180 + * contains a `title` property that identifies the route. The `renderScene`
  181 + * prop returns a function that displays text based on the route's title.
  182 + *
  183 + * ### Additional Scenes
  184 + *
  185 + * The first example demonstrated one scene. To set up multiple scenes, you pass
  186 + * the `initialRouteStack` prop to `Navigator`:
  187 + *
  188 + * ```
  189 + * render() {
  190 + * const routes = [
  191 + * {title: 'First Scene', index: 0},
  192 + * {title: 'Second Scene', index: 1},
  193 + * ];
  194 + * return (
  195 + * <Navigator
  196 + * initialRoute={routes[0]}
  197 + * initialRouteStack={routes}
  198 + * renderScene={(route, navigator) =>
  199 + * <TouchableHighlight onPress={() => {
  200 + * if (route.index === 0) {
  201 + * navigator.push(routes[1]);
  202 + * } else {
  203 + * navigator.pop();
  204 + * }
  205 + * }}>
  206 + * <Text>Hello {route.title}!</Text>
  207 + * </TouchableHighlight>
  208 + * }
  209 + * style={{padding: 100}}
  210 + * />
  211 + * );
  212 + * }
  213 + * ```
  214 + *
  215 + * In the above example, a `routes` variable is defined with two route objects
  216 + * representing two scenes. Each route has an `index` property that is used to
  217 + * manage the scene being rendered. The `renderScene` method is changed to
  218 + * either push or pop the navigator depending on the current route's index.
  219 + * Finally, the `Text` component in the scene is now wrapped in a
  220 + * `TouchableHighlight` component to help trigger the navigator transitions.
  221 + *
  222 + * ### Navigation Bar
  223 + *
  224 + * You can optionally pass in your own navigation bar by returning a
  225 + * `Navigator.NavigationBar` component to the `navigationBar` prop in
  226 + * `Navigator`. You can configure the navigation bar properties, through
  227 + * the `routeMapper` prop. There you set up the left, right, and title
  228 + * properties of the navigation bar:
  229 + *
  230 + * ```
  231 + * <Navigator
  232 + * renderScene={(route, navigator) =>
  233 + * // ...
  234 + * }
  235 + * navigationBar={
  236 + * <Navigator.NavigationBar
  237 + * routeMapper={{
  238 + * LeftButton: (route, navigator, index, navState) =>
  239 + * { return (<Text>Cancel</Text>); },
  240 + * RightButton: (route, navigator, index, navState) =>
  241 + * { return (<Text>Done</Text>); },
  242 + * Title: (route, navigator, index, navState) =>
  243 + * { return (<Text>Awesome Nav Bar</Text>); },
  244 + * }}
  245 + * style={{backgroundColor: 'gray'}}
  246 + * />
  247 + * }
  248 + * />
  249 + * ```
  250 + *
  251 + * When configuring the left, right, and title items for the navigation bar,
  252 + * you have access to info such as the current route object and navigation
  253 + * state. This allows you to customize the title for each scene as well as
  254 + * the buttons. For example, you can choose to hide the left button for one of
  255 + * the scenes.
  256 + *
  257 + * Typically you want buttons to represent the left and right buttons. Building
  258 + * on the previous example, you can set this up as follows:
  259 + *
  260 + * ```
  261 + * LeftButton: (route, navigator, index, navState) =>
  262 + * {
  263 + * if (route.index === 0) {
  264 + * return null;
  265 + * } else {
  266 + * return (
  267 + * <TouchableHighlight onPress={() => navigator.pop()}>
  268 + * <Text>Back</Text>
  269 + * </TouchableHighlight>
  270 + * );
  271 + * }
  272 + * },
  273 + * ```
  274 + *
  275 + * This sets up a left navigator bar button that's visible on scenes after the
  276 + * the first one. When the button is tapped the navigator is popped.
  277 + *
  278 + * Another type of navigation bar, with breadcrumbs, is provided by
  279 + * `Navigator.BreadcrumbNavigationBar`. You can also provide your own navigation
  280 + * bar by passing it through the `navigationBar` prop. See the
  281 + * [UIExplorer](https://github.com/facebook/react-native/tree/master/Examples/UIExplorer)
  282 + * demo to try out both built-in navigation bars out and see how to use them.
  283 + *
  284 + * ### Scene Transitions
  285 + *
  286 + * To change the animation or gesture properties of the scene, provide a
  287 + * `configureScene` prop to get the config object for a given route:
  288 + *
  289 + * ```
  290 + * <Navigator
  291 + * renderScene={(route, navigator) =>
  292 + * // ...
  293 + * }
  294 + * configureScene={(route, routeStack) =>
  295 + * Navigator.SceneConfigs.FloatFromBottom}
  296 + * />
  297 + * ```
  298 + * In the above example, the newly pushed scene will float up from the bottom.
  299 + * See `Navigator.SceneConfigs` for default animations and more info on
  300 + * available [scene config options](docs/navigator.html#configurescene).
  301 + */
  302 +var Navigator = createReactClass({
  303 +
  304 + propTypes: {
  305 + /**
  306 + * Optional function where you can configure scene animations and
  307 + * gestures. Will be invoked with `route` and `routeStack` parameters,
  308 + * where `route` corresponds to the current scene being rendered by the
  309 + * `Navigator` and `routeStack` is the set of currently mounted routes
  310 + * that the navigator could transition to.
  311 + *
  312 + * The function should return a scene configuration object.
  313 + *
  314 + * ```
  315 + * (route, routeStack) => Navigator.SceneConfigs.FloatFromRight
  316 + * ```
  317 + *
  318 + * Available scene configuration options are:
  319 + *
  320 + * - Navigator.SceneConfigs.PushFromRight (default)
  321 + * - Navigator.SceneConfigs.FloatFromRight
  322 + * - Navigator.SceneConfigs.FloatFromLeft
  323 + * - Navigator.SceneConfigs.FloatFromBottom
  324 + * - Navigator.SceneConfigs.FloatFromBottomAndroid
  325 + * - Navigator.SceneConfigs.FadeAndroid
  326 + * - Navigator.SceneConfigs.SwipeFromLeft
  327 + * - Navigator.SceneConfigs.HorizontalSwipeJump
  328 + * - Navigator.SceneConfigs.HorizontalSwipeJumpFromRight
  329 + * - Navigator.SceneConfigs.HorizontalSwipeJumpFromLeft
  330 + * - Navigator.SceneConfigs.VerticalUpSwipeJump
  331 + * - Navigator.SceneConfigs.VerticalDownSwipeJump
  332 + *
  333 + */
  334 + configureScene: PropTypes.func,
  335 +
  336 + /**
  337 + * Required function which renders the scene for a given route. Will be
  338 + * invoked with the `route` and the `navigator` object.
  339 + *
  340 + * ```
  341 + * (route, navigator) =>
  342 + * <MySceneComponent title={route.title} navigator={navigator} />
  343 + * ```
  344 + */
  345 + renderScene: PropTypes.func.isRequired,
  346 +
  347 + /**
  348 + * The initial route for navigation. A route is an object that the navigator
  349 + * will use to identify each scene it renders.
  350 + *
  351 + * If both `initialRoute` and `initialRouteStack` props are passed to
  352 + * `Navigator`, then `initialRoute` must be in a route in
  353 + * `initialRouteStack`. If `initialRouteStack` is passed as a prop but
  354 + * `initialRoute` is not, then `initialRoute` will default internally to
  355 + * the last item in `initialRouteStack`.
  356 + */
  357 + initialRoute: PropTypes.object,
  358 +
  359 + /**
  360 + * Pass this in to provide a set of routes to initially mount. This prop
  361 + * is required if `initialRoute` is not provided to the navigator. If this
  362 + * prop is not passed in, it will default internally to an array
  363 + * containing only `initialRoute`.
  364 + */
  365 + initialRouteStack: PropTypes.arrayOf(PropTypes.object),
  366 +
  367 + /**
  368 + * Pass in a function to get notified with the target route when
  369 + * the navigator component is mounted and before each navigator transition.
  370 + */
  371 + onWillFocus: PropTypes.func,
  372 +
  373 + /**
  374 + * Will be called with the new route of each scene after the transition is
  375 + * complete or after the initial mounting.
  376 + */
  377 + onDidFocus: PropTypes.func,
  378 +
  379 + /**
  380 + * Use this to provide an optional component representing a navigation bar
  381 + * that is persisted across scene transitions. This component will receive
  382 + * two props: `navigator` and `navState` representing the navigator
  383 + * component and its state. The component is re-rendered when the route
  384 + * changes.
  385 + */
  386 + navigationBar: PropTypes.node,
  387 +
  388 + /**
  389 + * Optionally pass in the navigator object from a parent `Navigator`.
  390 + */
  391 + navigator: PropTypes.object,
  392 +
  393 + /**
  394 + * Styles to apply to the container of each scene.
  395 + */
  396 + sceneStyle: ViewPropTypes.style,
  397 + },
  398 +
  399 + statics: {
  400 + BreadcrumbNavigationBar: NavigatorBreadcrumbNavigationBar,
  401 + NavigationBar: NavigatorNavigationBar,
  402 + SceneConfigs: NavigatorSceneConfigs,
  403 + },
  404 +
  405 + mixins: [TimerMixin, InteractionMixin, Subscribable.Mixin],
  406 +
  407 + getDefaultProps: function() {
  408 + return {
  409 + configureScene: () => NavigatorSceneConfigs.PushFromRight,
  410 + sceneStyle: DEFAULT_SCENE_STYLE,
  411 + };
  412 + },
  413 +
  414 + getInitialState: function() {
  415 + this._navigationBarNavigator = this.props.navigationBarNavigator || this;
  416 +
  417 + this._renderedSceneMap = new Map();
  418 +
  419 + this._sceneRefs = [];
  420 +
  421 + var routeStack = this.props.initialRouteStack || [this.props.initialRoute];
  422 + invariant(
  423 + routeStack.length >= 1,
  424 + 'Navigator requires props.initialRoute or props.initialRouteStack.'
  425 + );
  426 + var initialRouteIndex = routeStack.length - 1;
  427 + if (this.props.initialRoute) {
  428 + initialRouteIndex = routeStack.indexOf(this.props.initialRoute);
  429 + invariant(
  430 + initialRouteIndex !== -1,
  431 + 'initialRoute is not in initialRouteStack.'
  432 + );
  433 + }
  434 + return {
  435 + sceneConfigStack: routeStack.map(
  436 + (route) => this.props.configureScene(route, routeStack)
  437 + ),
  438 + routeStack,
  439 + presentedIndex: initialRouteIndex,
  440 + transitionFromIndex: null,
  441 + activeGesture: null,
  442 + pendingGestureProgress: null,
  443 + transitionQueue: [],
  444 + };
  445 + },
  446 +
  447 + componentWillMount: function() {
  448 + // TODO(t7489503): Don't need this once ES6 Class landed.
  449 + this.__defineGetter__('navigationContext', this._getNavigationContext);
  450 +
  451 + this._subRouteFocus = [];
  452 + this.parentNavigator = this.props.navigator;
  453 + this._handlers = {};
  454 + this.springSystem = new rebound.SpringSystem();
  455 + this.spring = this.springSystem.createSpring();
  456 + this.spring.setRestSpeedThreshold(0.05);
  457 + this.spring.setCurrentValue(0).setAtRest();
  458 + this.spring.addListener({
  459 + onSpringEndStateChange: () => {
  460 + if (!this._interactionHandle) {
  461 + this._interactionHandle = this.createInteractionHandle();
  462 + }
  463 + },
  464 + onSpringUpdate: () => {
  465 + this._handleSpringUpdate();
  466 + },
  467 + onSpringAtRest: () => {
  468 + this._completeTransition();
  469 + },
  470 + });
  471 + this.panGesture = PanResponder.create({
  472 + onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder,
  473 + onPanResponderRelease: this._handlePanResponderRelease,
  474 + onPanResponderMove: this._handlePanResponderMove,
  475 + onPanResponderTerminate: this._handlePanResponderTerminate,
  476 + });
  477 + this._interactionHandle = null;
  478 + this._emitWillFocus(this.state.routeStack[this.state.presentedIndex]);
  479 + },
  480 +
  481 + componentDidMount: function() {
  482 + this._isMounted = true;
  483 + this._handleSpringUpdate();
  484 + this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]);
  485 + this._enableTVEventHandler();
  486 + },
  487 +
  488 + componentWillUnmount: function() {
  489 + this._isMounted = false;
  490 + if (this._navigationContext) {
  491 + this._navigationContext.dispose();
  492 + this._navigationContext = null;
  493 + }
  494 +
  495 + this.spring.destroy();
  496 +
  497 + if (this._interactionHandle) {
  498 + this.clearInteractionHandle(this._interactionHandle);
  499 + }
  500 +
  501 + this._disableTVEventHandler();
  502 + },
  503 +
  504 + /**
  505 + * Reset every scene with an array of routes.
  506 + *
  507 + * @param {RouteStack} nextRouteStack Next route stack to reinitialize.
  508 + * All existing route stacks are destroyed and potentially recreated. There
  509 + * is no accompanying animation and this method immediately replaces and
  510 + * re-renders the navigation bar and the stack items.
  511 + */
  512 + immediatelyResetRouteStack: function(nextRouteStack) {
  513 + var destIndex = nextRouteStack.length - 1;
  514 + this._emitWillFocus(nextRouteStack[destIndex]);
  515 + this.setState({
  516 + routeStack: nextRouteStack,
  517 + sceneConfigStack: nextRouteStack.map(
  518 + route => this.props.configureScene(route, nextRouteStack)
  519 + ),
  520 + presentedIndex: destIndex,
  521 + activeGesture: null,
  522 + transitionFromIndex: null,
  523 + transitionQueue: [],
  524 + }, () => {
  525 + this._handleSpringUpdate();
  526 + var navBar = this._navBar;
  527 + if (navBar && navBar.immediatelyRefresh) {
  528 + navBar.immediatelyRefresh();
  529 + }
  530 + this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]);
  531 + });
  532 + },
  533 +
  534 + _transitionTo: function(destIndex, velocity, jumpSpringTo, cb) {
  535 + if (this.state.presentedIndex === destIndex) {
  536 + cb && cb();
  537 + return;
  538 + }
  539 +
  540 + if (this.state.transitionFromIndex !== null) {
  541 + // Navigation is still transitioning, put the `destIndex` into queue.
  542 + this.state.transitionQueue.push({
  543 + destIndex,
  544 + velocity,
  545 + cb,
  546 + });
  547 + return;
  548 + }
  549 +
  550 + this.state.transitionFromIndex = this.state.presentedIndex;
  551 + this.state.presentedIndex = destIndex;
  552 + this.state.transitionCb = cb;
  553 + this._onAnimationStart();
  554 + if (AnimationsDebugModule) {
  555 + AnimationsDebugModule.startRecordingFps();
  556 + }
  557 + var sceneConfig = this.state.sceneConfigStack[this.state.transitionFromIndex] ||
  558 + this.state.sceneConfigStack[this.state.presentedIndex];
  559 + invariant(
  560 + sceneConfig,
  561 + 'Cannot configure scene at index ' + this.state.transitionFromIndex
  562 + );
  563 + if (jumpSpringTo != null) {
  564 + this.spring.setCurrentValue(jumpSpringTo);
  565 + }
  566 + this.spring.setOvershootClampingEnabled(true);
  567 + this.spring.getSpringConfig().friction = sceneConfig.springFriction;
  568 + this.spring.getSpringConfig().tension = sceneConfig.springTension;
  569 + this.spring.setVelocity(velocity || sceneConfig.defaultTransitionVelocity);
  570 + this.spring.setEndValue(1);
  571 + },
  572 +
  573 + /**
  574 + * This happens for each frame of either a gesture or a transition. If both are
  575 + * happening, we only set values for the transition and the gesture will catch up later
  576 + */
  577 + _handleSpringUpdate: function() {
  578 + if (!this._isMounted) {
  579 + return;
  580 + }
  581 + // Prioritize handling transition in progress over a gesture:
  582 + if (this.state.transitionFromIndex != null) {
  583 + this._transitionBetween(
  584 + this.state.transitionFromIndex,
  585 + this.state.presentedIndex,
  586 + this.spring.getCurrentValue()
  587 + );
  588 + } else if (this.state.activeGesture != null) {
  589 + var presentedToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  590 + this._transitionBetween(
  591 + this.state.presentedIndex,
  592 + presentedToIndex,
  593 + this.spring.getCurrentValue()
  594 + );
  595 + }
  596 + },
  597 +
  598 + /**
  599 + * This happens at the end of a transition started by transitionTo, and when the spring catches up to a pending gesture
  600 + */
  601 + _completeTransition: function() {
  602 + if (!this._isMounted) {
  603 + return;
  604 + }
  605 +
  606 + if (this.spring.getCurrentValue() !== 1 && this.spring.getCurrentValue() !== 0) {
  607 + // The spring has finished catching up to a gesture in progress. Remove the pending progress
  608 + // and we will be in a normal activeGesture state
  609 + if (this.state.pendingGestureProgress) {
  610 + this.state.pendingGestureProgress = null;
  611 + }
  612 + return;
  613 + }
  614 + this._onAnimationEnd();
  615 + var presentedIndex = this.state.presentedIndex;
  616 + var didFocusRoute = this._subRouteFocus[presentedIndex] || this.state.routeStack[presentedIndex];
  617 +
  618 + if (AnimationsDebugModule) {
  619 + AnimationsDebugModule.stopRecordingFps(Date.now());
  620 + }
  621 + this.state.transitionFromIndex = null;
  622 + this.spring.setCurrentValue(0).setAtRest();
  623 + this._hideScenes();
  624 + if (this.state.transitionCb) {
  625 + this.state.transitionCb();
  626 + this.state.transitionCb = null;
  627 + }
  628 +
  629 + this._emitDidFocus(didFocusRoute);
  630 +
  631 + if (this._interactionHandle) {
  632 + this.clearInteractionHandle(this._interactionHandle);
  633 + this._interactionHandle = null;
  634 + }
  635 + if (this.state.pendingGestureProgress) {
  636 + // A transition completed, but there is already another gesture happening.
  637 + // Enable the scene and set the spring to catch up with the new gesture
  638 + var gestureToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  639 + this._enableScene(gestureToIndex);
  640 + this.spring.setEndValue(this.state.pendingGestureProgress);
  641 + return;
  642 + }
  643 + if (this.state.transitionQueue.length) {
  644 + var queuedTransition = this.state.transitionQueue.shift();
  645 + this._enableScene(queuedTransition.destIndex);
  646 + this._emitWillFocus(this.state.routeStack[queuedTransition.destIndex]);
  647 + this._transitionTo(
  648 + queuedTransition.destIndex,
  649 + queuedTransition.velocity,
  650 + null,
  651 + queuedTransition.cb
  652 + );
  653 + }
  654 + },
  655 +
  656 + _emitDidFocus: function(route) {
  657 + this.navigationContext.emit('didfocus', {route: route});
  658 +
  659 + if (this.props.onDidFocus) {
  660 + this.props.onDidFocus(route);
  661 + }
  662 + },
  663 +
  664 + _emitWillFocus: function(route) {
  665 + this.navigationContext.emit('willfocus', {route: route});
  666 +
  667 + var navBar = this._navBar;
  668 + if (navBar && navBar.handleWillFocus) {
  669 + navBar.handleWillFocus(route);
  670 + }
  671 + if (this.props.onWillFocus) {
  672 + this.props.onWillFocus(route);
  673 + }
  674 + },
  675 +
  676 + /**
  677 + * Hides all scenes that we are not currently on, gesturing to, or transitioning from
  678 + */
  679 + _hideScenes: function() {
  680 + var gesturingToIndex = null;
  681 + if (this.state.activeGesture) {
  682 + gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  683 + }
  684 + for (var i = 0; i < this.state.routeStack.length; i++) {
  685 + if (i === this.state.presentedIndex ||
  686 + i === this.state.transitionFromIndex ||
  687 + i === gesturingToIndex) {
  688 + continue;
  689 + }
  690 + this._disableScene(i);
  691 + }
  692 + },
  693 +
  694 + /**
  695 + * Push a scene off the screen, so that opacity:0 scenes will not block touches sent to the presented scenes
  696 + */
  697 + _disableScene: function(sceneIndex) {
  698 + this._sceneRefs[sceneIndex] &&
  699 + this._sceneRefs[sceneIndex].setNativeProps(SCENE_DISABLED_NATIVE_PROPS);
  700 + },
  701 +
  702 + /**
  703 + * Put the scene back into the state as defined by props.sceneStyle, so transitions can happen normally
  704 + */
  705 + _enableScene: function(sceneIndex) {
  706 + // First, determine what the defined styles are for scenes in this navigator
  707 + var sceneStyle = flattenStyle([BASE_SCENE_STYLE, this.props.sceneStyle]);
  708 + // Then restore the pointer events and top value for this scene
  709 + var enabledSceneNativeProps = {
  710 + pointerEvents: 'auto',
  711 + style: {
  712 + top: sceneStyle.top,
  713 + bottom: sceneStyle.bottom,
  714 + },
  715 + };
  716 + if (sceneIndex !== this.state.transitionFromIndex &&
  717 + sceneIndex !== this.state.presentedIndex) {
  718 + // If we are not in a transition from this index, make sure opacity is 0
  719 + // to prevent the enabled scene from flashing over the presented scene
  720 + enabledSceneNativeProps.style.opacity = 0;
  721 + }
  722 + this._sceneRefs[sceneIndex] &&
  723 + this._sceneRefs[sceneIndex].setNativeProps(enabledSceneNativeProps);
  724 + },
  725 +
  726 + _clearTransformations: function(sceneIndex) {
  727 + const defaultStyle = flattenStyle([DEFAULT_SCENE_STYLE]);
  728 + this._sceneRefs[sceneIndex].setNativeProps({ style: defaultStyle });
  729 + },
  730 +
  731 + _onAnimationStart: function() {
  732 + var fromIndex = this.state.presentedIndex;
  733 + var toIndex = this.state.presentedIndex;
  734 + if (this.state.transitionFromIndex != null) {
  735 + fromIndex = this.state.transitionFromIndex;
  736 + } else if (this.state.activeGesture) {
  737 + toIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  738 + }
  739 + this._setRenderSceneToHardwareTextureAndroid(fromIndex, true);
  740 + this._setRenderSceneToHardwareTextureAndroid(toIndex, true);
  741 + var navBar = this._navBar;
  742 + if (navBar && navBar.onAnimationStart) {
  743 + navBar.onAnimationStart(fromIndex, toIndex);
  744 + }
  745 + },
  746 +
  747 + _onAnimationEnd: function() {
  748 + var max = this.state.routeStack.length - 1;
  749 + for (var index = 0; index <= max; index++) {
  750 + this._setRenderSceneToHardwareTextureAndroid(index, false);
  751 + }
  752 +
  753 + var navBar = this._navBar;
  754 + if (navBar && navBar.onAnimationEnd) {
  755 + navBar.onAnimationEnd();
  756 + }
  757 + },
  758 +
  759 + _setRenderSceneToHardwareTextureAndroid: function(sceneIndex, shouldRenderToHardwareTexture) {
  760 + var viewAtIndex = this._sceneRefs[sceneIndex];
  761 + if (viewAtIndex === null || viewAtIndex === undefined) {
  762 + return;
  763 + }
  764 + viewAtIndex.setNativeProps({renderToHardwareTextureAndroid: shouldRenderToHardwareTexture});
  765 + },
  766 +
  767 + _handleTouchStart: function() {
  768 + this._eligibleGestures = GESTURE_ACTIONS;
  769 + },
  770 +
  771 + _handleMoveShouldSetPanResponder: function(e, gestureState) {
  772 + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
  773 + if (!sceneConfig) {
  774 + return false;
  775 + }
  776 + this._expectingGestureGrant =
  777 + this._matchGestureAction(this._eligibleGestures, sceneConfig.gestures, gestureState);
  778 + return !!this._expectingGestureGrant;
  779 + },
  780 +
  781 + _doesGestureOverswipe: function(gestureName) {
  782 + var wouldOverswipeBack = this.state.presentedIndex <= 0 &&
  783 + (gestureName === 'pop' || gestureName === 'jumpBack');
  784 + var wouldOverswipeForward = this.state.presentedIndex >= this.state.routeStack.length - 1 &&
  785 + gestureName === 'jumpForward';
  786 + return wouldOverswipeForward || wouldOverswipeBack;
  787 + },
  788 +
  789 + _deltaForGestureAction: function(gestureAction) {
  790 + switch (gestureAction) {
  791 + case 'pop':
  792 + case 'jumpBack':
  793 + return -1;
  794 + case 'jumpForward':
  795 + return 1;
  796 + default:
  797 + invariant(false, 'Unsupported gesture action ' + gestureAction);
  798 + return;
  799 + }
  800 + },
  801 +
  802 + _handlePanResponderRelease: function(e, gestureState) {
  803 + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
  804 + var releaseGestureAction = this.state.activeGesture;
  805 + if (!releaseGestureAction) {
  806 + // The gesture may have been detached while responder, so there is no action here
  807 + return;
  808 + }
  809 + var releaseGesture = sceneConfig.gestures[releaseGestureAction];
  810 + var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  811 + if (this.spring.getCurrentValue() === 0) {
  812 + // The spring is at zero, so the gesture is already complete
  813 + this.spring.setCurrentValue(0).setAtRest();
  814 + this._completeTransition();
  815 + return;
  816 + }
  817 + var isTravelVertical = releaseGesture.direction === 'top-to-bottom' || releaseGesture.direction === 'bottom-to-top';
  818 + var isTravelInverted = releaseGesture.direction === 'right-to-left' || releaseGesture.direction === 'bottom-to-top';
  819 + var velocity, gestureDistance;
  820 + if (isTravelVertical) {
  821 + velocity = isTravelInverted ? -gestureState.vy : gestureState.vy;
  822 + gestureDistance = isTravelInverted ? -gestureState.dy : gestureState.dy;
  823 + } else {
  824 + velocity = isTravelInverted ? -gestureState.vx : gestureState.vx;
  825 + gestureDistance = isTravelInverted ? -gestureState.dx : gestureState.dx;
  826 + }
  827 + var transitionVelocity = clamp(-10, velocity, 10);
  828 + if (Math.abs(velocity) < releaseGesture.notMoving) {
  829 + // The gesture velocity is so slow, is "not moving"
  830 + var hasGesturedEnoughToComplete = gestureDistance > releaseGesture.fullDistance * releaseGesture.stillCompletionRatio;
  831 + transitionVelocity = hasGesturedEnoughToComplete ? releaseGesture.snapVelocity : -releaseGesture.snapVelocity;
  832 + }
  833 + if (transitionVelocity < 0 || this._doesGestureOverswipe(releaseGestureAction)) {
  834 + // This gesture is to an overswiped region or does not have enough velocity to complete
  835 + // If we are currently mid-transition, then this gesture was a pending gesture. Because this gesture takes no action, we can stop here
  836 + if (this.state.transitionFromIndex == null) {
  837 + // There is no current transition, so we need to transition back to the presented index
  838 + var transitionBackToPresentedIndex = this.state.presentedIndex;
  839 + // slight hack: change the presented index for a moment in order to transitionTo correctly
  840 + this.state.presentedIndex = destIndex;
  841 + this._transitionTo(
  842 + transitionBackToPresentedIndex,
  843 + -transitionVelocity,
  844 + 1 - this.spring.getCurrentValue()
  845 + );
  846 + }
  847 + } else {
  848 + // The gesture has enough velocity to complete, so we transition to the gesture's destination
  849 + this._emitWillFocus(this.state.routeStack[destIndex]);
  850 + this._transitionTo(
  851 + destIndex,
  852 + transitionVelocity,
  853 + null,
  854 + () => {
  855 + if (releaseGestureAction === 'pop') {
  856 + this._cleanScenesPastIndex(destIndex);
  857 + }
  858 + }
  859 + );
  860 + }
  861 + this._detachGesture();
  862 + },
  863 +
  864 + _handlePanResponderTerminate: function(e, gestureState) {
  865 + if (this.state.activeGesture == null) {
  866 + return;
  867 + }
  868 + var destIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  869 + this._detachGesture();
  870 + var transitionBackToPresentedIndex = this.state.presentedIndex;
  871 + // slight hack: change the presented index for a moment in order to transitionTo correctly
  872 + this.state.presentedIndex = destIndex;
  873 + this._transitionTo(
  874 + transitionBackToPresentedIndex,
  875 + null,
  876 + 1 - this.spring.getCurrentValue()
  877 + );
  878 + },
  879 +
  880 + _attachGesture: function(gestureId) {
  881 + this.state.activeGesture = gestureId;
  882 + var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  883 + this._enableScene(gesturingToIndex);
  884 + },
  885 +
  886 + _detachGesture: function() {
  887 + this.state.activeGesture = null;
  888 + this.state.pendingGestureProgress = null;
  889 + this._hideScenes();
  890 + },
  891 +
  892 + _handlePanResponderMove: function(e, gestureState) {
  893 + if (this._isMoveGestureAttached !== undefined) {
  894 + invariant(
  895 + this._expectingGestureGrant,
  896 + 'Responder granted unexpectedly.'
  897 + );
  898 + this._attachGesture(this._expectingGestureGrant);
  899 + this._onAnimationStart();
  900 + this._expectingGestureGrant = undefined;
  901 + }
  902 +
  903 + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
  904 + if (this.state.activeGesture) {
  905 + var gesture = sceneConfig.gestures[this.state.activeGesture];
  906 + return this._moveAttachedGesture(gesture, gestureState);
  907 + }
  908 + var matchedGesture = this._matchGestureAction(GESTURE_ACTIONS, sceneConfig.gestures, gestureState);
  909 + if (matchedGesture) {
  910 + this._attachGesture(matchedGesture);
  911 + }
  912 + },
  913 +
  914 + _moveAttachedGesture: function(gesture, gestureState) {
  915 + var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top';
  916 + var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top';
  917 + var distance = isTravelVertical ? gestureState.dy : gestureState.dx;
  918 + distance = isTravelInverted ? -distance : distance;
  919 + var gestureDetectMovement = gesture.gestureDetectMovement;
  920 + var nextProgress = (distance - gestureDetectMovement) /
  921 + (gesture.fullDistance - gestureDetectMovement);
  922 + if (nextProgress < 0 && gesture.isDetachable) {
  923 + var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
  924 + this._transitionBetween(this.state.presentedIndex, gesturingToIndex, 0);
  925 + this._detachGesture();
  926 + if (this.state.pendingGestureProgress != null) {
  927 + this.spring.setCurrentValue(0);
  928 + }
  929 + return;
  930 + }
  931 + if (gesture.overswipe && this._doesGestureOverswipe(this.state.activeGesture)) {
  932 + var frictionConstant = gesture.overswipe.frictionConstant;
  933 + var frictionByDistance = gesture.overswipe.frictionByDistance;
  934 + var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance));
  935 + nextProgress *= frictionRatio;
  936 + }
  937 + nextProgress = clamp(0, nextProgress, 1);
  938 + if (this.state.transitionFromIndex != null) {
  939 + this.state.pendingGestureProgress = nextProgress;
  940 + } else if (this.state.pendingGestureProgress) {
  941 + this.spring.setEndValue(nextProgress);
  942 + } else {
  943 + this.spring.setCurrentValue(nextProgress);
  944 + }
  945 + },
  946 +
  947 + _matchGestureAction: function(eligibleGestures, gestures, gestureState) {
  948 + if (!gestures || !eligibleGestures || !eligibleGestures.some) {
  949 + return null;
  950 + }
  951 + var matchedGesture = null;
  952 + eligibleGestures.some((gestureName, gestureIndex) => {
  953 + var gesture = gestures[gestureName];
  954 + if (!gesture) {
  955 + return;
  956 + }
  957 + if (gesture.overswipe == null && this._doesGestureOverswipe(gestureName)) {
  958 + // cannot swipe past first or last scene without overswiping
  959 + return false;
  960 + }
  961 + var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top';
  962 + var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top';
  963 + var startedLoc = isTravelVertical ? gestureState.y0 : gestureState.x0;
  964 + var currentLoc = isTravelVertical ? gestureState.moveY : gestureState.moveX;
  965 + var travelDist = isTravelVertical ? gestureState.dy : gestureState.dx;
  966 + var oppositeAxisTravelDist =
  967 + isTravelVertical ? gestureState.dx : gestureState.dy;
  968 + var edgeHitWidth = gesture.edgeHitWidth;
  969 + if (isTravelInverted) {
  970 + startedLoc = -startedLoc;
  971 + currentLoc = -currentLoc;
  972 + travelDist = -travelDist;
  973 + oppositeAxisTravelDist = -oppositeAxisTravelDist;
  974 + edgeHitWidth = isTravelVertical ?
  975 + -(SCREEN_HEIGHT - edgeHitWidth) :
  976 + -(SCREEN_WIDTH - edgeHitWidth);
  977 + }
  978 + if (startedLoc === 0) {
  979 + startedLoc = currentLoc;
  980 + }
  981 + var moveStartedInRegion = gesture.edgeHitWidth == null ||
  982 + startedLoc < edgeHitWidth;
  983 + if (!moveStartedInRegion) {
  984 + return false;
  985 + }
  986 + var moveTravelledFarEnough = travelDist >= gesture.gestureDetectMovement;
  987 + if (!moveTravelledFarEnough) {
  988 + return false;
  989 + }
  990 + var directionIsCorrect = Math.abs(travelDist) > Math.abs(oppositeAxisTravelDist) * gesture.directionRatio;
  991 + if (directionIsCorrect) {
  992 + matchedGesture = gestureName;
  993 + return true;
  994 + } else {
  995 + this._eligibleGestures = this._eligibleGestures.slice().splice(gestureIndex, 1);
  996 + }
  997 + });
  998 + return matchedGesture || null;
  999 + },
  1000 +
  1001 + _transitionSceneStyle: function(fromIndex, toIndex, progress, index) {
  1002 + var viewAtIndex = this._sceneRefs[index];
  1003 + if (viewAtIndex === null || viewAtIndex === undefined) {
  1004 + return;
  1005 + }
  1006 + // Use toIndex animation when we move forwards. Use fromIndex when we move back
  1007 + var sceneConfigIndex = fromIndex < toIndex ? toIndex : fromIndex;
  1008 + var sceneConfig = this.state.sceneConfigStack[sceneConfigIndex];
  1009 + // this happens for overswiping when there is no scene at toIndex
  1010 + if (!sceneConfig) {
  1011 + sceneConfig = this.state.sceneConfigStack[sceneConfigIndex - 1];
  1012 + }
  1013 + var styleToUse = {};
  1014 + var useFn = index < fromIndex || index < toIndex ?
  1015 + sceneConfig.animationInterpolators.out :
  1016 + sceneConfig.animationInterpolators.into;
  1017 + var directionAdjustedProgress = fromIndex < toIndex ? progress : 1 - progress;
  1018 + var didChange = useFn(styleToUse, directionAdjustedProgress);
  1019 + if (didChange) {
  1020 + viewAtIndex.setNativeProps({style: styleToUse});
  1021 + }
  1022 + },
  1023 +
  1024 + _transitionBetween: function(fromIndex, toIndex, progress) {
  1025 + this._transitionSceneStyle(fromIndex, toIndex, progress, fromIndex);
  1026 + this._transitionSceneStyle(fromIndex, toIndex, progress, toIndex);
  1027 + var navBar = this._navBar;
  1028 + if (navBar && navBar.updateProgress && toIndex >= 0 && fromIndex >= 0) {
  1029 + navBar.updateProgress(progress, fromIndex, toIndex);
  1030 + }
  1031 + },
  1032 +
  1033 + _handleResponderTerminationRequest: function() {
  1034 + return false;
  1035 + },
  1036 +
  1037 + _getDestIndexWithinBounds: function(n) {
  1038 + var currentIndex = this.state.presentedIndex;
  1039 + var destIndex = currentIndex + n;
  1040 + invariant(
  1041 + destIndex >= 0,
  1042 + 'Cannot jump before the first route.'
  1043 + );
  1044 + var maxIndex = this.state.routeStack.length - 1;
  1045 + invariant(
  1046 + maxIndex >= destIndex,
  1047 + 'Cannot jump past the last route.'
  1048 + );
  1049 + return destIndex;
  1050 + },
  1051 +
  1052 + _jumpN: function(n) {
  1053 + var destIndex = this._getDestIndexWithinBounds(n);
  1054 + this._enableScene(destIndex);
  1055 + this._emitWillFocus(this.state.routeStack[destIndex]);
  1056 + this._transitionTo(destIndex);
  1057 + },
  1058 +
  1059 + /**
  1060 + * Transition to an existing scene without unmounting.
  1061 + * @param {object} route Route to transition to. The specified route must
  1062 + * be in the currently mounted set of routes defined in `routeStack`.
  1063 + */
  1064 + jumpTo: function(route) {
  1065 + var destIndex = this.state.routeStack.indexOf(route);
  1066 + invariant(
  1067 + destIndex !== -1,
  1068 + 'Cannot jump to route that is not in the route stack'
  1069 + );
  1070 + this._jumpN(destIndex - this.state.presentedIndex);
  1071 + },
  1072 +
  1073 + /**
  1074 + * Jump forward to the next scene in the route stack.
  1075 + */
  1076 + jumpForward: function() {
  1077 + this._jumpN(1);
  1078 + },
  1079 +
  1080 + /**
  1081 + * Jump backward without unmounting the current scene.
  1082 + */
  1083 + jumpBack: function() {
  1084 + this._jumpN(-1);
  1085 + },
  1086 +
  1087 + /**
  1088 + * Navigate forward to a new scene, squashing any scenes that you could
  1089 + * jump forward to.
  1090 + * @param {object} route Route to push into the navigator stack.
  1091 + */
  1092 + push: function(route) {
  1093 + invariant(!!route, 'Must supply route to push');
  1094 + var activeLength = this.state.presentedIndex + 1;
  1095 + var activeStack = this.state.routeStack.slice(0, activeLength);
  1096 + var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
  1097 + var nextStack = activeStack.concat([route]);
  1098 + var destIndex = nextStack.length - 1;
  1099 + var nextSceneConfig = this.props.configureScene(route, nextStack);
  1100 + var nextAnimationConfigStack = activeAnimationConfigStack.concat([nextSceneConfig]);
  1101 + this._emitWillFocus(nextStack[destIndex]);
  1102 + this.setState({
  1103 + routeStack: nextStack,
  1104 + sceneConfigStack: nextAnimationConfigStack,
  1105 + }, () => {
  1106 + this._enableScene(destIndex);
  1107 + this._transitionTo(destIndex, nextSceneConfig.defaultTransitionVelocity);
  1108 + });
  1109 + },
  1110 +
  1111 + /**
  1112 + * Go back N scenes at once. When N=1, behavior matches `pop()`.
  1113 + * When N is invalid(negative or bigger than current routes count), do nothing.
  1114 + * @param {number} n The number of scenes to pop. Should be an integer.
  1115 + */
  1116 + popN: function(n) {
  1117 + invariant(typeof n === 'number', 'Must supply a number to popN');
  1118 + n = parseInt(n, 10);
  1119 + if (n <= 0 || this.state.presentedIndex - n < 0) {
  1120 + return;
  1121 + }
  1122 + var popIndex = this.state.presentedIndex - n;
  1123 + var presentedRoute = this.state.routeStack[this.state.presentedIndex];
  1124 + var popSceneConfig = this.props.configureScene(presentedRoute); // using the scene config of the currently presented view
  1125 + this._enableScene(popIndex);
  1126 + // This is needed because scene at the pop index may be transformed
  1127 + // with a configuration different from the configuration on the presented
  1128 + // route.
  1129 + this._clearTransformations(popIndex);
  1130 + this._emitWillFocus(this.state.routeStack[popIndex]);
  1131 + this._transitionTo(
  1132 + popIndex,
  1133 + popSceneConfig.defaultTransitionVelocity,
  1134 + null, // no spring jumping
  1135 + () => {
  1136 + this._cleanScenesPastIndex(popIndex);
  1137 + }
  1138 + );
  1139 + },
  1140 +
  1141 + /**
  1142 + * Transition back and unmount the current scene.
  1143 + */
  1144 + pop: function() {
  1145 + if (this.state.transitionQueue.length) {
  1146 + // This is the workaround to prevent user from firing multiple `pop()`
  1147 + // calls that may pop the routes beyond the limit.
  1148 + // Because `this.state.presentedIndex` does not update until the
  1149 + // transition starts, we can't reliably use `this.state.presentedIndex`
  1150 + // to know whether we can safely keep popping the routes or not at this
  1151 + // moment.
  1152 + return;
  1153 + }
  1154 +
  1155 + this.popN(1);
  1156 + },
  1157 +
  1158 + /**
  1159 + * Replace a scene as specified by an index.
  1160 + * @param {object} route Route representing the new scene to render.
  1161 + * @param {number} index The route in the stack that should be replaced.
  1162 + * If negative, it counts from the back of the stack.
  1163 + * @param {Function} cb Callback function when the scene has been replaced.
  1164 + */
  1165 + replaceAtIndex: function(route, index, cb) {
  1166 + invariant(!!route, 'Must supply route to replace');
  1167 + if (index < 0) {
  1168 + index += this.state.routeStack.length;
  1169 + }
  1170 +
  1171 + if (this.state.routeStack.length <= index) {
  1172 + return;
  1173 + }
  1174 +
  1175 + var nextRouteStack = this.state.routeStack.slice();
  1176 + var nextAnimationModeStack = this.state.sceneConfigStack.slice();
  1177 + nextRouteStack[index] = route;
  1178 + nextAnimationModeStack[index] = this.props.configureScene(route, nextRouteStack);
  1179 +
  1180 + if (index === this.state.presentedIndex) {
  1181 + this._emitWillFocus(route);
  1182 + }
  1183 + this.setState({
  1184 + routeStack: nextRouteStack,
  1185 + sceneConfigStack: nextAnimationModeStack,
  1186 + }, () => {
  1187 + if (index === this.state.presentedIndex) {
  1188 + this._emitDidFocus(route);
  1189 + }
  1190 + cb && cb();
  1191 + });
  1192 + },
  1193 +
  1194 + /**
  1195 + * Replace the current scene with a new route.
  1196 + * @param {object} route Route that replaces the current scene.
  1197 + */
  1198 + replace: function(route) {
  1199 + this.replaceAtIndex(route, this.state.presentedIndex);
  1200 + },
  1201 +
  1202 + /**
  1203 + * Replace the previous scene.
  1204 + * @param {object} route Route that replaces the previous scene.
  1205 + */
  1206 + replacePrevious: function(route) {
  1207 + this.replaceAtIndex(route, this.state.presentedIndex - 1);
  1208 + },
  1209 +
  1210 + /**
  1211 + * Pop to the first scene in the stack, unmounting every other scene.
  1212 + */
  1213 + popToTop: function() {
  1214 + this.popToRoute(this.state.routeStack[0]);
  1215 + },
  1216 +
  1217 + /**
  1218 + * Pop to a particular scene, as specified by its route.
  1219 + * All scenes after it will be unmounted.
  1220 + * @param {object} route Route to pop to.
  1221 + */
  1222 + popToRoute: function(route) {
  1223 + var indexOfRoute = this.state.routeStack.indexOf(route);
  1224 + invariant(
  1225 + indexOfRoute !== -1,
  1226 + 'Calling popToRoute for a route that doesn\'t exist!'
  1227 + );
  1228 + var numToPop = this.state.presentedIndex - indexOfRoute;
  1229 + this.popN(numToPop);
  1230 + },
  1231 +
  1232 + /**
  1233 + * Replace the previous scene and pop to it.
  1234 + * @param {object} route Route that replaces the previous scene.
  1235 + */
  1236 + replacePreviousAndPop: function(route) {
  1237 + if (this.state.routeStack.length < 2) {
  1238 + return;
  1239 + }
  1240 + this.replacePrevious(route);
  1241 + this.pop();
  1242 + },
  1243 +
  1244 + /**
  1245 + * Navigate to a new scene and reset route stack.
  1246 + * @param {object} route Route to navigate to.
  1247 + */
  1248 + resetTo: function(route) {
  1249 + invariant(!!route, 'Must supply route to push');
  1250 + this.replaceAtIndex(route, 0, () => {
  1251 + // Do not use popToRoute here, because race conditions could prevent the
  1252 + // route from existing at this time. Instead, just go to index 0
  1253 + this.popN(this.state.presentedIndex);
  1254 + });
  1255 + },
  1256 +
  1257 + /**
  1258 + * Returns the current list of routes.
  1259 + */
  1260 + getCurrentRoutes: function() {
  1261 + // Clone before returning to avoid caller mutating the stack
  1262 + return this.state.routeStack.slice();
  1263 + },
  1264 +
  1265 + _cleanScenesPastIndex: function(index) {
  1266 + var newStackLength = index + 1;
  1267 + // Remove any unneeded rendered routes.
  1268 + if (newStackLength < this.state.routeStack.length) {
  1269 + this.setState({
  1270 + sceneConfigStack: this.state.sceneConfigStack.slice(0, newStackLength),
  1271 + routeStack: this.state.routeStack.slice(0, newStackLength),
  1272 + });
  1273 + }
  1274 + },
  1275 +
  1276 + _renderScene: function(route, i) {
  1277 + var disabledSceneStyle = null;
  1278 + var disabledScenePointerEvents = 'auto';
  1279 + if (i !== this.state.presentedIndex) {
  1280 + disabledSceneStyle = styles.disabledScene;
  1281 + disabledScenePointerEvents = 'none';
  1282 + }
  1283 + return (
  1284 + <View
  1285 + collapsable={false}
  1286 + key={'scene_' + getRouteID(route)}
  1287 + ref={(scene) => {
  1288 + this._sceneRefs[i] = scene;
  1289 + }}
  1290 + onStartShouldSetResponderCapture={() => {
  1291 + return (this.state.transitionFromIndex != null);
  1292 + }}
  1293 + pointerEvents={disabledScenePointerEvents}
  1294 + style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
  1295 + {this.props.renderScene(
  1296 + route,
  1297 + this
  1298 + )}
  1299 + </View>
  1300 + );
  1301 + },
  1302 +
  1303 + _renderNavigationBar: function() {
  1304 + const { navigationBar } = this.props;
  1305 + if (!navigationBar) {
  1306 + return null;
  1307 + }
  1308 + return React.cloneElement(navigationBar, {
  1309 + ref: (navBar) => {
  1310 + this._navBar = navBar;
  1311 + if (navigationBar && typeof navigationBar.ref === 'function') {
  1312 + navigationBar.ref(navBar);
  1313 + }
  1314 + },
  1315 + navigator: this._navigationBarNavigator,
  1316 + navState: this.state,
  1317 + });
  1318 + },
  1319 +
  1320 + _tvEventHandler: TVEventHandler,
  1321 +
  1322 + _enableTVEventHandler: function() {
  1323 + if (!TVEventHandler) {
  1324 + return;
  1325 + }
  1326 + this._tvEventHandler = new TVEventHandler();
  1327 + this._tvEventHandler.enable(this, function(cmp, evt) {
  1328 + if (evt && evt.eventType === 'menu') {
  1329 + cmp.pop();
  1330 + }
  1331 + });
  1332 + },
  1333 +
  1334 + _disableTVEventHandler: function() {
  1335 + if (this._tvEventHandler) {
  1336 + this._tvEventHandler.disable();
  1337 + delete this._tvEventHandler;
  1338 + }
  1339 + },
  1340 +
  1341 + render: function() {
  1342 + var newRenderedSceneMap = new Map();
  1343 + var scenes = this.state.routeStack.map((route, index) => {
  1344 + var renderedScene;
  1345 + if (this._renderedSceneMap.has(route) &&
  1346 + index !== this.state.presentedIndex) {
  1347 + renderedScene = this._renderedSceneMap.get(route);
  1348 + } else {
  1349 + renderedScene = this._renderScene(route, index);
  1350 + }
  1351 + newRenderedSceneMap.set(route, renderedScene);
  1352 + return renderedScene;
  1353 + });
  1354 + this._renderedSceneMap = newRenderedSceneMap;
  1355 + return (
  1356 + <View style={[styles.container, this.props.style]}>
  1357 + <View
  1358 + style={styles.transitioner}
  1359 + {...this.panGesture.panHandlers}
  1360 + onTouchStart={this._handleTouchStart}
  1361 + onResponderTerminationRequest={
  1362 + this._handleResponderTerminationRequest
  1363 + }>
  1364 + {scenes}
  1365 + </View>
  1366 + {this._renderNavigationBar()}
  1367 + </View>
  1368 + );
  1369 + },
  1370 +
  1371 + _getNavigationContext: function() {
  1372 + if (!this._navigationContext) {
  1373 + this._navigationContext = new NavigationContext();
  1374 + }
  1375 + return this._navigationContext;
  1376 + }
  1377 +});
  1378 +
  1379 +module.exports = Navigator;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +import {
  29 + Platform,
  30 + StyleSheet,
  31 + View,
  32 + ViewPropTypes,
  33 +} from 'react-native';
  34 +import React from 'react';
  35 +
  36 +const NavigatorBreadcrumbNavigationBarStyles = require('./NavigatorBreadcrumbNavigationBarStyles');
  37 +const NavigatorNavigationBarStylesAndroid = require('./NavigatorNavigationBarStylesAndroid');
  38 +const NavigatorNavigationBarStylesIOS = require('./NavigatorNavigationBarStylesIOS');
  39 +
  40 +const guid = require('./guid');
  41 +const invariant = require('fbjs/lib/invariant');
  42 +
  43 +const { Map } = require('immutable');
  44 +
  45 +const Interpolators = NavigatorBreadcrumbNavigationBarStyles.Interpolators;
  46 +const NavigatorNavigationBarStyles = Platform.OS === 'android' ?
  47 + NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS;
  48 +const PropTypes = require('prop-types');
  49 +
  50 +/**
  51 + * Reusable props objects.
  52 + */
  53 +const CRUMB_PROPS = Interpolators.map(() => ({style: {}}));
  54 +const ICON_PROPS = Interpolators.map(() => ({style: {}}));
  55 +const SEPARATOR_PROPS = Interpolators.map(() => ({style: {}}));
  56 +const TITLE_PROPS = Interpolators.map(() => ({style: {}}));
  57 +const RIGHT_BUTTON_PROPS = Interpolators.map(() => ({style: {}}));
  58 +
  59 +
  60 +function navStatePresentedIndex(navState) {
  61 + if (navState.presentedIndex !== undefined) {
  62 + return navState.presentedIndex;
  63 + }
  64 + // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS`
  65 + return navState.observedTopOfStack;
  66 +}
  67 +
  68 +
  69 +/**
  70 + * The first route is initially rendered using a different style than all
  71 + * future routes.
  72 + *
  73 + * @param {number} index Index of breadcrumb.
  74 + * @return {object} Style config for initial rendering of index.
  75 + */
  76 +function initStyle(index, presentedIndex) {
  77 + return index === presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Center[index] :
  78 + index < presentedIndex ? NavigatorBreadcrumbNavigationBarStyles.Left[index] :
  79 + NavigatorBreadcrumbNavigationBarStyles.Right[index];
  80 +}
  81 +
  82 +class NavigatorBreadcrumbNavigationBar extends React.Component {
  83 + static propTypes = {
  84 + navigator: PropTypes.shape({
  85 + push: PropTypes.func,
  86 + pop: PropTypes.func,
  87 + replace: PropTypes.func,
  88 + popToRoute: PropTypes.func,
  89 + popToTop: PropTypes.func,
  90 + }),
  91 + routeMapper: PropTypes.shape({
  92 + rightContentForRoute: PropTypes.func,
  93 + titleContentForRoute: PropTypes.func,
  94 + iconForRoute: PropTypes.func,
  95 + }),
  96 + navState: PropTypes.shape({
  97 + routeStack: PropTypes.arrayOf(PropTypes.object),
  98 + presentedIndex: PropTypes.number,
  99 + }),
  100 + style: ViewPropTypes.style,
  101 + };
  102 +
  103 + static Styles = NavigatorBreadcrumbNavigationBarStyles;
  104 +
  105 + _updateIndexProgress(progress, index, fromIndex, toIndex) {
  106 + var amount = toIndex > fromIndex ? progress : (1 - progress);
  107 + var oldDistToCenter = index - fromIndex;
  108 + var newDistToCenter = index - toIndex;
  109 + var interpolate;
  110 + invariant(
  111 + Interpolators[index],
  112 + 'Cannot find breadcrumb interpolators for ' + index
  113 + );
  114 + if (oldDistToCenter > 0 && newDistToCenter === 0 ||
  115 + newDistToCenter > 0 && oldDistToCenter === 0) {
  116 + interpolate = Interpolators[index].RightToCenter;
  117 + } else if (oldDistToCenter < 0 && newDistToCenter === 0 ||
  118 + newDistToCenter < 0 && oldDistToCenter === 0) {
  119 + interpolate = Interpolators[index].CenterToLeft;
  120 + } else if (oldDistToCenter === newDistToCenter) {
  121 + interpolate = Interpolators[index].RightToCenter;
  122 + } else {
  123 + interpolate = Interpolators[index].RightToLeft;
  124 + }
  125 +
  126 + if (interpolate.Crumb(CRUMB_PROPS[index].style, amount)) {
  127 + this._setPropsIfExists('crumb_' + index, CRUMB_PROPS[index]);
  128 + }
  129 + if (interpolate.Icon(ICON_PROPS[index].style, amount)) {
  130 + this._setPropsIfExists('icon_' + index, ICON_PROPS[index]);
  131 + }
  132 + if (interpolate.Separator(SEPARATOR_PROPS[index].style, amount)) {
  133 + this._setPropsIfExists('separator_' + index, SEPARATOR_PROPS[index]);
  134 + }
  135 + if (interpolate.Title(TITLE_PROPS[index].style, amount)) {
  136 + this._setPropsIfExists('title_' + index, TITLE_PROPS[index]);
  137 + }
  138 + var right = this.refs['right_' + index];
  139 +
  140 + const rightButtonStyle = RIGHT_BUTTON_PROPS[index].style;
  141 + if (right && interpolate.RightItem(rightButtonStyle, amount)) {
  142 + right.setNativeProps({
  143 + style: rightButtonStyle,
  144 + pointerEvents: rightButtonStyle.opacity === 0 ? 'none' : 'auto',
  145 + });
  146 + }
  147 + }
  148 +
  149 + updateProgress(progress, fromIndex, toIndex) {
  150 + var max = Math.max(fromIndex, toIndex);
  151 + var min = Math.min(fromIndex, toIndex);
  152 + for (var index = min; index <= max; index++) {
  153 + this._updateIndexProgress(progress, index, fromIndex, toIndex);
  154 + }
  155 + }
  156 +
  157 + onAnimationStart(fromIndex, toIndex) {
  158 + var max = Math.max(fromIndex, toIndex);
  159 + var min = Math.min(fromIndex, toIndex);
  160 + for (var index = min; index <= max; index++) {
  161 + this._setRenderViewsToHardwareTextureAndroid(index, true);
  162 + }
  163 + }
  164 +
  165 + onAnimationEnd() {
  166 + var max = this.props.navState.routeStack.length - 1;
  167 + for (var index = 0; index <= max; index++) {
  168 + this._setRenderViewsToHardwareTextureAndroid(index, false);
  169 + }
  170 + }
  171 +
  172 + _setRenderViewsToHardwareTextureAndroid(index, renderToHardwareTexture) {
  173 + var props = {
  174 + renderToHardwareTextureAndroid: renderToHardwareTexture,
  175 + };
  176 +
  177 + this._setPropsIfExists('icon_' + index, props);
  178 + this._setPropsIfExists('separator_' + index, props);
  179 + this._setPropsIfExists('title_' + index, props);
  180 + this._setPropsIfExists('right_' + index, props);
  181 + }
  182 +
  183 + componentWillMount() {
  184 + this._reset();
  185 + }
  186 +
  187 + render() {
  188 + var navState = this.props.navState;
  189 + var icons = navState && navState.routeStack.map(this._getBreadcrumb);
  190 + var titles = navState.routeStack.map(this._getTitle);
  191 + var buttons = navState.routeStack.map(this._getRightButton);
  192 +
  193 + return (
  194 + <View
  195 + key={this._key}
  196 + style={[styles.breadCrumbContainer, this.props.style]}>
  197 + {titles}
  198 + {icons}
  199 + {buttons}
  200 + </View>
  201 + );
  202 + }
  203 +
  204 + immediatelyRefresh() {
  205 + this._reset();
  206 + this.forceUpdate();
  207 + }
  208 +
  209 + _reset() {
  210 + this._key = guid();
  211 + this._descriptors = {
  212 + title: new Map(),
  213 + right: new Map(),
  214 + };
  215 + }
  216 +
  217 + _getBreadcrumb = (route, index) => {
  218 + /**
  219 + * To prevent the case where titles on an empty navigation stack covers the first icon and
  220 + * becomes partially unpressable, we set the first breadcrumb to be unpressable by default, and
  221 + * make it pressable when there are multiple items in the stack.
  222 + */
  223 + const pointerEvents = (
  224 + (this.props.navState.routeStack.length <= 1 && index === 0) ?
  225 + 'none' :
  226 + 'auto'
  227 + );
  228 + const navBarRouteMapper = this.props.routeMapper;
  229 + const firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState));
  230 +
  231 + var breadcrumbDescriptor = (
  232 + <View
  233 + key={'crumb_' + index}
  234 + pointerEvents={pointerEvents}
  235 + ref={'crumb_' + index}
  236 + style={firstStyles.Crumb}>
  237 + <View ref={'icon_' + index} style={firstStyles.Icon}>
  238 + {navBarRouteMapper.iconForRoute(route, this.props.navigator)}
  239 + </View>
  240 + <View ref={'separator_' + index} style={firstStyles.Separator}>
  241 + {navBarRouteMapper.separatorForRoute(route, this.props.navigator)}
  242 + </View>
  243 + </View>
  244 + );
  245 +
  246 + return breadcrumbDescriptor;
  247 + };
  248 +
  249 + _getTitle = (route, index) => {
  250 + if (this._descriptors.title.has(route)) {
  251 + return this._descriptors.title.get(route);
  252 + }
  253 +
  254 + var titleContent = this.props.routeMapper.titleContentForRoute(
  255 + this.props.navState.routeStack[index],
  256 + this.props.navigator
  257 + );
  258 + var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState));
  259 +
  260 + var titleDescriptor = (
  261 + <View
  262 + key={'title_' + index}
  263 + ref={'title_' + index}
  264 + style={firstStyles.Title}>
  265 + {titleContent}
  266 + </View>
  267 + );
  268 + this._descriptors.title = this._descriptors.title.set(route, titleDescriptor);
  269 + return titleDescriptor;
  270 + };
  271 +
  272 + _getRightButton = (route, index) => {
  273 + if (this._descriptors.right.has(route)) {
  274 + return this._descriptors.right.get(route);
  275 + }
  276 + var rightContent = this.props.routeMapper.rightContentForRoute(
  277 + this.props.navState.routeStack[index],
  278 + this.props.navigator
  279 + );
  280 + if (!rightContent) {
  281 + this._descriptors.right = this._descriptors.right.set(route, null);
  282 + return null;
  283 + }
  284 + var firstStyles = initStyle(index, navStatePresentedIndex(this.props.navState));
  285 + var rightButtonDescriptor = (
  286 + <View
  287 + key={'right_' + index}
  288 + ref={'right_' + index}
  289 + style={firstStyles.RightItem}>
  290 + {rightContent}
  291 + </View>
  292 + );
  293 + this._descriptors.right = this._descriptors.right.set(route, rightButtonDescriptor);
  294 + return rightButtonDescriptor;
  295 + };
  296 +
  297 + _setPropsIfExists(ref, props) {
  298 + var ref = this.refs[ref];
  299 + ref && ref.setNativeProps(props);
  300 + }
  301 +}
  302 +
  303 +const styles = StyleSheet.create({
  304 + breadCrumbContainer: {
  305 + overflow: 'hidden',
  306 + position: 'absolute',
  307 + height: NavigatorNavigationBarStyles.General.TotalNavHeight,
  308 + top: 0,
  309 + left: 0,
  310 + right: 0,
  311 + },
  312 +});
  313 +
  314 +module.exports = NavigatorBreadcrumbNavigationBar;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +var NavigatorNavigationBarStylesAndroid = require('./NavigatorNavigationBarStylesAndroid');
  29 +
  30 +var buildStyleInterpolator = require('./buildStyleInterpolator');
  31 +var merge = require('./merge');
  32 +
  33 +var NAV_BAR_HEIGHT = NavigatorNavigationBarStylesAndroid.General.NavBarHeight;
  34 +
  35 +var SPACING = 8;
  36 +var ICON_WIDTH = 40;
  37 +var SEPARATOR_WIDTH = 9;
  38 +var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH;
  39 +var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT;
  40 +
  41 +var OPACITY_RATIO = 100;
  42 +var ICON_INACTIVE_OPACITY = 0.6;
  43 +var MAX_BREADCRUMBS = 10;
  44 +
  45 +var CRUMB_BASE = {
  46 + position: 'absolute',
  47 + flexDirection: 'row',
  48 + top: 0,
  49 + width: CRUMB_WIDTH,
  50 + height: NAV_ELEMENT_HEIGHT,
  51 + backgroundColor: 'transparent',
  52 +};
  53 +
  54 +var ICON_BASE = {
  55 + width: ICON_WIDTH,
  56 + height: NAV_ELEMENT_HEIGHT,
  57 +};
  58 +
  59 +var SEPARATOR_BASE = {
  60 + width: SEPARATOR_WIDTH,
  61 + height: NAV_ELEMENT_HEIGHT,
  62 +};
  63 +
  64 +var TITLE_BASE = {
  65 + position: 'absolute',
  66 + top: 0,
  67 + height: NAV_ELEMENT_HEIGHT,
  68 + backgroundColor: 'transparent',
  69 + alignItems: 'flex-start',
  70 +};
  71 +
  72 +var FIRST_TITLE_BASE = merge(TITLE_BASE, {
  73 + left: 0,
  74 + right: 0,
  75 +});
  76 +
  77 +var RIGHT_BUTTON_BASE = {
  78 + position: 'absolute',
  79 + top: 0,
  80 + right: 0,
  81 + overflow: 'hidden',
  82 + opacity: 1,
  83 + height: NAV_ELEMENT_HEIGHT,
  84 + backgroundColor: 'transparent',
  85 +};
  86 +
  87 +/**
  88 + * Precompute crumb styles so that they don't need to be recomputed on every
  89 + * interaction.
  90 + */
  91 +var LEFT = [];
  92 +var CENTER = [];
  93 +var RIGHT = [];
  94 +for (var i = 0; i < MAX_BREADCRUMBS; i++) {
  95 + var crumbLeft = CRUMB_WIDTH * i + SPACING;
  96 + LEFT[i] = {
  97 + Crumb: merge(CRUMB_BASE, { left: crumbLeft }),
  98 + Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }),
  99 + Separator: merge(SEPARATOR_BASE, { opacity: 1 }),
  100 + Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }),
  101 + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }),
  102 + };
  103 + CENTER[i] = {
  104 + Crumb: merge(CRUMB_BASE, { left: crumbLeft }),
  105 + Icon: merge(ICON_BASE, { opacity: 1 }),
  106 + Separator: merge(SEPARATOR_BASE, { opacity: 0 }),
  107 + Title: merge(TITLE_BASE, {
  108 + left: crumbLeft + ICON_WIDTH,
  109 + opacity: 1,
  110 + }),
  111 + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }),
  112 + };
  113 + var crumbRight = crumbLeft + 50;
  114 + RIGHT[i] = {
  115 + Crumb: merge(CRUMB_BASE, { left: crumbRight}),
  116 + Icon: merge(ICON_BASE, { opacity: 0 }),
  117 + Separator: merge(SEPARATOR_BASE, { opacity: 0 }),
  118 + Title: merge(TITLE_BASE, {
  119 + left: crumbRight + ICON_WIDTH,
  120 + opacity: 0,
  121 + }),
  122 + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }),
  123 + };
  124 +}
  125 +
  126 +// Special case the CENTER state of the first scene.
  127 +CENTER[0] = {
  128 + Crumb: merge(CRUMB_BASE, {left: SPACING + CRUMB_WIDTH}),
  129 + Icon: merge(ICON_BASE, {opacity: 0}),
  130 + Separator: merge(SEPARATOR_BASE, {opacity: 0}),
  131 + Title: merge(FIRST_TITLE_BASE, {opacity: 1}),
  132 + RightItem: CENTER[0].RightItem,
  133 +};
  134 +LEFT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0});
  135 +RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0});
  136 +
  137 +
  138 +var buildIndexSceneInterpolator = function(startStyles, endStyles) {
  139 + return {
  140 + Crumb: buildStyleInterpolator({
  141 + left: {
  142 + type: 'linear',
  143 + from: startStyles.Crumb.left,
  144 + to: endStyles.Crumb.left,
  145 + min: 0,
  146 + max: 1,
  147 + extrapolate: true,
  148 + },
  149 + }),
  150 + Icon: buildStyleInterpolator({
  151 + opacity: {
  152 + type: 'linear',
  153 + from: startStyles.Icon.opacity,
  154 + to: endStyles.Icon.opacity,
  155 + min: 0,
  156 + max: 1,
  157 + },
  158 + }),
  159 + Separator: buildStyleInterpolator({
  160 + opacity: {
  161 + type: 'linear',
  162 + from: startStyles.Separator.opacity,
  163 + to: endStyles.Separator.opacity,
  164 + min: 0,
  165 + max: 1,
  166 + },
  167 + }),
  168 + Title: buildStyleInterpolator({
  169 + opacity: {
  170 + type: 'linear',
  171 + from: startStyles.Title.opacity,
  172 + to: endStyles.Title.opacity,
  173 + min: 0,
  174 + max: 1,
  175 + },
  176 + left: {
  177 + type: 'linear',
  178 + from: startStyles.Title.left,
  179 + to: endStyles.Title.left,
  180 + min: 0,
  181 + max: 1,
  182 + extrapolate: true,
  183 + },
  184 + }),
  185 + RightItem: buildStyleInterpolator({
  186 + opacity: {
  187 + type: 'linear',
  188 + from: startStyles.RightItem.opacity,
  189 + to: endStyles.RightItem.opacity,
  190 + min: 0,
  191 + max: 1,
  192 + round: OPACITY_RATIO,
  193 + },
  194 + }),
  195 + };
  196 +};
  197 +
  198 +var Interpolators = CENTER.map(function(_, ii) {
  199 + return {
  200 + // Animating *into* the center stage from the right
  201 + RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]),
  202 + // Animating out of the center stage, to the left
  203 + CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]),
  204 + // Both stages (animating *past* the center stage)
  205 + RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]),
  206 + };
  207 +});
  208 +
  209 +/**
  210 + * Contains constants that are used in constructing both `StyleSheet`s and
  211 + * inline styles during transitions.
  212 + */
  213 +module.exports = {
  214 + Interpolators,
  215 + Left: LEFT,
  216 + Center: CENTER,
  217 + Right: RIGHT,
  218 + IconWidth: ICON_WIDTH,
  219 + IconHeight: NAV_BAR_HEIGHT,
  220 + SeparatorWidth: SEPARATOR_WIDTH,
  221 + SeparatorHeight: NAV_BAR_HEIGHT,
  222 +};
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +import {Dimensions} from 'react-native';
  29 +var NavigatorNavigationBarStylesIOS = require('./NavigatorNavigationBarStylesIOS');
  30 +
  31 +var buildStyleInterpolator = require('./buildStyleInterpolator');
  32 +var merge = require('./merge');
  33 +
  34 +var SCREEN_WIDTH = Dimensions.get('window').width;
  35 +var STATUS_BAR_HEIGHT = NavigatorNavigationBarStylesIOS.General.StatusBarHeight;
  36 +var NAV_BAR_HEIGHT = NavigatorNavigationBarStylesIOS.General.NavBarHeight;
  37 +
  38 +var SPACING = 4;
  39 +var ICON_WIDTH = 40;
  40 +var SEPARATOR_WIDTH = 9;
  41 +var CRUMB_WIDTH = ICON_WIDTH + SEPARATOR_WIDTH;
  42 +
  43 +var OPACITY_RATIO = 100;
  44 +var ICON_INACTIVE_OPACITY = 0.6;
  45 +var MAX_BREADCRUMBS = 10;
  46 +
  47 +var CRUMB_BASE = {
  48 + position: 'absolute',
  49 + flexDirection: 'row',
  50 + top: STATUS_BAR_HEIGHT,
  51 + width: CRUMB_WIDTH,
  52 + height: NAV_BAR_HEIGHT,
  53 + backgroundColor: 'transparent',
  54 +};
  55 +
  56 +var ICON_BASE = {
  57 + width: ICON_WIDTH,
  58 + height: NAV_BAR_HEIGHT,
  59 +};
  60 +
  61 +var SEPARATOR_BASE = {
  62 + width: SEPARATOR_WIDTH,
  63 + height: NAV_BAR_HEIGHT,
  64 +};
  65 +
  66 +var TITLE_BASE = {
  67 + position: 'absolute',
  68 + top: STATUS_BAR_HEIGHT,
  69 + height: NAV_BAR_HEIGHT,
  70 + backgroundColor: 'transparent',
  71 +};
  72 +
  73 +// For first title styles, make sure first title is centered
  74 +var FIRST_TITLE_BASE = merge(TITLE_BASE, {
  75 + left: 0,
  76 + right: 0,
  77 + alignItems: 'center',
  78 + height: NAV_BAR_HEIGHT,
  79 +});
  80 +
  81 +var RIGHT_BUTTON_BASE = {
  82 + position: 'absolute',
  83 + top: STATUS_BAR_HEIGHT,
  84 + right: SPACING,
  85 + overflow: 'hidden',
  86 + opacity: 1,
  87 + height: NAV_BAR_HEIGHT,
  88 + backgroundColor: 'transparent',
  89 +};
  90 +
  91 +/**
  92 + * Precompute crumb styles so that they don't need to be recomputed on every
  93 + * interaction.
  94 + */
  95 +var LEFT = [];
  96 +var CENTER = [];
  97 +var RIGHT = [];
  98 +for (var i = 0; i < MAX_BREADCRUMBS; i++) {
  99 + var crumbLeft = CRUMB_WIDTH * i + SPACING;
  100 + LEFT[i] = {
  101 + Crumb: merge(CRUMB_BASE, { left: crumbLeft }),
  102 + Icon: merge(ICON_BASE, { opacity: ICON_INACTIVE_OPACITY }),
  103 + Separator: merge(SEPARATOR_BASE, { opacity: 1 }),
  104 + Title: merge(TITLE_BASE, { left: crumbLeft, opacity: 0 }),
  105 + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }),
  106 + };
  107 + CENTER[i] = {
  108 + Crumb: merge(CRUMB_BASE, { left: crumbLeft }),
  109 + Icon: merge(ICON_BASE, { opacity: 1 }),
  110 + Separator: merge(SEPARATOR_BASE, { opacity: 0 }),
  111 + Title: merge(TITLE_BASE, {
  112 + left: crumbLeft + ICON_WIDTH,
  113 + opacity: 1,
  114 + }),
  115 + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 1 }),
  116 + };
  117 + var crumbRight = SCREEN_WIDTH - 100;
  118 + RIGHT[i] = {
  119 + Crumb: merge(CRUMB_BASE, { left: crumbRight}),
  120 + Icon: merge(ICON_BASE, { opacity: 0 }),
  121 + Separator: merge(SEPARATOR_BASE, { opacity: 0 }),
  122 + Title: merge(TITLE_BASE, {
  123 + left: crumbRight + ICON_WIDTH,
  124 + opacity: 0,
  125 + }),
  126 + RightItem: merge(RIGHT_BUTTON_BASE, { opacity: 0 }),
  127 + };
  128 +}
  129 +
  130 +// Special case the CENTER state of the first scene.
  131 +CENTER[0] = {
  132 + Crumb: merge(CRUMB_BASE, {left: SCREEN_WIDTH / 4}),
  133 + Icon: merge(ICON_BASE, {opacity: 0}),
  134 + Separator: merge(SEPARATOR_BASE, {opacity: 0}),
  135 + Title: merge(FIRST_TITLE_BASE, {opacity: 1}),
  136 + RightItem: CENTER[0].RightItem,
  137 +};
  138 +LEFT[0].Title = merge(FIRST_TITLE_BASE, {left: -SCREEN_WIDTH / 4, opacity: 0});
  139 +RIGHT[0].Title = merge(FIRST_TITLE_BASE, {opacity: 0});
  140 +
  141 +
  142 +var buildIndexSceneInterpolator = function(startStyles, endStyles) {
  143 + return {
  144 + Crumb: buildStyleInterpolator({
  145 + left: {
  146 + type: 'linear',
  147 + from: startStyles.Crumb.left,
  148 + to: endStyles.Crumb.left,
  149 + min: 0,
  150 + max: 1,
  151 + extrapolate: true,
  152 + },
  153 + }),
  154 + Icon: buildStyleInterpolator({
  155 + opacity: {
  156 + type: 'linear',
  157 + from: startStyles.Icon.opacity,
  158 + to: endStyles.Icon.opacity,
  159 + min: 0,
  160 + max: 1,
  161 + },
  162 + }),
  163 + Separator: buildStyleInterpolator({
  164 + opacity: {
  165 + type: 'linear',
  166 + from: startStyles.Separator.opacity,
  167 + to: endStyles.Separator.opacity,
  168 + min: 0,
  169 + max: 1,
  170 + },
  171 + }),
  172 + Title: buildStyleInterpolator({
  173 + opacity: {
  174 + type: 'linear',
  175 + from: startStyles.Title.opacity,
  176 + to: endStyles.Title.opacity,
  177 + min: 0,
  178 + max: 1,
  179 + },
  180 + left: {
  181 + type: 'linear',
  182 + from: startStyles.Title.left,
  183 + to: endStyles.Title.left,
  184 + min: 0,
  185 + max: 1,
  186 + extrapolate: true,
  187 + },
  188 + }),
  189 + RightItem: buildStyleInterpolator({
  190 + opacity: {
  191 + type: 'linear',
  192 + from: startStyles.RightItem.opacity,
  193 + to: endStyles.RightItem.opacity,
  194 + min: 0,
  195 + max: 1,
  196 + round: OPACITY_RATIO,
  197 + },
  198 + }),
  199 + };
  200 +};
  201 +
  202 +var Interpolators = CENTER.map(function(_, ii) {
  203 + return {
  204 + // Animating *into* the center stage from the right
  205 + RightToCenter: buildIndexSceneInterpolator(RIGHT[ii], CENTER[ii]),
  206 + // Animating out of the center stage, to the left
  207 + CenterToLeft: buildIndexSceneInterpolator(CENTER[ii], LEFT[ii]),
  208 + // Both stages (animating *past* the center stage)
  209 + RightToLeft: buildIndexSceneInterpolator(RIGHT[ii], LEFT[ii]),
  210 + };
  211 +});
  212 +
  213 +/**
  214 + * Contains constants that are used in constructing both `StyleSheet`s and
  215 + * inline styles during transitions.
  216 + */
  217 +module.exports = {
  218 + Interpolators,
  219 + Left: LEFT,
  220 + Center: CENTER,
  221 + Right: RIGHT,
  222 + IconWidth: ICON_WIDTH,
  223 + IconHeight: NAV_BAR_HEIGHT,
  224 + SeparatorWidth: SEPARATOR_WIDTH,
  225 + SeparatorHeight: NAV_BAR_HEIGHT,
  226 +};
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +var React = require('react');
  29 +var NavigatorNavigationBarStylesAndroid = require('./NavigatorNavigationBarStylesAndroid');
  30 +var NavigatorNavigationBarStylesIOS = require('./NavigatorNavigationBarStylesIOS');
  31 +import {
  32 + Platform,
  33 + StyleSheet,
  34 + View,
  35 + ViewPropTypes,
  36 +} from 'react-native';
  37 +var PropTypes = require('prop-types');
  38 +
  39 +var guid = require('./guid');
  40 +
  41 +var { Map } = require('immutable');
  42 +
  43 +var COMPONENT_NAMES = ['Title', 'LeftButton', 'RightButton'];
  44 +
  45 +var NavigatorNavigationBarStyles = Platform.OS === 'android' ?
  46 + NavigatorNavigationBarStylesAndroid : NavigatorNavigationBarStylesIOS;
  47 +
  48 +var navStatePresentedIndex = function(navState) {
  49 + if (navState.presentedIndex !== undefined) {
  50 + return navState.presentedIndex;
  51 + }
  52 + // TODO: rename `observedTopOfStack` to `presentedIndex` in `NavigatorIOS`
  53 + return navState.observedTopOfStack;
  54 +};
  55 +
  56 +class NavigatorNavigationBar extends React.Component {
  57 + static propTypes = {
  58 + navigator: PropTypes.object,
  59 + routeMapper: PropTypes.shape({
  60 + Title: PropTypes.func.isRequired,
  61 + LeftButton: PropTypes.func.isRequired,
  62 + RightButton: PropTypes.func.isRequired,
  63 + }).isRequired,
  64 + navState: PropTypes.shape({
  65 + routeStack: PropTypes.arrayOf(PropTypes.object),
  66 + presentedIndex: PropTypes.number,
  67 + }),
  68 + navigationStyles: PropTypes.object,
  69 + style: ViewPropTypes.style,
  70 + };
  71 +
  72 + static Styles = NavigatorNavigationBarStyles;
  73 + static StylesAndroid = NavigatorNavigationBarStylesAndroid;
  74 + static StylesIOS = NavigatorNavigationBarStylesIOS;
  75 +
  76 + static defaultProps = {
  77 + navigationStyles: NavigatorNavigationBarStyles,
  78 + };
  79 +
  80 + componentWillMount() {
  81 + this._reset();
  82 + }
  83 +
  84 + /**
  85 + * Stop transtion, immediately resets the cached state and re-render the
  86 + * whole view.
  87 + */
  88 + immediatelyRefresh = () => {
  89 + this._reset();
  90 + this.forceUpdate();
  91 + };
  92 +
  93 + _reset = () => {
  94 + this._key = guid();
  95 + this._reusableProps = {};
  96 + this._components = {};
  97 + this._descriptors = {};
  98 +
  99 + COMPONENT_NAMES.forEach(componentName => {
  100 + this._components[componentName] = new Map();
  101 + this._descriptors[componentName] = new Map();
  102 + });
  103 + };
  104 +
  105 + _getReusableProps = (/*string*/componentName, /*number*/index) => /*object*/ {
  106 + var propStack = this._reusableProps[componentName];
  107 + if (!propStack) {
  108 + propStack = this._reusableProps[componentName] = [];
  109 + }
  110 + var props = propStack[index];
  111 + if (!props) {
  112 + props = propStack[index] = {style:{}};
  113 + }
  114 + return props;
  115 + };
  116 +
  117 + _updateIndexProgress = (
  118 + /*number*/progress,
  119 + /*number*/index,
  120 + /*number*/fromIndex,
  121 + /*number*/toIndex,
  122 + ) => {
  123 + var amount = toIndex > fromIndex ? progress : (1 - progress);
  124 + var oldDistToCenter = index - fromIndex;
  125 + var newDistToCenter = index - toIndex;
  126 + var interpolate;
  127 + if (oldDistToCenter > 0 && newDistToCenter === 0 ||
  128 + newDistToCenter > 0 && oldDistToCenter === 0) {
  129 + interpolate = this.props.navigationStyles.Interpolators.RightToCenter;
  130 + } else if (oldDistToCenter < 0 && newDistToCenter === 0 ||
  131 + newDistToCenter < 0 && oldDistToCenter === 0) {
  132 + interpolate = this.props.navigationStyles.Interpolators.CenterToLeft;
  133 + } else if (oldDistToCenter === newDistToCenter) {
  134 + interpolate = this.props.navigationStyles.Interpolators.RightToCenter;
  135 + } else {
  136 + interpolate = this.props.navigationStyles.Interpolators.RightToLeft;
  137 + }
  138 +
  139 + COMPONENT_NAMES.forEach(function (componentName) {
  140 + var component = this._components[componentName].get(this.props.navState.routeStack[index]);
  141 + var props = this._getReusableProps(componentName, index);
  142 + if (component && interpolate[componentName](props.style, amount)) {
  143 + props.pointerEvents = props.style.opacity === 0 ? 'none' : 'box-none';
  144 + component.setNativeProps(props);
  145 + }
  146 + }, this);
  147 + };
  148 +
  149 + updateProgress = (/*number*/progress, /*number*/fromIndex, /*number*/toIndex) => {
  150 + var max = Math.max(fromIndex, toIndex);
  151 + var min = Math.min(fromIndex, toIndex);
  152 + for (var index = min; index <= max; index++) {
  153 + this._updateIndexProgress(progress, index, fromIndex, toIndex);
  154 + }
  155 + };
  156 +
  157 + render() {
  158 + var navBarStyle = {
  159 + height: this.props.navigationStyles.General.TotalNavHeight,
  160 + };
  161 + var navState = this.props.navState;
  162 + var components = navState.routeStack.map((route, index) =>
  163 + COMPONENT_NAMES.map(componentName =>
  164 + this._getComponent(componentName, route, index)
  165 + )
  166 + );
  167 +
  168 + return (
  169 + <View
  170 + key={this._key}
  171 + style={[styles.navBarContainer, navBarStyle, this.props.style]}>
  172 + {components}
  173 + </View>
  174 + );
  175 + }
  176 +
  177 + _getComponent = (/*string*/componentName, /*object*/route, /*number*/index) => /*?Object*/ {
  178 + if (this._descriptors[componentName].includes(route)) {
  179 + return this._descriptors[componentName].get(route);
  180 + }
  181 +
  182 + var rendered = null;
  183 +
  184 + var content = this.props.routeMapper[componentName](
  185 + this.props.navState.routeStack[index],
  186 + this.props.navigator,
  187 + index,
  188 + this.props.navState
  189 + );
  190 + if (!content) {
  191 + return null;
  192 + }
  193 +
  194 + var componentIsActive = index === navStatePresentedIndex(this.props.navState);
  195 + var initialStage = componentIsActive ?
  196 + this.props.navigationStyles.Stages.Center :
  197 + this.props.navigationStyles.Stages.Left;
  198 + rendered = (
  199 + <View
  200 + ref={(ref) => {
  201 + this._components[componentName] = this._components[componentName].set(route, ref);
  202 + }}
  203 + pointerEvents={componentIsActive ? 'box-none' : 'none'}
  204 + style={initialStage[componentName]}>
  205 + {content}
  206 + </View>
  207 + );
  208 +
  209 + this._descriptors[componentName] = this._descriptors[componentName].set(route, rendered);
  210 + return rendered;
  211 + };
  212 +}
  213 +
  214 +
  215 +var styles = StyleSheet.create({
  216 + navBarContainer: {
  217 + position: 'absolute',
  218 + top: 0,
  219 + left: 0,
  220 + right: 0,
  221 + backgroundColor: 'transparent',
  222 + },
  223 +});
  224 +
  225 +module.exports = NavigatorNavigationBar;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +var buildStyleInterpolator = require('./buildStyleInterpolator');
  29 +var merge = require('./merge');
  30 +
  31 +// Android Material Design
  32 +var NAV_BAR_HEIGHT = 56;
  33 +var TITLE_LEFT = 72;
  34 +var BUTTON_SIZE = 24;
  35 +var TOUCH_TARGT_SIZE = 48;
  36 +var BUTTON_HORIZONTAL_MARGIN = 16;
  37 +
  38 +var BUTTON_EFFECTIVE_MARGIN = BUTTON_HORIZONTAL_MARGIN - (TOUCH_TARGT_SIZE - BUTTON_SIZE) / 2;
  39 +var NAV_ELEMENT_HEIGHT = NAV_BAR_HEIGHT;
  40 +
  41 +var BASE_STYLES = {
  42 + Title: {
  43 + position: 'absolute',
  44 + bottom: 0,
  45 + left: 0,
  46 + right: 0,
  47 + alignItems: 'flex-start',
  48 + height: NAV_ELEMENT_HEIGHT,
  49 + backgroundColor: 'transparent',
  50 + marginLeft: TITLE_LEFT,
  51 + },
  52 + LeftButton: {
  53 + position: 'absolute',
  54 + top: 0,
  55 + left: BUTTON_EFFECTIVE_MARGIN,
  56 + overflow: 'hidden',
  57 + height: NAV_ELEMENT_HEIGHT,
  58 + backgroundColor: 'transparent',
  59 + },
  60 + RightButton: {
  61 + position: 'absolute',
  62 + top: 0,
  63 + right: BUTTON_EFFECTIVE_MARGIN,
  64 + overflow: 'hidden',
  65 + alignItems: 'flex-end',
  66 + height: NAV_ELEMENT_HEIGHT,
  67 + backgroundColor: 'transparent',
  68 + },
  69 +};
  70 +
  71 +// There are 3 stages: left, center, right. All previous navigation
  72 +// items are in the left stage. The current navigation item is in the
  73 +// center stage. All upcoming navigation items are in the right stage.
  74 +// Another way to think of the stages is in terms of transitions. When
  75 +// we move forward in the navigation stack, we perform a
  76 +// right-to-center transition on the new navigation item and a
  77 +// center-to-left transition on the current navigation item.
  78 +var Stages = {
  79 + Left: {
  80 + Title: merge(BASE_STYLES.Title, { opacity: 0 }),
  81 + LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }),
  82 + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }),
  83 + },
  84 + Center: {
  85 + Title: merge(BASE_STYLES.Title, { opacity: 1 }),
  86 + LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 1 }),
  87 + RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }),
  88 + },
  89 + Right: {
  90 + Title: merge(BASE_STYLES.Title, { opacity: 0 }),
  91 + LeftButton: merge(BASE_STYLES.LeftButton, { opacity: 0 }),
  92 + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }),
  93 + },
  94 +};
  95 +
  96 +
  97 +var opacityRatio = 100;
  98 +
  99 +function buildSceneInterpolators(startStyles, endStyles) {
  100 + return {
  101 + Title: buildStyleInterpolator({
  102 + opacity: {
  103 + type: 'linear',
  104 + from: startStyles.Title.opacity,
  105 + to: endStyles.Title.opacity,
  106 + min: 0,
  107 + max: 1,
  108 + },
  109 + left: {
  110 + type: 'linear',
  111 + from: startStyles.Title.left,
  112 + to: endStyles.Title.left,
  113 + min: 0,
  114 + max: 1,
  115 + extrapolate: true,
  116 + },
  117 + }),
  118 + LeftButton: buildStyleInterpolator({
  119 + opacity: {
  120 + type: 'linear',
  121 + from: startStyles.LeftButton.opacity,
  122 + to: endStyles.LeftButton.opacity,
  123 + min: 0,
  124 + max: 1,
  125 + round: opacityRatio,
  126 + },
  127 + left: {
  128 + type: 'linear',
  129 + from: startStyles.LeftButton.left,
  130 + to: endStyles.LeftButton.left,
  131 + min: 0,
  132 + max: 1,
  133 + },
  134 + }),
  135 + RightButton: buildStyleInterpolator({
  136 + opacity: {
  137 + type: 'linear',
  138 + from: startStyles.RightButton.opacity,
  139 + to: endStyles.RightButton.opacity,
  140 + min: 0,
  141 + max: 1,
  142 + round: opacityRatio,
  143 + },
  144 + left: {
  145 + type: 'linear',
  146 + from: startStyles.RightButton.left,
  147 + to: endStyles.RightButton.left,
  148 + min: 0,
  149 + max: 1,
  150 + extrapolate: true,
  151 + },
  152 + }),
  153 + };
  154 +}
  155 +
  156 +var Interpolators = {
  157 + // Animating *into* the center stage from the right
  158 + RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center),
  159 + // Animating out of the center stage, to the left
  160 + CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left),
  161 + // Both stages (animating *past* the center stage)
  162 + RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left),
  163 +};
  164 +
  165 +
  166 +module.exports = {
  167 + General: {
  168 + NavBarHeight: NAV_BAR_HEIGHT,
  169 + StatusBarHeight: 0,
  170 + TotalNavHeight: NAV_BAR_HEIGHT,
  171 + },
  172 + Interpolators,
  173 + Stages,
  174 +};
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +import {
  29 + Dimensions,
  30 + I18nManager,
  31 + PixelRatio,
  32 +} from 'react-native';
  33 +
  34 +var buildStyleInterpolator = require('./buildStyleInterpolator');
  35 +var merge = require('./merge');
  36 +
  37 +var SCREEN_WIDTH = Dimensions.get('window').width;
  38 +var NAV_BAR_HEIGHT = 44;
  39 +var STATUS_BAR_HEIGHT = 20;
  40 +var NAV_HEIGHT = NAV_BAR_HEIGHT + STATUS_BAR_HEIGHT;
  41 +
  42 +var BASE_STYLES = {
  43 + Title: {
  44 + position: 'absolute',
  45 + top: STATUS_BAR_HEIGHT,
  46 + left: 0,
  47 + right: 0,
  48 + alignItems: 'center',
  49 + height: NAV_BAR_HEIGHT,
  50 + backgroundColor: 'transparent',
  51 + },
  52 + LeftButton: {
  53 + position: 'absolute',
  54 + top: STATUS_BAR_HEIGHT,
  55 + left: 0,
  56 + overflow: 'hidden',
  57 + opacity: 1,
  58 + height: NAV_BAR_HEIGHT,
  59 + backgroundColor: 'transparent',
  60 + },
  61 + RightButton: {
  62 + position: 'absolute',
  63 + top: STATUS_BAR_HEIGHT,
  64 + right: 0,
  65 + overflow: 'hidden',
  66 + opacity: 1,
  67 + alignItems: 'flex-end',
  68 + height: NAV_BAR_HEIGHT,
  69 + backgroundColor: 'transparent',
  70 + },
  71 +};
  72 +
  73 +// There are 3 stages: left, center, right. All previous navigation
  74 +// items are in the left stage. The current navigation item is in the
  75 +// center stage. All upcoming navigation items are in the right stage.
  76 +// Another way to think of the stages is in terms of transitions. When
  77 +// we move forward in the navigation stack, we perform a
  78 +// right-to-center transition on the new navigation item and a
  79 +// center-to-left transition on the current navigation item.
  80 +var Stages = {
  81 + Left: {
  82 + Title: merge(BASE_STYLES.Title, { left: -SCREEN_WIDTH / 2, opacity: 0 }),
  83 + LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 0 }),
  84 + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }),
  85 + },
  86 + Center: {
  87 + Title: merge(BASE_STYLES.Title, { left: 0, opacity: 1 }),
  88 + LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 1 }),
  89 + RightButton: merge(BASE_STYLES.RightButton, { opacity: 1 }),
  90 + },
  91 + Right: {
  92 + Title: merge(BASE_STYLES.Title, { left: SCREEN_WIDTH / 2, opacity: 0 }),
  93 + LeftButton: merge(BASE_STYLES.LeftButton, { left: 0, opacity: 0 }),
  94 + RightButton: merge(BASE_STYLES.RightButton, { opacity: 0 }),
  95 + },
  96 +};
  97 +
  98 +
  99 +var opacityRatio = 100;
  100 +
  101 +function buildSceneInterpolators(startStyles, endStyles) {
  102 + return {
  103 + Title: buildStyleInterpolator({
  104 + opacity: {
  105 + type: 'linear',
  106 + from: startStyles.Title.opacity,
  107 + to: endStyles.Title.opacity,
  108 + min: 0,
  109 + max: 1,
  110 + },
  111 + left: {
  112 + type: 'linear',
  113 + from: startStyles.Title.left,
  114 + to: endStyles.Title.left,
  115 + min: 0,
  116 + max: 1,
  117 + extrapolate: true,
  118 + },
  119 + }),
  120 + LeftButton: buildStyleInterpolator({
  121 + opacity: {
  122 + type: 'linear',
  123 + from: startStyles.LeftButton.opacity,
  124 + to: endStyles.LeftButton.opacity,
  125 + min: 0,
  126 + max: 1,
  127 + round: opacityRatio,
  128 + },
  129 + left: {
  130 + type: 'linear',
  131 + from: startStyles.LeftButton.left,
  132 + to: endStyles.LeftButton.left,
  133 + min: 0,
  134 + max: 1,
  135 + },
  136 + }),
  137 + RightButton: buildStyleInterpolator({
  138 + opacity: {
  139 + type: 'linear',
  140 + from: startStyles.RightButton.opacity,
  141 + to: endStyles.RightButton.opacity,
  142 + min: 0,
  143 + max: 1,
  144 + round: opacityRatio,
  145 + },
  146 + left: {
  147 + type: 'linear',
  148 + from: startStyles.RightButton.left,
  149 + to: endStyles.RightButton.left,
  150 + min: 0,
  151 + max: 1,
  152 + extrapolate: true,
  153 + },
  154 + }),
  155 + };
  156 +}
  157 +
  158 +var Interpolators = {
  159 + // Animating *into* the center stage from the right
  160 + RightToCenter: buildSceneInterpolators(Stages.Right, Stages.Center),
  161 + // Animating out of the center stage, to the left
  162 + CenterToLeft: buildSceneInterpolators(Stages.Center, Stages.Left),
  163 + // Both stages (animating *past* the center stage)
  164 + RightToLeft: buildSceneInterpolators(Stages.Right, Stages.Left),
  165 +};
  166 +
  167 +
  168 +module.exports = {
  169 + General: {
  170 + NavBarHeight: NAV_BAR_HEIGHT,
  171 + StatusBarHeight: STATUS_BAR_HEIGHT,
  172 + TotalNavHeight: NAV_HEIGHT,
  173 + },
  174 + Interpolators,
  175 + Stages,
  176 +};
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +'use strict';
  27 +
  28 +import {
  29 + Dimensions,
  30 + I18nManager,
  31 + PixelRatio,
  32 +} from 'react-native';
  33 +
  34 +var buildStyleInterpolator = require('./buildStyleInterpolator');
  35 +
  36 +var IS_RTL = I18nManager.isRTL;
  37 +
  38 +var SCREEN_WIDTH = Dimensions.get('window').width;
  39 +var SCREEN_HEIGHT = Dimensions.get('window').height;
  40 +var PIXEL_RATIO = PixelRatio.get();
  41 +
  42 +var ToTheLeftIOS = {
  43 + transformTranslate: {
  44 + from: {x: 0, y: 0, z: 0},
  45 + to: {x: -SCREEN_WIDTH * 0.3, y: 0, z: 0},
  46 + min: 0,
  47 + max: 1,
  48 + type: 'linear',
  49 + extrapolate: true,
  50 + round: PIXEL_RATIO,
  51 + },
  52 + opacity: {
  53 + value: 1.0,
  54 + type: 'constant',
  55 + },
  56 +};
  57 +
  58 +var ToTheRightIOS = {
  59 + ...ToTheLeftIOS,
  60 + transformTranslate: {
  61 + from: {x: 0, y: 0, z: 0},
  62 + to: {x: SCREEN_WIDTH * 0.3, y: 0, z: 0},
  63 + },
  64 +};
  65 +
  66 +var FadeToTheLeft = {
  67 + // Rotate *requires* you to break out each individual component of
  68 + // rotation (x, y, z, w)
  69 + transformTranslate: {
  70 + from: {x: 0, y: 0, z: 0},
  71 + to: {x: -Math.round(SCREEN_WIDTH * 0.3), y: 0, z: 0},
  72 + min: 0,
  73 + max: 1,
  74 + type: 'linear',
  75 + extrapolate: true,
  76 + round: PIXEL_RATIO,
  77 + },
  78 + // Uncomment to try rotation:
  79 + // Quick guide to reasoning about rotations:
  80 + // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/#Quaternions
  81 + // transformRotateRadians: {
  82 + // from: {x: 0, y: 0, z: 0, w: 1},
  83 + // to: {x: 0, y: 0, z: -0.47, w: 0.87},
  84 + // min: 0,
  85 + // max: 1,
  86 + // type: 'linear',
  87 + // extrapolate: true
  88 + // },
  89 + transformScale: {
  90 + from: {x: 1, y: 1, z: 1},
  91 + to: {x: 0.95, y: 0.95, z: 1},
  92 + min: 0,
  93 + max: 1,
  94 + type: 'linear',
  95 + extrapolate: true
  96 + },
  97 + opacity: {
  98 + from: 1,
  99 + to: 0.3,
  100 + min: 0,
  101 + max: 1,
  102 + type: 'linear',
  103 + extrapolate: false,
  104 + round: 100,
  105 + },
  106 + translateX: {
  107 + from: 0,
  108 + to: -Math.round(SCREEN_WIDTH * 0.3),
  109 + min: 0,
  110 + max: 1,
  111 + type: 'linear',
  112 + extrapolate: true,
  113 + round: PIXEL_RATIO,
  114 + },
  115 + scaleX: {
  116 + from: 1,
  117 + to: 0.95,
  118 + min: 0,
  119 + max: 1,
  120 + type: 'linear',
  121 + extrapolate: true
  122 + },
  123 + scaleY: {
  124 + from: 1,
  125 + to: 0.95,
  126 + min: 0,
  127 + max: 1,
  128 + type: 'linear',
  129 + extrapolate: true
  130 + },
  131 +};
  132 +
  133 +var FadeToTheRight = {
  134 + ...FadeToTheLeft,
  135 + transformTranslate: {
  136 + from: {x: 0, y: 0, z: 0},
  137 + to: {x: Math.round(SCREEN_WIDTH * 0.3), y: 0, z: 0},
  138 + },
  139 + translateX: {
  140 + from: 0,
  141 + to: Math.round(SCREEN_WIDTH * 0.3),
  142 + },
  143 +};
  144 +
  145 +var FadeIn = {
  146 + opacity: {
  147 + from: 0,
  148 + to: 1,
  149 + min: 0.5,
  150 + max: 1,
  151 + type: 'linear',
  152 + extrapolate: false,
  153 + round: 100,
  154 + },
  155 +};
  156 +
  157 +var FadeOut = {
  158 + opacity: {
  159 + from: 1,
  160 + to: 0,
  161 + min: 0,
  162 + max: 0.5,
  163 + type: 'linear',
  164 + extrapolate: false,
  165 + round: 100,
  166 + },
  167 +};
  168 +
  169 +var ToTheLeft = {
  170 + transformTranslate: {
  171 + from: {x: 0, y: 0, z: 0},
  172 + to: {x: -SCREEN_WIDTH, y: 0, z: 0},
  173 + min: 0,
  174 + max: 1,
  175 + type: 'linear',
  176 + extrapolate: true,
  177 + round: PIXEL_RATIO,
  178 + },
  179 + opacity: {
  180 + value: 1.0,
  181 + type: 'constant',
  182 + },
  183 +
  184 + translateX: {
  185 + from: 0,
  186 + to: -SCREEN_WIDTH,
  187 + min: 0,
  188 + max: 1,
  189 + type: 'linear',
  190 + extrapolate: true,
  191 + round: PIXEL_RATIO,
  192 + },
  193 +};
  194 +
  195 +var ToTheRight = {
  196 + transformTranslate: {
  197 + from: {x: 0, y: 0, z: 0},
  198 + to: {x: SCREEN_WIDTH, y: 0, z: 0},
  199 + min: 0,
  200 + max: 1,
  201 + type: 'linear',
  202 + extrapolate: true,
  203 + round: PIXEL_RATIO,
  204 + },
  205 + opacity: {
  206 + value: 1.0,
  207 + type: 'constant',
  208 + },
  209 +
  210 + translateX: {
  211 + from: 0,
  212 + to: SCREEN_WIDTH,
  213 + min: 0,
  214 + max: 1,
  215 + type: 'linear',
  216 + extrapolate: true,
  217 + round: PIXEL_RATIO,
  218 + },
  219 +};
  220 +
  221 +var ToTheUp = {
  222 + transformTranslate: {
  223 + from: {x: 0, y: 0, z: 0},
  224 + to: {x: 0, y: -SCREEN_HEIGHT, z: 0},
  225 + min: 0,
  226 + max: 1,
  227 + type: 'linear',
  228 + extrapolate: true,
  229 + round: PIXEL_RATIO,
  230 + },
  231 + opacity: {
  232 + value: 1.0,
  233 + type: 'constant',
  234 + },
  235 + translateY: {
  236 + from: 0,
  237 + to: -SCREEN_HEIGHT,
  238 + min: 0,
  239 + max: 1,
  240 + type: 'linear',
  241 + extrapolate: true,
  242 + round: PIXEL_RATIO,
  243 + },
  244 +};
  245 +
  246 +var ToTheDown = {
  247 + transformTranslate: {
  248 + from: {x: 0, y: 0, z: 0},
  249 + to: {x: 0, y: SCREEN_HEIGHT, z: 0},
  250 + min: 0,
  251 + max: 1,
  252 + type: 'linear',
  253 + extrapolate: true,
  254 + round: PIXEL_RATIO,
  255 + },
  256 + opacity: {
  257 + value: 1.0,
  258 + type: 'constant',
  259 + },
  260 + translateY: {
  261 + from: 0,
  262 + to: SCREEN_HEIGHT,
  263 + min: 0,
  264 + max: 1,
  265 + type: 'linear',
  266 + extrapolate: true,
  267 + round: PIXEL_RATIO,
  268 + },
  269 +};
  270 +
  271 +var FromTheRight = {
  272 + opacity: {
  273 + value: 1.0,
  274 + type: 'constant',
  275 + },
  276 +
  277 + transformTranslate: {
  278 + from: {x: SCREEN_WIDTH, y: 0, z: 0},
  279 + to: {x: 0, y: 0, z: 0},
  280 + min: 0,
  281 + max: 1,
  282 + type: 'linear',
  283 + extrapolate: true,
  284 + round: PIXEL_RATIO,
  285 + },
  286 +
  287 + translateX: {
  288 + from: SCREEN_WIDTH,
  289 + to: 0,
  290 + min: 0,
  291 + max: 1,
  292 + type: 'linear',
  293 + extrapolate: true,
  294 + round: PIXEL_RATIO,
  295 + },
  296 +
  297 + scaleX: {
  298 + value: 1,
  299 + type: 'constant',
  300 + },
  301 + scaleY: {
  302 + value: 1,
  303 + type: 'constant',
  304 + },
  305 +};
  306 +
  307 +var FromTheLeft = {
  308 + ...FromTheRight,
  309 + transformTranslate: {
  310 + from: {x: -SCREEN_WIDTH, y: 0, z: 0},
  311 + to: {x: 0, y: 0, z: 0},
  312 + min: 0,
  313 + max: 1,
  314 + type: 'linear',
  315 + extrapolate: true,
  316 + round: PIXEL_RATIO,
  317 + },
  318 + translateX: {
  319 + from: -SCREEN_WIDTH,
  320 + to: 0,
  321 + min: 0,
  322 + max: 1,
  323 + type: 'linear',
  324 + extrapolate: true,
  325 + round: PIXEL_RATIO,
  326 + },
  327 +};
  328 +
  329 +var FromTheDown = {
  330 + ...FromTheRight,
  331 + transformTranslate: {
  332 + from: {y: SCREEN_HEIGHT, x: 0, z: 0},
  333 + to: {x: 0, y: 0, z: 0},
  334 + min: 0,
  335 + max: 1,
  336 + type: 'linear',
  337 + extrapolate: true,
  338 + round: PIXEL_RATIO,
  339 + },
  340 + translateY: {
  341 + from: SCREEN_HEIGHT,
  342 + to: 0,
  343 + min: 0,
  344 + max: 1,
  345 + type: 'linear',
  346 + extrapolate: true,
  347 + round: PIXEL_RATIO,
  348 + },
  349 +};
  350 +
  351 +var FromTheTop = {
  352 + ...FromTheRight,
  353 + transformTranslate: {
  354 + from: {y: -SCREEN_HEIGHT, x: 0, z: 0},
  355 + to: {x: 0, y: 0, z: 0},
  356 + min: 0,
  357 + max: 1,
  358 + type: 'linear',
  359 + extrapolate: true,
  360 + round: PIXEL_RATIO,
  361 + },
  362 + translateY: {
  363 + from: -SCREEN_HEIGHT,
  364 + to: 0,
  365 + min: 0,
  366 + max: 1,
  367 + type: 'linear',
  368 + extrapolate: true,
  369 + round: PIXEL_RATIO,
  370 + },
  371 +};
  372 +
  373 +var ToTheBack = {
  374 + // Rotate *requires* you to break out each individual component of
  375 + // rotation (x, y, z, w)
  376 + transformTranslate: {
  377 + from: {x: 0, y: 0, z: 0},
  378 + to: {x: 0, y: 0, z: 0},
  379 + min: 0,
  380 + max: 1,
  381 + type: 'linear',
  382 + extrapolate: true,
  383 + round: PIXEL_RATIO,
  384 + },
  385 + transformScale: {
  386 + from: {x: 1, y: 1, z: 1},
  387 + to: {x: 0.95, y: 0.95, z: 1},
  388 + min: 0,
  389 + max: 1,
  390 + type: 'linear',
  391 + extrapolate: true
  392 + },
  393 + opacity: {
  394 + from: 1,
  395 + to: 0.3,
  396 + min: 0,
  397 + max: 1,
  398 + type: 'linear',
  399 + extrapolate: false,
  400 + round: 100,
  401 + },
  402 + scaleX: {
  403 + from: 1,
  404 + to: 0.95,
  405 + min: 0,
  406 + max: 1,
  407 + type: 'linear',
  408 + extrapolate: true
  409 + },
  410 + scaleY: {
  411 + from: 1,
  412 + to: 0.95,
  413 + min: 0,
  414 + max: 1,
  415 + type: 'linear',
  416 + extrapolate: true
  417 + },
  418 +};
  419 +
  420 +var FromTheFront = {
  421 + opacity: {
  422 + value: 1.0,
  423 + type: 'constant',
  424 + },
  425 +
  426 + transformTranslate: {
  427 + from: {x: 0, y: SCREEN_HEIGHT, z: 0},
  428 + to: {x: 0, y: 0, z: 0},
  429 + min: 0,
  430 + max: 1,
  431 + type: 'linear',
  432 + extrapolate: true,
  433 + round: PIXEL_RATIO,
  434 + },
  435 + translateY: {
  436 + from: SCREEN_HEIGHT,
  437 + to: 0,
  438 + min: 0,
  439 + max: 1,
  440 + type: 'linear',
  441 + extrapolate: true,
  442 + round: PIXEL_RATIO,
  443 + },
  444 + scaleX: {
  445 + value: 1,
  446 + type: 'constant',
  447 + },
  448 + scaleY: {
  449 + value: 1,
  450 + type: 'constant',
  451 + },
  452 +};
  453 +
  454 +var ToTheBackAndroid = {
  455 + opacity: {
  456 + value: 1,
  457 + type: 'constant',
  458 + },
  459 +};
  460 +
  461 +var FromTheFrontAndroid = {
  462 + opacity: {
  463 + from: 0,
  464 + to: 1,
  465 + min: 0.5,
  466 + max: 1,
  467 + type: 'linear',
  468 + extrapolate: false,
  469 + round: 100,
  470 + },
  471 + transformTranslate: {
  472 + from: {x: 0, y: 100, z: 0},
  473 + to: {x: 0, y: 0, z: 0},
  474 + min: 0,
  475 + max: 1,
  476 + type: 'linear',
  477 + extrapolate: true,
  478 + round: PIXEL_RATIO,
  479 + },
  480 + translateY: {
  481 + from: 100,
  482 + to: 0,
  483 + min: 0,
  484 + max: 1,
  485 + type: 'linear',
  486 + extrapolate: true,
  487 + round: PIXEL_RATIO,
  488 + },
  489 +};
  490 +
  491 +var BaseOverswipeConfig = {
  492 + frictionConstant: 1,
  493 + frictionByDistance: 1.5,
  494 +};
  495 +
  496 +var BaseLeftToRightGesture = {
  497 +
  498 + // If the gesture can end and restart during one continuous touch
  499 + isDetachable: false,
  500 +
  501 + // How far the swipe must drag to start transitioning
  502 + gestureDetectMovement: 2,
  503 +
  504 + // Amplitude of release velocity that is considered still
  505 + notMoving: 0.3,
  506 +
  507 + // Fraction of directional move required.
  508 + directionRatio: 0.66,
  509 +
  510 + // Velocity to transition with when the gesture release was "not moving"
  511 + snapVelocity: 2,
  512 +
  513 + // Region that can trigger swipe. iOS default is 30px from the left edge
  514 + edgeHitWidth: 30,
  515 +
  516 + // Ratio of gesture completion when non-velocity release will cause action
  517 + stillCompletionRatio: 3 / 5,
  518 +
  519 + fullDistance: SCREEN_WIDTH,
  520 +
  521 + direction: 'left-to-right',
  522 +
  523 +};
  524 +
  525 +var BaseRightToLeftGesture = {
  526 + ...BaseLeftToRightGesture,
  527 + direction: 'right-to-left',
  528 +};
  529 +
  530 +var BaseDownUpGesture = {
  531 + ...BaseLeftToRightGesture,
  532 + fullDistance: SCREEN_HEIGHT,
  533 + direction: 'bottom-to-top',
  534 +};
  535 +
  536 +var BaseUpDownGesture = {
  537 + ...BaseLeftToRightGesture,
  538 + fullDistance: SCREEN_HEIGHT,
  539 + direction: 'top-to-bottom',
  540 +};
  541 +
  542 +// For RTL experiment, we need to swap all the Left and Right gesture and animation.
  543 +// So we create a direction mapping for both LTR and RTL, and change left/right to start/end.
  544 +let directionMapping = {
  545 + ToTheStartIOS: ToTheLeftIOS,
  546 + ToTheEndIOS: ToTheRightIOS,
  547 + FadeToTheStart: FadeToTheLeft,
  548 + FadeToTheEnd: FadeToTheRight,
  549 + ToTheStart: ToTheLeft,
  550 + ToTheEnd: ToTheRight,
  551 + FromTheStart: FromTheLeft,
  552 + FromTheEnd: FromTheRight,
  553 + BaseStartToEndGesture: BaseLeftToRightGesture,
  554 + BaseEndToStartGesture: BaseRightToLeftGesture,
  555 +};
  556 +
  557 +if (IS_RTL) {
  558 + directionMapping = {
  559 + ToTheStartIOS: ToTheRightIOS,
  560 + ToTheEndIOS: ToTheLeftIOS,
  561 + FadeToTheStart: FadeToTheRight,
  562 + FadeToTheEnd: FadeToTheLeft,
  563 + ToTheStart: ToTheRight,
  564 + ToTheEnd: ToTheLeft,
  565 + FromTheStart: FromTheRight,
  566 + FromTheEnd: FromTheLeft,
  567 + BaseStartToEndGesture: BaseRightToLeftGesture,
  568 + BaseEndToStartGesture: BaseLeftToRightGesture,
  569 + };
  570 +}
  571 +
  572 +var BaseConfig = {
  573 + // A list of all gestures that are enabled on this scene
  574 + gestures: {
  575 + pop: directionMapping.BaseStartToEndGesture,
  576 + },
  577 +
  578 + // Rebound spring parameters when transitioning FROM this scene
  579 + springFriction: 26,
  580 + springTension: 200,
  581 +
  582 + // Velocity to start at when transitioning without gesture
  583 + defaultTransitionVelocity: 1.5,
  584 +
  585 + // Animation interpolators for horizontal transitioning:
  586 + animationInterpolators: {
  587 + into: buildStyleInterpolator(directionMapping.FromTheEnd),
  588 + out: buildStyleInterpolator(directionMapping.FadeToTheStart),
  589 + },
  590 +};
  591 +
  592 +var NavigatorSceneConfigs = {
  593 + PushFromRight: {
  594 + ...BaseConfig,
  595 + animationInterpolators: {
  596 + into: buildStyleInterpolator(directionMapping.FromTheEnd),
  597 + out: buildStyleInterpolator(directionMapping.ToTheStartIOS),
  598 + },
  599 + },
  600 + PushFromLeft: {
  601 + ...BaseConfig,
  602 + animationInterpolators: {
  603 + into: buildStyleInterpolator(directionMapping.FromTheStart),
  604 + out: buildStyleInterpolator(directionMapping.ToTheEndIOS),
  605 + },
  606 + },
  607 + FloatFromRight: {
  608 + ...BaseConfig,
  609 + // We will want to customize this soon
  610 + },
  611 + FloatFromLeft: {
  612 + ...BaseConfig,
  613 + gestures: {
  614 + pop: directionMapping.BaseEndToStartGesture,
  615 + },
  616 + animationInterpolators: {
  617 + into: buildStyleInterpolator(directionMapping.FromTheStart),
  618 + out: buildStyleInterpolator(directionMapping.FadeToTheEnd),
  619 + },
  620 + },
  621 + FloatFromBottom: {
  622 + ...BaseConfig,
  623 + gestures: {
  624 + pop: {
  625 + ...directionMapping.BaseStartToEndGesture,
  626 + edgeHitWidth: 150,
  627 + direction: 'top-to-bottom',
  628 + fullDistance: SCREEN_HEIGHT,
  629 + }
  630 + },
  631 + animationInterpolators: {
  632 + into: buildStyleInterpolator(FromTheFront),
  633 + out: buildStyleInterpolator(ToTheBack),
  634 + },
  635 + },
  636 + FloatFromBottomAndroid: {
  637 + ...BaseConfig,
  638 + gestures: null,
  639 + defaultTransitionVelocity: 3,
  640 + springFriction: 20,
  641 + animationInterpolators: {
  642 + into: buildStyleInterpolator(FromTheFrontAndroid),
  643 + out: buildStyleInterpolator(ToTheBackAndroid),
  644 + },
  645 + },
  646 + FadeAndroid: {
  647 + ...BaseConfig,
  648 + gestures: null,
  649 + animationInterpolators: {
  650 + into: buildStyleInterpolator(FadeIn),
  651 + out: buildStyleInterpolator(FadeOut),
  652 + },
  653 + },
  654 + SwipeFromLeft: {
  655 + ...BaseConfig,
  656 + gestures: {
  657 + jumpBack: {
  658 + ...directionMapping.BaseEndToStartGesture,
  659 + overswipe: BaseOverswipeConfig,
  660 + edgeHitWidth: null,
  661 + isDetachable: true,
  662 + },
  663 + jumpForward: {
  664 + ...directionMapping.BaseStartToEndGesture,
  665 + overswipe: BaseOverswipeConfig,
  666 + edgeHitWidth: null,
  667 + isDetachable: true,
  668 + },
  669 + },
  670 + animationInterpolators: {
  671 + into: buildStyleInterpolator(directionMapping.FromTheStart),
  672 + out: buildStyleInterpolator(directionMapping.ToTheEnd),
  673 + },
  674 + },
  675 + HorizontalSwipeJump: {
  676 + ...BaseConfig,
  677 + gestures: {
  678 + jumpBack: {
  679 + ...directionMapping.BaseStartToEndGesture,
  680 + overswipe: BaseOverswipeConfig,
  681 + edgeHitWidth: null,
  682 + isDetachable: true,
  683 + },
  684 + jumpForward: {
  685 + ...directionMapping.BaseEndToStartGesture,
  686 + overswipe: BaseOverswipeConfig,
  687 + edgeHitWidth: null,
  688 + isDetachable: true,
  689 + },
  690 + },
  691 + animationInterpolators: {
  692 + into: buildStyleInterpolator(directionMapping.FromTheEnd),
  693 + out: buildStyleInterpolator(directionMapping.ToTheStart),
  694 + },
  695 + },
  696 + HorizontalSwipeJumpFromRight: {
  697 + ...BaseConfig,
  698 + gestures: {
  699 + jumpBack: {
  700 + ...directionMapping.BaseEndToStartGesture,
  701 + overswipe: BaseOverswipeConfig,
  702 + edgeHitWidth: null,
  703 + isDetachable: true,
  704 + },
  705 + jumpForward: {
  706 + ...directionMapping.BaseStartToEndGesture,
  707 + overswipe: BaseOverswipeConfig,
  708 + edgeHitWidth: null,
  709 + isDetachable: true,
  710 + },
  711 + pop: directionMapping.BaseEndToStartGesture,
  712 + },
  713 + animationInterpolators: {
  714 + into: buildStyleInterpolator(directionMapping.FromTheStart),
  715 + out: buildStyleInterpolator(directionMapping.FadeToTheEnd),
  716 + },
  717 + },
  718 + HorizontalSwipeJumpFromLeft: {
  719 + ...BaseConfig,
  720 + gestures: {
  721 + jumpBack: {
  722 + ...directionMapping.BaseEndToStartGesture,
  723 + overswipe: BaseOverswipeConfig,
  724 + edgeHitWidth: null,
  725 + isDetachable: true,
  726 + },
  727 + jumpForward: {
  728 + ...directionMapping.BaseStartToEndGesture,
  729 + overswipe: BaseOverswipeConfig,
  730 + edgeHitWidth: null,
  731 + isDetachable: true,
  732 + },
  733 + pop: directionMapping.BaseEndToStartGesture,
  734 + },
  735 + animationInterpolators: {
  736 + into: buildStyleInterpolator(directionMapping.FromTheStart),
  737 + out: buildStyleInterpolator(directionMapping.ToTheEnd),
  738 + },
  739 + },
  740 + VerticalUpSwipeJump: {
  741 + ...BaseConfig,
  742 + gestures: {
  743 + jumpBack: {
  744 + ...BaseUpDownGesture,
  745 + overswipe: BaseOverswipeConfig,
  746 + edgeHitWidth: null,
  747 + isDetachable: true,
  748 + },
  749 + jumpForward: {
  750 + ...BaseDownUpGesture,
  751 + overswipe: BaseOverswipeConfig,
  752 + edgeHitWidth: null,
  753 + isDetachable: true,
  754 + },
  755 + },
  756 + animationInterpolators: {
  757 + into: buildStyleInterpolator(FromTheDown),
  758 + out: buildStyleInterpolator(ToTheUp),
  759 + },
  760 + },
  761 + VerticalDownSwipeJump: {
  762 + ...BaseConfig,
  763 + gestures: {
  764 + jumpBack: {
  765 + ...BaseDownUpGesture,
  766 + overswipe: BaseOverswipeConfig,
  767 + edgeHitWidth: null,
  768 + isDetachable: true,
  769 + },
  770 + jumpForward: {
  771 + ...BaseUpDownGesture,
  772 + overswipe: BaseOverswipeConfig,
  773 + edgeHitWidth: null,
  774 + isDetachable: true,
  775 + },
  776 + },
  777 + animationInterpolators: {
  778 + into: buildStyleInterpolator(FromTheTop),
  779 + out: buildStyleInterpolator(ToTheDown),
  780 + },
  781 + },
  782 +};
  783 +
  784 +module.exports = NavigatorSceneConfigs;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +import type EventEmitter from './EventEmitter';
  30 +
  31 +/**
  32 + * Subscribable provides a mixin for safely subscribing a component to an
  33 + * eventEmitter
  34 + *
  35 + * This will be replaced with the observe interface that will be coming soon to
  36 + * React Core
  37 + */
  38 +
  39 +var Subscribable = {};
  40 +
  41 +Subscribable.Mixin = {
  42 +
  43 + componentWillMount: function() {
  44 + this._subscribableSubscriptions = [];
  45 + },
  46 +
  47 + componentWillUnmount: function() {
  48 + this._subscribableSubscriptions.forEach(
  49 + (subscription) => subscription.remove()
  50 + );
  51 + this._subscribableSubscriptions = null;
  52 + },
  53 +
  54 + /**
  55 + * Special form of calling `addListener` that *guarantees* that a
  56 + * subscription *must* be tied to a component instance, and therefore will
  57 + * be cleaned up when the component is unmounted. It is impossible to create
  58 + * the subscription and pass it in - this method must be the one to create
  59 + * the subscription and therefore can guarantee it is retained in a way that
  60 + * will be cleaned up.
  61 + *
  62 + * @param {EventEmitter} eventEmitter emitter to subscribe to.
  63 + * @param {string} eventType Type of event to listen to.
  64 + * @param {function} listener Function to invoke when event occurs.
  65 + * @param {object} context Object to use as listener context.
  66 + */
  67 + addListenerOn: function(
  68 + eventEmitter: EventEmitter,
  69 + eventType: string,
  70 + listener: Function,
  71 + context: Object
  72 + ) {
  73 + this._subscribableSubscriptions.push(
  74 + eventEmitter.addListener(eventType, listener, context)
  75 + );
  76 + }
  77 +};
  78 +
  79 +module.exports = Subscribable;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + */
  25 +'use strict';
  26 +
  27 +jest
  28 + .disableAutomock()
  29 + .mock('ErrorUtils');
  30 +
  31 +var NavigationContext = require('NavigationContext');
  32 +var NavigationEvent = require('NavigationEvent');
  33 +
  34 +describe('NavigationContext', () => {
  35 + it('defaults `currentRoute` to null', () => {
  36 + var context = new NavigationContext();
  37 + expect(context.currentRoute).toEqual(null);
  38 + });
  39 +
  40 + it('updates `currentRoute`', () => {
  41 + var context = new NavigationContext();
  42 + context.emit('didfocus', {route: {name: 'a'}});
  43 + expect(context.currentRoute.name).toEqual('a');
  44 + });
  45 +
  46 + it('has parent', () => {
  47 + var parent = new NavigationContext();
  48 + var child = new NavigationContext();
  49 + parent.appendChild(child);
  50 + expect(child.parent).toBe(parent);
  51 + });
  52 +
  53 + it('has `top`', () => {
  54 + var top = new NavigationContext();
  55 + var parent = new NavigationContext();
  56 + var child = new NavigationContext();
  57 + top.appendChild(parent);
  58 + parent.appendChild(child);
  59 + expect(child.top).toBe(top);
  60 + });
  61 +
  62 + it('captures event', () => {
  63 + var parent = new NavigationContext();
  64 + var child = new NavigationContext();
  65 + parent.appendChild(child);
  66 +
  67 + var logs = [];
  68 +
  69 + var listener = (event) => {
  70 + var {currentTarget, eventPhase, target, type} = event;
  71 + logs.push({
  72 + currentTarget,
  73 + eventPhase,
  74 + target,
  75 + type,
  76 + });
  77 + };
  78 +
  79 + parent.addListener('yo', listener, true);
  80 + child.addListener('yo', listener, true);
  81 +
  82 + child.emit('yo');
  83 +
  84 + expect(logs).toEqual([
  85 + {
  86 + currentTarget: parent,
  87 + eventPhase: NavigationEvent.CAPTURING_PHASE,
  88 + target: child,
  89 + type: 'yo',
  90 + },
  91 + {
  92 + currentTarget: child,
  93 + eventPhase: NavigationEvent.AT_TARGET,
  94 + target: child,
  95 + type: 'yo',
  96 + }
  97 + ]);
  98 + });
  99 +
  100 + it('bubbles events', () => {
  101 + var parent = new NavigationContext();
  102 + var child = new NavigationContext();
  103 + parent.appendChild(child);
  104 +
  105 + var logs = [];
  106 +
  107 + var listener = (event) => {
  108 + var {currentTarget, eventPhase, target, type} = event;
  109 + logs.push({
  110 + currentTarget,
  111 + eventPhase,
  112 + target,
  113 + type,
  114 + });
  115 + };
  116 +
  117 + parent.addListener('yo', listener);
  118 + child.addListener('yo', listener);
  119 +
  120 + child.emit('yo');
  121 +
  122 + expect(logs).toEqual([
  123 + {
  124 + currentTarget: child,
  125 + eventPhase: NavigationEvent.AT_TARGET,
  126 + target: child,
  127 + type: 'yo',
  128 + },
  129 + {
  130 + currentTarget: parent,
  131 + eventPhase: NavigationEvent.BUBBLING_PHASE,
  132 + target: child,
  133 + type: 'yo',
  134 + },
  135 + ]);
  136 + });
  137 +
  138 + it('stops event propagation at capture phase', () => {
  139 + var parent = new NavigationContext();
  140 + var child = new NavigationContext();
  141 + parent.appendChild(child);
  142 +
  143 + var counter = 0;
  144 +
  145 + parent.addListener('yo', event => event.stopPropagation(), true);
  146 + child.addListener('yo', event => counter++, true);
  147 +
  148 + child.emit('yo');
  149 +
  150 + expect(counter).toBe(0);
  151 + });
  152 +
  153 + it('stops event propagation at bubbling phase', () => {
  154 + var parent = new NavigationContext();
  155 + var child = new NavigationContext();
  156 + parent.appendChild(child);
  157 +
  158 + var counter = 0;
  159 +
  160 + parent.addListener('yo', event => counter++);
  161 + child.addListener('yo', event => event.stopPropagation());
  162 +
  163 + child.emit('yo');
  164 +
  165 + expect(counter).toBe(0);
  166 + });
  167 +
  168 + it('prevents event at capture phase', () => {
  169 + var parent = new NavigationContext();
  170 + var child = new NavigationContext();
  171 + parent.appendChild(child);
  172 +
  173 + var val;
  174 + parent.addListener('yo', event => event.preventDefault(), true);
  175 + child.addListener('yo', event => val = event.defaultPrevented, true);
  176 +
  177 + child.emit('yo');
  178 +
  179 + expect(val).toBe(true);
  180 + });
  181 +
  182 + it('prevents event at bubble phase', () => {
  183 + var parent = new NavigationContext();
  184 + var child = new NavigationContext();
  185 + parent.appendChild(child);
  186 +
  187 + var val;
  188 + parent.addListener('yo', event => val = event.defaultPrevented);
  189 + child.addListener('yo', event => event.preventDefault());
  190 +
  191 + child.emit('yo');
  192 +
  193 + expect(val).toBe(true);
  194 + });
  195 +
  196 + it('emits nested events in order at capture phase', () => {
  197 + var parent = new NavigationContext();
  198 + var child = new NavigationContext();
  199 + parent.appendChild(child);
  200 +
  201 + var logs = [];
  202 +
  203 + var listener = (event) => {
  204 + var {currentTarget, type} = event;
  205 + logs.push({
  206 + currentTarget,
  207 + type,
  208 + });
  209 + };
  210 +
  211 + child.addListener('yo', event => {
  212 + // event `didyo` should be fired after the full propagation cycle of the
  213 + // `yo` event.
  214 + child.emit('didyo');
  215 + });
  216 +
  217 + parent.addListener('yo', listener, true);
  218 + parent.addListener('didyo', listener, true);
  219 + child.addListener('yo', listener, true);
  220 +
  221 + child.emit('yo');
  222 +
  223 + expect(logs).toEqual([
  224 + {type: 'yo', currentTarget: parent},
  225 + {type: 'yo', currentTarget: child},
  226 + {type: 'didyo', currentTarget: parent},
  227 + ]);
  228 + });
  229 +
  230 + it('emits nested events in order at bubbling phase', () => {
  231 + var parent = new NavigationContext();
  232 + var child = new NavigationContext();
  233 + parent.appendChild(child);
  234 +
  235 + var logs = [];
  236 +
  237 + var listener = (event) => {
  238 + var {currentTarget, type} = event;
  239 + logs.push({
  240 + currentTarget,
  241 + type,
  242 + });
  243 + };
  244 +
  245 + child.addListener('yo', event => {
  246 + // event `didyo` should be fired after the full propagation cycle of the
  247 + // `yo` event.
  248 + child.emit('didyo');
  249 + });
  250 +
  251 + parent.addListener('yo', listener);
  252 + child.addListener('yo', listener);
  253 + parent.addListener('didyo', listener);
  254 +
  255 + child.emit('yo');
  256 +
  257 + expect(logs).toEqual([
  258 + {type: 'yo', currentTarget: child},
  259 + {type: 'yo', currentTarget: parent},
  260 + {type: 'didyo', currentTarget: parent},
  261 + ]);
  262 + });
  263 +});
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + */
  25 +'use strict';
  26 +
  27 +jest
  28 + .unmock('NavigationEvent')
  29 + .unmock('fbjs/lib/invariant');
  30 +
  31 +var NavigationEvent = require('NavigationEvent');
  32 +
  33 +describe('NavigationEvent', () => {
  34 + it('constructs', () => {
  35 + var target = {};
  36 + var event = new NavigationEvent('foo', target, 123);
  37 + expect(event.type).toBe('foo');
  38 + expect(event.target).toBe(target);
  39 + expect(event.data).toBe(123);
  40 + });
  41 +
  42 + it('constructs from pool', () => {
  43 + var target = {};
  44 + var event = NavigationEvent.pool('foo', target, 123);
  45 + expect(event.type).toBe('foo');
  46 + expect(event.target).toBe(target);
  47 + expect(event.data).toBe(123);
  48 + });
  49 +
  50 + it('prevents default', () => {
  51 + var event = new NavigationEvent('foo', {}, 123);
  52 + expect(event.defaultPrevented).toBe(false);
  53 + event.preventDefault();
  54 + expect(event.defaultPrevented).toBe(true);
  55 + });
  56 +
  57 + it('recycles', () => {
  58 + var event1 = NavigationEvent.pool('foo', {}, 123);
  59 + event1.dispose();
  60 + expect(event1.type).toBeFalsy();
  61 + expect(event1.data).toBe(null);
  62 + expect(event1.target).toBe(null);
  63 +
  64 + var event2 = NavigationEvent.pool('bar', {}, 456);
  65 + expect(event2.type).toBe('bar');
  66 + expect(event2).toBe(event1);
  67 + });
  68 +});
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + */
  25 +'use strict';
  26 +
  27 +jest
  28 + .unmock('EmitterSubscription')
  29 + .unmock('EventSubscription')
  30 + .unmock('EventEmitter')
  31 + .unmock('EventSubscriptionVendor')
  32 + .unmock('NavigationEvent')
  33 + .unmock('NavigationEventEmitter');
  34 +
  35 +var NavigationEventEmitter = require('NavigationEventEmitter');
  36 +
  37 +describe('NavigationEventEmitter', () => {
  38 + it('emits event', () => {
  39 + var context = {};
  40 + var emitter = new NavigationEventEmitter(context);
  41 + var logs = [];
  42 +
  43 + emitter.addListener('ping', (event) => {
  44 + var {type, data, target, defaultPrevented} = event;
  45 +
  46 + logs.push({
  47 + data,
  48 + defaultPrevented,
  49 + target,
  50 + type,
  51 + });
  52 +
  53 + });
  54 +
  55 + emitter.emit('ping', 'hello');
  56 +
  57 + expect(logs.length).toBe(1);
  58 + expect(logs[0].target).toBe(context);
  59 + expect(logs[0].type).toBe('ping');
  60 + expect(logs[0].data).toBe('hello');
  61 + expect(logs[0].defaultPrevented).toBe(false);
  62 + });
  63 +
  64 + it('does not emit event that has no listeners', () => {
  65 + var context = {};
  66 + var emitter = new NavigationEventEmitter(context);
  67 + var pinged = false;
  68 +
  69 + emitter.addListener('ping', () => {
  70 + pinged = true;
  71 + });
  72 +
  73 + emitter.emit('yo', 'bo');
  74 + expect(pinged).toBe(false);
  75 + });
  76 +
  77 + it('puts nested emit call in a queue', () => {
  78 + var context = {};
  79 + var emitter = new NavigationEventEmitter(context);
  80 + var logs = [];
  81 +
  82 + emitter.addListener('one', () => {
  83 + logs.push(1);
  84 + emitter.emit('two');
  85 + logs.push(2);
  86 + });
  87 +
  88 + emitter.addListener('two', () => {
  89 + logs.push(3);
  90 + emitter.emit('three');
  91 + logs.push(4);
  92 + });
  93 +
  94 + emitter.addListener('three', () => {
  95 + logs.push(5);
  96 + });
  97 +
  98 + emitter.emit('one');
  99 +
  100 + expect(logs).toEqual([1, 2, 3, 4, 5]);
  101 + });
  102 +
  103 + it('puts nested emit call in a queue should be in sequence order', () => {
  104 + var context = {};
  105 + var emitter = new NavigationEventEmitter(context);
  106 + var logs = [];
  107 +
  108 + emitter.addListener('one', () => {
  109 + logs.push(1);
  110 + emitter.emit('two');
  111 + emitter.emit('three');
  112 + logs.push(2);
  113 + });
  114 +
  115 + emitter.addListener('two', () => {
  116 + logs.push(3);
  117 + logs.push(4);
  118 + });
  119 +
  120 + emitter.addListener('three', () => {
  121 + logs.push(5);
  122 + });
  123 +
  124 + emitter.emit('one');
  125 +
  126 + expect(logs).toEqual([1, 2, 3, 4, 5]);
  127 + });
  128 +
  129 + it('calls callback after emitting', () => {
  130 + var context = {};
  131 + var emitter = new NavigationEventEmitter(context);
  132 + var logs = [];
  133 +
  134 + emitter.addListener('ping', (event) => {
  135 + var {type, data, target, defaultPrevented} = event;
  136 + logs.push({
  137 + data,
  138 + defaultPrevented,
  139 + target,
  140 + type,
  141 + });
  142 + event.preventDefault();
  143 + });
  144 +
  145 + emitter.emit('ping', 'hello', (event) => {
  146 + var {type, data, target, defaultPrevented} = event;
  147 + logs.push({
  148 + data,
  149 + defaultPrevented,
  150 + target,
  151 + type,
  152 + });
  153 + });
  154 +
  155 + expect(logs.length).toBe(2);
  156 + expect(logs[1].target).toBe(context);
  157 + expect(logs[1].type).toBe('ping');
  158 + expect(logs[1].data).toBe('hello');
  159 + expect(logs[1].defaultPrevented).toBe(true);
  160 + });
  161 +
  162 + it('calls callback after emitting the current event and before ' +
  163 + 'emitting the next event', () => {
  164 + var context = {};
  165 + var emitter = new NavigationEventEmitter(context);
  166 + var logs = [];
  167 +
  168 + emitter.addListener('ping', (event) => {
  169 + logs.push('ping');
  170 + emitter.emit('pong');
  171 + });
  172 +
  173 + emitter.addListener('pong', (event) => {
  174 + logs.push('pong');
  175 + });
  176 +
  177 + emitter.emit('ping', null, () => {
  178 + logs.push('did-ping');
  179 + });
  180 +
  181 + expect(logs).toEqual([
  182 + 'ping',
  183 + 'did-ping',
  184 + 'pong',
  185 + ]);
  186 + });
  187 +});
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + */
  25 +'use strict';
  26 +
  27 +
  28 +jest
  29 + .disableAutomock()
  30 + .mock('ErrorUtils');
  31 +
  32 +var NavigationRouteStack = require('NavigationRouteStack');
  33 +
  34 +function assetStringNotEmpty(str) {
  35 + expect(!!str && typeof str === 'string').toBe(true);
  36 +}
  37 +
  38 +describe('NavigationRouteStack:', () => {
  39 + // Different types of routes.
  40 + var ROUTES = [
  41 + 'foo',
  42 + 1,
  43 + true,
  44 + {foo: 'bar'},
  45 + ['foo'],
  46 + ];
  47 +
  48 + // Basic
  49 + it('gets index', () => {
  50 + var stack = new NavigationRouteStack(1, ['a', 'b', 'c']);
  51 + expect(stack.index).toBe(1);
  52 + });
  53 +
  54 + it('gets size', () => {
  55 + var stack = new NavigationRouteStack(1, ['a', 'b', 'c']);
  56 + expect(stack.size).toBe(3);
  57 + });
  58 +
  59 + it('gets route', () => {
  60 + var stack = new NavigationRouteStack(0, ['a', 'b', 'c']);
  61 + expect(stack.get(2)).toBe('c');
  62 + });
  63 +
  64 + it('converts to an array', () => {
  65 + var stack = new NavigationRouteStack(0, ['a', 'b']);
  66 + expect(stack.toArray()).toEqual(['a', 'b']);
  67 + });
  68 +
  69 + it('creates a new stack after mutation', () => {
  70 + var stack1 = new NavigationRouteStack(0, ['a', 'b']);
  71 + var stack2 = stack1.push('c');
  72 + expect(stack1).not.toBe(stack2);
  73 + });
  74 +
  75 + it('throws at index out of bound', () => {
  76 + expect(() => {
  77 + new NavigationRouteStack(-1, ['a', 'b']);
  78 + }).toThrow();
  79 +
  80 + expect(() => {
  81 + new NavigationRouteStack(100, ['a', 'b']);
  82 + }).toThrow();
  83 + });
  84 +
  85 + it('finds index', () => {
  86 + var stack = new NavigationRouteStack(0, ['a', 'b']);
  87 + expect(stack.indexOf('b')).toBe(1);
  88 + expect(stack.indexOf('c')).toBe(-1);
  89 + });
  90 +
  91 + // Key
  92 + it('gets key for route', () => {
  93 + var test = (route) => {
  94 + var stack = new NavigationRouteStack(0, ['a']);
  95 + var key = stack.push(route).keyOf(route);
  96 + expect(typeof key).toBe('string');
  97 + expect(!!key).toBe(true);
  98 + };
  99 +
  100 + ROUTES.forEach(test);
  101 + });
  102 +
  103 + it('gets a key of larger value for route', () => {
  104 + var lastKey = '';
  105 + var test = (route) => {
  106 + var stack = new NavigationRouteStack(0, ['a']);
  107 + var key = stack.push(route).keyOf(route);
  108 + expect(key > lastKey).toBe(true);
  109 + lastKey = key;
  110 + };
  111 +
  112 + ROUTES.forEach(test);
  113 + });
  114 +
  115 + it('gets an unique key for a different route', () => {
  116 + var stack = new NavigationRouteStack(0, ['a']);
  117 + var keys = {};
  118 +
  119 + var test = (route) => {
  120 + stack = stack.push(route);
  121 + var key = stack.keyOf(route);
  122 + expect(keys[key]).toBe(undefined);
  123 + keys[key] = true;
  124 + };
  125 +
  126 + ROUTES.forEach(test);
  127 + });
  128 +
  129 + it('gets the same unique key for the same route', () => {
  130 + var test = (route) => {
  131 + var stack = new NavigationRouteStack(0, [route]);
  132 + expect(stack.keyOf(route)).toBe(stack.keyOf(route));
  133 + };
  134 +
  135 + ROUTES.forEach(test);
  136 + });
  137 +
  138 +
  139 + it('gets the same unique key form the derived stack', () => {
  140 + var test = (route) => {
  141 + var stack = new NavigationRouteStack(0, [route]);
  142 + var derivedStack = stack.push('wow').pop().slice(0, 10).push('blah');
  143 + expect(derivedStack.keyOf(route)).toBe(stack.keyOf(route));
  144 + };
  145 +
  146 + ROUTES.forEach(test);
  147 + });
  148 +
  149 + it('gets a different key from a different stack', () => {
  150 + var test = (route) => {
  151 + var stack1 = new NavigationRouteStack(0, [route]);
  152 + var stack2 = new NavigationRouteStack(0, [route]);
  153 + expect(stack1.keyOf(route)).not.toBe(stack2.keyOf(route));
  154 + };
  155 +
  156 + ROUTES.forEach(test);
  157 + });
  158 +
  159 + it('gets no key for a route that does not contains this route', () => {
  160 + var stack = new NavigationRouteStack(0, ['a']);
  161 + expect(stack.keyOf('b')).toBe(null);
  162 + });
  163 +
  164 + it('gets a new key for a route that was removed and added again', () => {
  165 + var test = (route) => {
  166 + var stack = new NavigationRouteStack(0, ['a']);
  167 +
  168 + var key1 = stack.push(route).keyOf(route);
  169 + var key2 = stack.push(route).pop().push(route).keyOf(route);
  170 + expect(key1).not.toBe(key2);
  171 + };
  172 +
  173 + ROUTES.forEach(test);
  174 + });
  175 +
  176 + // Slice
  177 + it('slices', () => {
  178 + var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c', 'd']);
  179 + var stack2 = stack1.slice(1, 3);
  180 + expect(stack2).not.toBe(stack1);
  181 + expect(stack2.toArray()).toEqual(['b', 'c']);
  182 + });
  183 +
  184 + it('may update index after slicing', () => {
  185 + var stack = new NavigationRouteStack(2, ['a', 'b', 'c']);
  186 + expect(stack.slice().index).toBe(2);
  187 + expect(stack.slice(0, 1).index).toBe(0);
  188 + expect(stack.slice(0, 2).index).toBe(1);
  189 + expect(stack.slice(0, 3).index).toBe(2);
  190 + expect(stack.slice(0, 100).index).toBe(2);
  191 + expect(stack.slice(-2).index).toBe(1);
  192 + });
  193 +
  194 + it('slices without specifying params', () => {
  195 + var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c']);
  196 + var stack2 = stack1.slice();
  197 + expect(stack2).toBe(stack1);
  198 + });
  199 +
  200 + it('slices to from the end', () => {
  201 + var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c', 'd']);
  202 + var stack2 = stack1.slice(-2);
  203 + expect(stack2.toArray()).toEqual(['c', 'd']);
  204 + });
  205 +
  206 + it('throws when slicing to empty', () => {
  207 + expect(() => {
  208 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  209 + stack.slice(100);
  210 + }).toThrow();
  211 + });
  212 +
  213 + // Push
  214 + it('pushes route', () => {
  215 + var stack1 = new NavigationRouteStack(1, ['a', 'b']);
  216 + var stack2 = stack1.push('c');
  217 +
  218 + expect(stack2).not.toBe(stack1);
  219 + expect(stack2.toArray()).toEqual(['a', 'b', 'c']);
  220 + expect(stack2.index).toBe(2);
  221 + expect(stack2.size).toBe(3);
  222 + });
  223 +
  224 + it('throws when pushing empty route', () => {
  225 + expect(() => {
  226 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  227 + stack.push(null);
  228 + }).toThrow();
  229 +
  230 + expect(() => {
  231 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  232 + stack.push('');
  233 + }).toThrow();
  234 +
  235 + expect(() => {
  236 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  237 + stack.push(undefined);
  238 + }).toThrow();
  239 + });
  240 +
  241 + it('replaces routes on push', () => {
  242 + var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c']);
  243 + var stack2 = stack1.push('d');
  244 + expect(stack2).not.toBe(stack1);
  245 + expect(stack2.toArray()).toEqual(['a', 'b', 'd']);
  246 + expect(stack2.index).toBe(2);
  247 + });
  248 +
  249 + // Pop
  250 + it('pops route', () => {
  251 + var stack1 = new NavigationRouteStack(2, ['a', 'b', 'c']);
  252 + var stack2 = stack1.pop();
  253 + expect(stack2).not.toBe(stack1);
  254 + expect(stack2.toArray()).toEqual(['a', 'b']);
  255 + expect(stack2.index).toBe(1);
  256 + expect(stack2.size).toBe(2);
  257 + });
  258 +
  259 + it('replaces routes on pop', () => {
  260 + var stack1 = new NavigationRouteStack(1, ['a', 'b', 'c']);
  261 + var stack2 = stack1.pop();
  262 + expect(stack2).not.toBe(stack1);
  263 + expect(stack2.toArray()).toEqual(['a']);
  264 + expect(stack2.index).toBe(0);
  265 + });
  266 +
  267 + it('throws when popping to empty stack', () => {
  268 + expect(() => {
  269 + var stack = new NavigationRouteStack(0, ['a']);
  270 + stack.pop();
  271 + }).toThrow();
  272 + });
  273 +
  274 + // Jump
  275 + it('jumps to index', () => {
  276 + var stack1 = new NavigationRouteStack(0, ['a', 'b', 'c']);
  277 + var stack2 = stack1.jumpToIndex(2);
  278 +
  279 + expect(stack2).not.toBe(stack1);
  280 + expect(stack2.index).toBe(2);
  281 + });
  282 +
  283 + it('throws then jumping to index out of bound', () => {
  284 + expect(() => {
  285 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  286 + stack.jumpToIndex(2);
  287 + }).toThrow();
  288 +
  289 + expect(() => {
  290 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  291 + stack.jumpToIndex(-1);
  292 + }).toThrow();
  293 + });
  294 +
  295 + // Replace
  296 + it('replaces route at index', () => {
  297 + var stack1 = new NavigationRouteStack(1, ['a', 'b']);
  298 + var stack2 = stack1.replaceAtIndex(0, 'x');
  299 +
  300 + expect(stack2).not.toBe(stack1);
  301 + expect(stack2.toArray()).toEqual(['x', 'b']);
  302 + expect(stack2.index).toBe(0);
  303 + });
  304 +
  305 + it('replaces route at negative index', () => {
  306 + var stack1 = new NavigationRouteStack(1, ['a', 'b']);
  307 + var stack2 = stack1.replaceAtIndex(-1, 'x');
  308 +
  309 + expect(stack2).not.toBe(stack1);
  310 + expect(stack2.toArray()).toEqual(['a', 'x']);
  311 + expect(stack2.index).toBe(1);
  312 + });
  313 +
  314 + it('throws when replacing empty route', () => {
  315 + expect(() => {
  316 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  317 + stack.replaceAtIndex(1, null);
  318 + }).toThrow();
  319 + });
  320 +
  321 + it('throws when replacing at index out of bound', () => {
  322 + expect(() => {
  323 + var stack = new NavigationRouteStack(1, ['a', 'b']);
  324 + stack.replaceAtIndex(100, 'x');
  325 + }).toThrow();
  326 + });
  327 +
  328 + // Iteration
  329 + it('iterates each item', () => {
  330 + var stack = new NavigationRouteStack(0, ['a', 'b']);
  331 + var logs = [];
  332 + var keys = {};
  333 + var context = {name: 'yo'};
  334 +
  335 + stack.forEach(function (route, index, key) {
  336 + assetStringNotEmpty(key);
  337 + if (!keys.hasOwnProperty(key)) {
  338 + keys[key] = true;
  339 + logs.push([
  340 + route,
  341 + index,
  342 + this.name,
  343 + ]);
  344 + }
  345 + }, context);
  346 +
  347 + expect(logs).toEqual([
  348 + ['a', 0, 'yo'],
  349 + ['b', 1, 'yo'],
  350 + ]);
  351 + });
  352 +
  353 + it('Maps to an array', () => {
  354 + var stack = new NavigationRouteStack(0, ['a', 'b']);
  355 + var keys = {};
  356 + var context = {name: 'yo'};
  357 +
  358 + var logs = stack.mapToArray(function(route, index, key) {
  359 + assetStringNotEmpty(key);
  360 + if (!keys.hasOwnProperty(key)) {
  361 + keys[key] = true;
  362 + return [
  363 + route,
  364 + index,
  365 + this.name,
  366 + ];
  367 + }
  368 + }, context);
  369 +
  370 + expect(logs).toEqual([
  371 + ['a', 0, 'yo'],
  372 + ['b', 1, 'yo'],
  373 + ]);
  374 + });
  375 +
  376 + // Diff
  377 + it('subtracts stack', () => {
  378 + var stack1 = new NavigationRouteStack(2, ['a', 'b', 'c']);
  379 + var stack2 = stack1.pop().pop().push('x').push('y');
  380 +
  381 + var diff = stack1.subtract(stack2);
  382 +
  383 + var result = diff.toJS().map((record) => {
  384 + assetStringNotEmpty(record.key);
  385 + return {
  386 + index: record.index,
  387 + route: record.route,
  388 + };
  389 + });
  390 +
  391 + // route `b` and `c` are no longer in the stack.
  392 + expect(result).toEqual([
  393 + {
  394 + index: 1,
  395 + route: 'b',
  396 + },
  397 + {
  398 + index: 2,
  399 + route: 'c',
  400 + },
  401 + ]);
  402 + });
  403 +
  404 + it('only subtracts the derived stack', () => {
  405 + var stack1 = new NavigationRouteStack(2, ['a', 'b', 'c']);
  406 + var stack2 = new NavigationRouteStack(0, ['a']);
  407 + var diff = stack1.subtract(stack2);
  408 +
  409 + var result = diff.toJS().map((record) => {
  410 + assetStringNotEmpty(record.key);
  411 + return {
  412 + index: record.index,
  413 + route: record.route,
  414 + };
  415 + });
  416 +
  417 + expect(result).toEqual([
  418 + {
  419 + index: 0,
  420 + route: 'a',
  421 + },
  422 + {
  423 + index: 1,
  424 + route: 'b',
  425 + },
  426 + {
  427 + index: 2,
  428 + route: 'c',
  429 + },
  430 + ]);
  431 +
  432 + });
  433 +});
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + */
  4 +
  5 +'use strict';
  6 +
  7 +jest
  8 + .unmock('NavigationTreeNode')
  9 + .unmock('fbjs/lib/invariant')
  10 + .unmock('immutable');
  11 +
  12 +var NavigationTreeNode = require('NavigationTreeNode');
  13 +
  14 +describe('NavigationTreeNode-test', () => {
  15 + it('should be empty', () => {
  16 + var node = new NavigationTreeNode();
  17 + expect(node.getValue()).toEqual(undefined);
  18 + expect(node.getParent()).toEqual(null);
  19 + expect(node.getChildrenCount()).toEqual(0);
  20 + expect(node.getChildAt(0)).toEqual(null);
  21 + });
  22 +
  23 +
  24 + it('should contain value', () => {
  25 + var node = new NavigationTreeNode(123);
  26 + expect(node.getValue()).toEqual(123);
  27 + });
  28 +
  29 + it('should appendChild', () => {
  30 + var papa = new NavigationTreeNode('hedger');
  31 + var baby = new NavigationTreeNode('hedger jr');
  32 + papa.appendChild(baby);
  33 + expect(papa.getChildAt(0)).toEqual(baby);
  34 + expect(papa.getChildrenCount()).toEqual(1);
  35 + expect(baby.getParent()).toEqual(papa);
  36 + });
  37 +
  38 + it('should removeChild', () => {
  39 + var papa = new NavigationTreeNode('Eddard Stark');
  40 + var baby = new NavigationTreeNode('Robb Stark');
  41 + papa.appendChild(baby);
  42 +
  43 + papa.removeChild(baby);
  44 + expect(papa.getChildAt(0)).toEqual(null);
  45 + expect(papa.getChildrenCount()).toEqual(0);
  46 + expect(baby.getParent()).toEqual(null);
  47 + });
  48 +
  49 + it('should not remove non-child', () => {
  50 + var papa = new NavigationTreeNode('dog');
  51 + var baby = new NavigationTreeNode('cat');
  52 + expect(papa.removeChild.bind(papa, baby)).toThrow();
  53 + });
  54 +
  55 + it('should find child', () => {
  56 + var papa = new NavigationTreeNode('Eddard Stark');
  57 + var baby = new NavigationTreeNode('Robb Stark');
  58 +
  59 + papa.appendChild(baby);
  60 + expect(papa.indexOf(baby)).toEqual(0);
  61 +
  62 + papa.removeChild(baby);
  63 + expect(papa.indexOf(baby)).toEqual(-1);
  64 + });
  65 +
  66 +
  67 + it('should traverse each child', () => {
  68 + var parent = new NavigationTreeNode();
  69 + parent.appendChild(new NavigationTreeNode('a'));
  70 + parent.appendChild(new NavigationTreeNode('b'));
  71 + parent.appendChild(new NavigationTreeNode('c'));
  72 + var result = [];
  73 + parent.forEach((child, index) => {
  74 + result[index] = child.getValue();
  75 + });
  76 +
  77 + expect(result).toEqual(['a', 'b', 'c']);
  78 + });
  79 +
  80 + it('should map children', () => {
  81 + var parent = new NavigationTreeNode();
  82 + parent.appendChild(new NavigationTreeNode('a'));
  83 + parent.appendChild(new NavigationTreeNode('b'));
  84 + parent.appendChild(new NavigationTreeNode('c'));
  85 + var result = parent.map((child, index) => {
  86 + return child.getValue();
  87 + });
  88 +
  89 + expect(result).toEqual(['a', 'b', 'c']);
  90 + });
  91 +
  92 + it('should traverse some children', () => {
  93 + var parent = new NavigationTreeNode();
  94 + parent.appendChild(new NavigationTreeNode('a'));
  95 + parent.appendChild(new NavigationTreeNode('b'));
  96 + parent.appendChild(new NavigationTreeNode('c'));
  97 +
  98 + var result = [];
  99 + var value = parent.some((child, index) => {
  100 + if (index > 1) {
  101 + return true;
  102 + } else {
  103 + result[index] = child.getValue();
  104 + }
  105 + });
  106 +
  107 + expect(value).toEqual(true);
  108 + expect(result).toEqual(['a', 'b']);
  109 + });
  110 +});
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +
  27 +/**
  28 + * Cannot "use strict" because we must use eval in this file.
  29 + */
  30 +/* eslint-disable global-strict */
  31 +
  32 +var keyOf = require('fbjs/lib/keyOf');
  33 +
  34 +var X_DIM = keyOf({x: null});
  35 +var Y_DIM = keyOf({y: null});
  36 +var Z_DIM = keyOf({z: null});
  37 +var W_DIM = keyOf({w: null});
  38 +
  39 +var TRANSFORM_ROTATE_NAME = keyOf({transformRotateRadians: null});
  40 +
  41 +var ShouldAllocateReusableOperationVars = {
  42 + transformRotateRadians: true,
  43 + transformScale: true,
  44 + transformTranslate: true,
  45 +};
  46 +
  47 +var InitialOperationField = {
  48 + transformRotateRadians: [0, 0, 0, 1],
  49 + transformTranslate: [0, 0, 0],
  50 + transformScale: [1, 1, 1],
  51 +};
  52 +
  53 +
  54 +/**
  55 + * Creates a highly specialized animation function that may be evaluated every
  56 + * frame. For example:
  57 + *
  58 + * var ToTheLeft = {
  59 + * opacity: {
  60 + * from: 1,
  61 + * to: 0.7,
  62 + * min: 0,
  63 + * max: 1,
  64 + * type: 'linear',
  65 + * extrapolate: false,
  66 + * round: 100,
  67 + * },
  68 + * left: {
  69 + * from: 0,
  70 + * to: -SCREEN_WIDTH * 0.3,
  71 + * min: 0,
  72 + * max: 1,
  73 + * type: 'linear',
  74 + * extrapolate: true,
  75 + * round: PixelRatio.get(),
  76 + * },
  77 + * };
  78 + *
  79 + * var toTheLeft = buildStyleInterpolator(ToTheLeft);
  80 + *
  81 + * Would returns a specialized function of the form:
  82 + *
  83 + * function(result, value) {
  84 + * var didChange = false;
  85 + * var nextScalarVal;
  86 + * var ratio;
  87 + * ratio = (value - 0) / 1;
  88 + * ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio);
  89 + * nextScalarVal = Math.round(100 * (1 * (1 - ratio) + 0.7 * ratio)) / 100;
  90 + * if (!didChange) {
  91 + * var prevVal = result.opacity;
  92 + * result.opacity = nextScalarVal;
  93 + * didChange = didChange || (nextScalarVal !== prevVal);
  94 + * } else {
  95 + * result.opacity = nextScalarVal;
  96 + * }
  97 + * ratio = (value - 0) / 1;
  98 + * nextScalarVal = Math.round(2 * (0 * (1 - ratio) + -30 * ratio)) / 2;
  99 + * if (!didChange) {
  100 + * var prevVal = result.left;
  101 + * result.left = nextScalarVal;
  102 + * didChange = didChange || (nextScalarVal !== prevVal);
  103 + * } else {
  104 + * result.left = nextScalarVal;
  105 + * }
  106 + * return didChange;
  107 + * }
  108 + */
  109 +
  110 +var ARGUMENT_NAMES_RE = /([^\s,]+)/g;
  111 +/**
  112 + * This is obviously a huge hack. Proper tooling would allow actual inlining.
  113 + * This only works in a few limited cases (where there is no function return
  114 + * value, and the function operates mutatively on parameters).
  115 + *
  116 + * Example:
  117 + *
  118 + *
  119 + * var inlineMe(a, b) {
  120 + * a = b + b;
  121 + * };
  122 + *
  123 + * inline(inlineMe, ['hi', 'bye']); // "hi = bye + bye;"
  124 + *
  125 + * @param {string} fnStr Source of any simple function who's arguments can be
  126 + * replaced via a regex.
  127 + * @param {array<string>} replaceWithArgs Corresponding names of variables
  128 + * within an environment, to replace `func` args with.
  129 + * @return {string} Resulting function body string.
  130 + */
  131 +var inline = function(fnStr, replaceWithArgs) {
  132 + var parameterNames = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')'))
  133 + .match(ARGUMENT_NAMES_RE) ||
  134 + [];
  135 + var replaceRegexStr = parameterNames.map(function(paramName) {
  136 + return '\\b' + paramName + '\\b';
  137 + }).join('|');
  138 + var replaceRegex = new RegExp(replaceRegexStr, 'g');
  139 + var fnBody = fnStr.substring(fnStr.indexOf('{') + 1, fnStr.lastIndexOf('}'));
  140 + var newFnBody = fnBody.replace(replaceRegex, function(parameterName) {
  141 + var indexInParameterNames = parameterNames.indexOf(parameterName);
  142 + var replacementName = replaceWithArgs[indexInParameterNames];
  143 + return replacementName;
  144 + });
  145 + return newFnBody.split('\n');
  146 +};
  147 +
  148 +/**
  149 + * Simply a convenient way to inline functions using the inline function.
  150 + */
  151 +var MatrixOps = {
  152 + unroll: `function(matVar, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15) {
  153 + m0 = matVar[0];
  154 + m1 = matVar[1];
  155 + m2 = matVar[2];
  156 + m3 = matVar[3];
  157 + m4 = matVar[4];
  158 + m5 = matVar[5];
  159 + m6 = matVar[6];
  160 + m7 = matVar[7];
  161 + m8 = matVar[8];
  162 + m9 = matVar[9];
  163 + m10 = matVar[10];
  164 + m11 = matVar[11];
  165 + m12 = matVar[12];
  166 + m13 = matVar[13];
  167 + m14 = matVar[14];
  168 + m15 = matVar[15];
  169 + }`,
  170 +
  171 + matrixDiffers: `function(retVar, matVar, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15) {
  172 + retVar = retVar ||
  173 + m0 !== matVar[0] ||
  174 + m1 !== matVar[1] ||
  175 + m2 !== matVar[2] ||
  176 + m3 !== matVar[3] ||
  177 + m4 !== matVar[4] ||
  178 + m5 !== matVar[5] ||
  179 + m6 !== matVar[6] ||
  180 + m7 !== matVar[7] ||
  181 + m8 !== matVar[8] ||
  182 + m9 !== matVar[9] ||
  183 + m10 !== matVar[10] ||
  184 + m11 !== matVar[11] ||
  185 + m12 !== matVar[12] ||
  186 + m13 !== matVar[13] ||
  187 + m14 !== matVar[14] ||
  188 + m15 !== matVar[15];
  189 + }`,
  190 +
  191 + transformScale: `function(matVar, opVar) {
  192 + // Scaling matVar by opVar
  193 + var x = opVar[0];
  194 + var y = opVar[1];
  195 + var z = opVar[2];
  196 + matVar[0] = matVar[0] * x;
  197 + matVar[1] = matVar[1] * x;
  198 + matVar[2] = matVar[2] * x;
  199 + matVar[3] = matVar[3] * x;
  200 + matVar[4] = matVar[4] * y;
  201 + matVar[5] = matVar[5] * y;
  202 + matVar[6] = matVar[6] * y;
  203 + matVar[7] = matVar[7] * y;
  204 + matVar[8] = matVar[8] * z;
  205 + matVar[9] = matVar[9] * z;
  206 + matVar[10] = matVar[10] * z;
  207 + matVar[11] = matVar[11] * z;
  208 + matVar[12] = matVar[12];
  209 + matVar[13] = matVar[13];
  210 + matVar[14] = matVar[14];
  211 + matVar[15] = matVar[15];
  212 + }`,
  213 +
  214 + /**
  215 + * All of these matrix transforms are not general purpose utilities, and are
  216 + * only suitable for being inlined for the use of building up interpolators.
  217 + */
  218 + transformTranslate: `function(matVar, opVar) {
  219 + // Translating matVar by opVar
  220 + var x = opVar[0];
  221 + var y = opVar[1];
  222 + var z = opVar[2];
  223 + matVar[12] = matVar[0] * x + matVar[4] * y + matVar[8] * z + matVar[12];
  224 + matVar[13] = matVar[1] * x + matVar[5] * y + matVar[9] * z + matVar[13];
  225 + matVar[14] = matVar[2] * x + matVar[6] * y + matVar[10] * z + matVar[14];
  226 + matVar[15] = matVar[3] * x + matVar[7] * y + matVar[11] * z + matVar[15];
  227 + }`,
  228 +
  229 + /**
  230 + * @param {array} matVar Both the input, and the output matrix.
  231 + * @param {quaternion specification} q Four element array describing rotation.
  232 + */
  233 + transformRotateRadians: `function(matVar, q) {
  234 + // Rotating matVar by q
  235 + var xQuat = q[0], yQuat = q[1], zQuat = q[2], wQuat = q[3];
  236 + var x2Quat = xQuat + xQuat;
  237 + var y2Quat = yQuat + yQuat;
  238 + var z2Quat = zQuat + zQuat;
  239 + var xxQuat = xQuat * x2Quat;
  240 + var xyQuat = xQuat * y2Quat;
  241 + var xzQuat = xQuat * z2Quat;
  242 + var yyQuat = yQuat * y2Quat;
  243 + var yzQuat = yQuat * z2Quat;
  244 + var zzQuat = zQuat * z2Quat;
  245 + var wxQuat = wQuat * x2Quat;
  246 + var wyQuat = wQuat * y2Quat;
  247 + var wzQuat = wQuat * z2Quat;
  248 + // Step 1: Inlines the construction of a quaternion matrix ('quatMat')
  249 + var quatMat0 = 1 - (yyQuat + zzQuat);
  250 + var quatMat1 = xyQuat + wzQuat;
  251 + var quatMat2 = xzQuat - wyQuat;
  252 + var quatMat4 = xyQuat - wzQuat;
  253 + var quatMat5 = 1 - (xxQuat + zzQuat);
  254 + var quatMat6 = yzQuat + wxQuat;
  255 + var quatMat8 = xzQuat + wyQuat;
  256 + var quatMat9 = yzQuat - wxQuat;
  257 + var quatMat10 = 1 - (xxQuat + yyQuat);
  258 + // quatMat3/7/11/12/13/14 = 0, quatMat15 = 1
  259 +
  260 + // Step 2: Inlines multiplication, takes advantage of constant quatMat cells
  261 + var a00 = matVar[0];
  262 + var a01 = matVar[1];
  263 + var a02 = matVar[2];
  264 + var a03 = matVar[3];
  265 + var a10 = matVar[4];
  266 + var a11 = matVar[5];
  267 + var a12 = matVar[6];
  268 + var a13 = matVar[7];
  269 + var a20 = matVar[8];
  270 + var a21 = matVar[9];
  271 + var a22 = matVar[10];
  272 + var a23 = matVar[11];
  273 +
  274 + var b0 = quatMat0, b1 = quatMat1, b2 = quatMat2;
  275 + matVar[0] = b0 * a00 + b1 * a10 + b2 * a20;
  276 + matVar[1] = b0 * a01 + b1 * a11 + b2 * a21;
  277 + matVar[2] = b0 * a02 + b1 * a12 + b2 * a22;
  278 + matVar[3] = b0 * a03 + b1 * a13 + b2 * a23;
  279 + b0 = quatMat4; b1 = quatMat5; b2 = quatMat6;
  280 + matVar[4] = b0 * a00 + b1 * a10 + b2 * a20;
  281 + matVar[5] = b0 * a01 + b1 * a11 + b2 * a21;
  282 + matVar[6] = b0 * a02 + b1 * a12 + b2 * a22;
  283 + matVar[7] = b0 * a03 + b1 * a13 + b2 * a23;
  284 + b0 = quatMat8; b1 = quatMat9; b2 = quatMat10;
  285 + matVar[8] = b0 * a00 + b1 * a10 + b2 * a20;
  286 + matVar[9] = b0 * a01 + b1 * a11 + b2 * a21;
  287 + matVar[10] = b0 * a02 + b1 * a12 + b2 * a22;
  288 + matVar[11] = b0 * a03 + b1 * a13 + b2 * a23;
  289 + }`
  290 +};
  291 +
  292 +// Optimized version of general operation applications that can be used when
  293 +// the target matrix is known to be the identity matrix.
  294 +var MatrixOpsInitial = {
  295 + transformScale: `function(matVar, opVar) {
  296 + // Scaling matVar known to be identity by opVar
  297 + matVar[0] = opVar[0];
  298 + matVar[1] = 0;
  299 + matVar[2] = 0;
  300 + matVar[3] = 0;
  301 + matVar[4] = 0;
  302 + matVar[5] = opVar[1];
  303 + matVar[6] = 0;
  304 + matVar[7] = 0;
  305 + matVar[8] = 0;
  306 + matVar[9] = 0;
  307 + matVar[10] = opVar[2];
  308 + matVar[11] = 0;
  309 + matVar[12] = 0;
  310 + matVar[13] = 0;
  311 + matVar[14] = 0;
  312 + matVar[15] = 1;
  313 + }`,
  314 +
  315 + transformTranslate: `function(matVar, opVar) {
  316 + // Translating matVar known to be identity by opVar;
  317 + matVar[0] = 1;
  318 + matVar[1] = 0;
  319 + matVar[2] = 0;
  320 + matVar[3] = 0;
  321 + matVar[4] = 0;
  322 + matVar[5] = 1;
  323 + matVar[6] = 0;
  324 + matVar[7] = 0;
  325 + matVar[8] = 0;
  326 + matVar[9] = 0;
  327 + matVar[10] = 1;
  328 + matVar[11] = 0;
  329 + matVar[12] = opVar[0];
  330 + matVar[13] = opVar[1];
  331 + matVar[14] = opVar[2];
  332 + matVar[15] = 1;
  333 + }`,
  334 +
  335 + /**
  336 + * @param {array} matVar Both the input, and the output matrix - assumed to be
  337 + * identity.
  338 + * @param {quaternion specification} q Four element array describing rotation.
  339 + */
  340 + transformRotateRadians: `function(matVar, q) {
  341 +
  342 + // Rotating matVar which is known to be identity by q
  343 + var xQuat = q[0], yQuat = q[1], zQuat = q[2], wQuat = q[3];
  344 + var x2Quat = xQuat + xQuat;
  345 + var y2Quat = yQuat + yQuat;
  346 + var z2Quat = zQuat + zQuat;
  347 + var xxQuat = xQuat * x2Quat;
  348 + var xyQuat = xQuat * y2Quat;
  349 + var xzQuat = xQuat * z2Quat;
  350 + var yyQuat = yQuat * y2Quat;
  351 + var yzQuat = yQuat * z2Quat;
  352 + var zzQuat = zQuat * z2Quat;
  353 + var wxQuat = wQuat * x2Quat;
  354 + var wyQuat = wQuat * y2Quat;
  355 + var wzQuat = wQuat * z2Quat;
  356 + // Step 1: Inlines the construction of a quaternion matrix ('quatMat')
  357 + var quatMat0 = 1 - (yyQuat + zzQuat);
  358 + var quatMat1 = xyQuat + wzQuat;
  359 + var quatMat2 = xzQuat - wyQuat;
  360 + var quatMat4 = xyQuat - wzQuat;
  361 + var quatMat5 = 1 - (xxQuat + zzQuat);
  362 + var quatMat6 = yzQuat + wxQuat;
  363 + var quatMat8 = xzQuat + wyQuat;
  364 + var quatMat9 = yzQuat - wxQuat;
  365 + var quatMat10 = 1 - (xxQuat + yyQuat);
  366 + // quatMat3/7/11/12/13/14 = 0, quatMat15 = 1
  367 +
  368 + // Step 2: Inlines the multiplication with identity matrix.
  369 + var b0 = quatMat0, b1 = quatMat1, b2 = quatMat2;
  370 + matVar[0] = b0;
  371 + matVar[1] = b1;
  372 + matVar[2] = b2;
  373 + matVar[3] = 0;
  374 + b0 = quatMat4; b1 = quatMat5; b2 = quatMat6;
  375 + matVar[4] = b0;
  376 + matVar[5] = b1;
  377 + matVar[6] = b2;
  378 + matVar[7] = 0;
  379 + b0 = quatMat8; b1 = quatMat9; b2 = quatMat10;
  380 + matVar[8] = b0;
  381 + matVar[9] = b1;
  382 + matVar[10] = b2;
  383 + matVar[11] = 0;
  384 + matVar[12] = 0;
  385 + matVar[13] = 0;
  386 + matVar[14] = 0;
  387 + matVar[15] = 1;
  388 + }`
  389 +};
  390 +
  391 +
  392 +var setNextValAndDetectChange = function(name, tmpVarName) {
  393 + return (
  394 + ' if (!didChange) {\n' +
  395 + ' var prevVal = result.' + name + ';\n' +
  396 + ' result.' + name + ' = ' + tmpVarName + ';\n' +
  397 + ' didChange = didChange || (' + tmpVarName + ' !== prevVal);\n' +
  398 + ' } else {\n' +
  399 + ' result.' + name + ' = ' + tmpVarName + ';\n' +
  400 + ' }\n'
  401 + );
  402 +};
  403 +
  404 +var computeNextValLinear = function(anim, from, to, tmpVarName) {
  405 + var hasRoundRatio = 'round' in anim;
  406 + var roundRatio = anim.round;
  407 + var fn = ' ratio = (value - ' + anim.min + ') / ' + (anim.max - anim.min) + ';\n';
  408 + if (!anim.extrapolate) {
  409 + fn += ' ratio = ratio > 1 ? 1 : (ratio < 0 ? 0 : ratio);\n';
  410 + }
  411 +
  412 + var roundOpen = (hasRoundRatio ? 'Math.round(' + roundRatio + ' * ' : '' );
  413 + var roundClose = (hasRoundRatio ? ') / ' + roundRatio : '' );
  414 + fn +=
  415 + ' ' + tmpVarName + ' = ' +
  416 + roundOpen +
  417 + '(' + from + ' * (1 - ratio) + ' + to + ' * ratio)' +
  418 + roundClose + ';\n';
  419 + return fn;
  420 +};
  421 +
  422 +var computeNextValLinearScalar = function(anim) {
  423 + return computeNextValLinear(anim, anim.from, anim.to, 'nextScalarVal');
  424 +};
  425 +
  426 +var computeNextValConstant = function(anim) {
  427 + var constantExpression = JSON.stringify(anim.value);
  428 + return ' nextScalarVal = ' + constantExpression + ';\n';
  429 +};
  430 +
  431 +var computeNextValStep = function(anim) {
  432 + return (
  433 + ' nextScalarVal = value >= ' +
  434 + (anim.threshold + ' ? ' + anim.to + ' : ' + anim.from) + ';\n'
  435 + );
  436 +};
  437 +
  438 +var computeNextValIdentity = function(anim) {
  439 + return ' nextScalarVal = value;\n';
  440 +};
  441 +
  442 +var operationVar = function(name) {
  443 + return name + 'ReuseOp';
  444 +};
  445 +
  446 +var createReusableOperationVars = function(anims) {
  447 + var ret = '';
  448 + for (var name in anims) {
  449 + if (ShouldAllocateReusableOperationVars[name]) {
  450 + ret += 'var ' + operationVar(name) + ' = [];\n';
  451 + }
  452 + }
  453 + return ret;
  454 +};
  455 +
  456 +var newlines = function(statements) {
  457 + return '\n' + statements.join('\n') + '\n';
  458 +};
  459 +
  460 +/**
  461 + * @param {Animation} anim Configuration entry.
  462 + * @param {key} dimension Key to examine in `from`/`to`.
  463 + * @param {number} index Field in operationVar to set.
  464 + * @return {string} Code that sets the operation variable's field.
  465 + */
  466 +var computeNextMatrixOperationField = function(anim, name, dimension, index) {
  467 + var fieldAccess = operationVar(name) + '[' + index + ']';
  468 + if (anim.from[dimension] !== undefined && anim.to[dimension] !== undefined) {
  469 + return ' ' + anim.from[dimension] !== anim.to[dimension] ?
  470 + computeNextValLinear(anim, anim.from[dimension], anim.to[dimension], fieldAccess) :
  471 + fieldAccess + ' = ' + anim.from[dimension] + ';';
  472 + } else {
  473 + return ' ' + fieldAccess + ' = ' + InitialOperationField[name][index] + ';';
  474 + }
  475 +};
  476 +
  477 +var unrolledVars = [];
  478 +for (var varIndex = 0; varIndex < 16; varIndex++) {
  479 + unrolledVars.push('m' + varIndex);
  480 +}
  481 +var setNextMatrixAndDetectChange = function(orderedMatrixOperations) {
  482 + var fn = [
  483 + ' var transform = result.transform !== undefined ? ' +
  484 + 'result.transform : (result.transform = [{ matrix: [] }]);' +
  485 + ' var transformMatrix = transform[0].matrix;'
  486 + ];
  487 + fn.push.apply(
  488 + fn,
  489 + inline(MatrixOps.unroll, ['transformMatrix'].concat(unrolledVars))
  490 + );
  491 + for (var i = 0; i < orderedMatrixOperations.length; i++) {
  492 + var opName = orderedMatrixOperations[i];
  493 + if (i === 0) {
  494 + fn.push.apply(
  495 + fn,
  496 + inline(MatrixOpsInitial[opName], ['transformMatrix', operationVar(opName)])
  497 + );
  498 + } else {
  499 + fn.push.apply(
  500 + fn,
  501 + inline(MatrixOps[opName], ['transformMatrix', operationVar(opName)])
  502 + );
  503 + }
  504 + }
  505 + fn.push.apply(
  506 + fn,
  507 + inline(MatrixOps.matrixDiffers, ['didChange', 'transformMatrix'].concat(unrolledVars))
  508 + );
  509 + return fn;
  510 +};
  511 +
  512 +var InterpolateMatrix = {
  513 + transformTranslate: true,
  514 + transformRotateRadians: true,
  515 + transformScale: true,
  516 +};
  517 +
  518 +var createFunctionString = function(anims) {
  519 + // We must track the order they appear in so transforms are applied in the
  520 + // correct order.
  521 + var orderedMatrixOperations = [];
  522 +
  523 + // Wrapping function allows the final function to contain state (for
  524 + // caching).
  525 + var fn = 'return (function() {\n';
  526 + fn += createReusableOperationVars(anims);
  527 + fn += 'return function(result, value) {\n';
  528 + fn += ' var didChange = false;\n';
  529 + fn += ' var nextScalarVal;\n';
  530 + fn += ' var ratio;\n';
  531 +
  532 + for (var name in anims) {
  533 + var anim = anims[name];
  534 + if (anim.type === 'linear') {
  535 + if (InterpolateMatrix[name]) {
  536 + orderedMatrixOperations.push(name);
  537 + var setOperations = [
  538 + computeNextMatrixOperationField(anim, name, X_DIM, 0),
  539 + computeNextMatrixOperationField(anim, name, Y_DIM, 1),
  540 + computeNextMatrixOperationField(anim, name, Z_DIM, 2)
  541 + ];
  542 + if (name === TRANSFORM_ROTATE_NAME) {
  543 + setOperations.push(computeNextMatrixOperationField(anim, name, W_DIM, 3));
  544 + }
  545 + fn += newlines(setOperations);
  546 + } else {
  547 + fn += computeNextValLinearScalar(anim, 'nextScalarVal');
  548 + fn += setNextValAndDetectChange(name, 'nextScalarVal');
  549 + }
  550 + } else if (anim.type === 'constant') {
  551 + fn += computeNextValConstant(anim);
  552 + fn += setNextValAndDetectChange(name, 'nextScalarVal');
  553 + } else if (anim.type === 'step') {
  554 + fn += computeNextValStep(anim);
  555 + fn += setNextValAndDetectChange(name, 'nextScalarVal');
  556 + } else if (anim.type === 'identity') {
  557 + fn += computeNextValIdentity(anim);
  558 + fn += setNextValAndDetectChange(name, 'nextScalarVal');
  559 + }
  560 + }
  561 + if (orderedMatrixOperations.length) {
  562 + fn += newlines(setNextMatrixAndDetectChange(orderedMatrixOperations));
  563 + }
  564 + fn += ' return didChange;\n';
  565 + fn += '};\n';
  566 + fn += '})()';
  567 + return fn;
  568 +};
  569 +
  570 +/**
  571 + * @param {object} anims Animation configuration by style property name.
  572 + * @return {function} Function accepting style object, that mutates that style
  573 + * object and returns a boolean describing if any update was actually applied.
  574 + */
  575 +var buildStyleInterpolator = function(anims) {
  576 + // Defer compiling this method until we really need it.
  577 + var interpolator = null;
  578 + function lazyStyleInterpolator(result, value) {
  579 + if (interpolator === null) {
  580 + interpolator = Function(createFunctionString(anims))();
  581 + }
  582 + return interpolator(result, value);
  583 + }
  584 + return lazyStyleInterpolator;
  585 +};
  586 +
  587 +module.exports = buildStyleInterpolator;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @typechecks
  26 + */
  27 +'use strict';
  28 +
  29 +/**
  30 + * @param {number} value
  31 + * @param {number} min
  32 + * @param {number} max
  33 + * @return {number}
  34 + */
  35 +function clamp(min, value, max) {
  36 + if (value < min) {
  37 + return min;
  38 + }
  39 + if (value > max) {
  40 + return max;
  41 + }
  42 + return value;
  43 +}
  44 +
  45 +module.exports = clamp;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @flow
  26 + */
  27 +'use strict';
  28 +
  29 +var invariant = require('fbjs/lib/invariant');
  30 +
  31 +import type { StyleObj } from 'StyleSheetTypes';
  32 +
  33 +function getStyle(style) {
  34 + if (style && typeof style === 'number') {
  35 + debugger;
  36 + invariant(false, "Error when using Navigator from react-native-custom-components. Please provide a raw object to `props.sceneStyle` instead of a StyleSheet reference.");
  37 + }
  38 + return style;
  39 +}
  40 +
  41 +function flattenStyle(style: ?StyleObj): ?Object {
  42 + if (!style) {
  43 + return undefined;
  44 + }
  45 + invariant(style !== true, 'style may be false but not true');
  46 +
  47 + if (!Array.isArray(style)) {
  48 + return getStyle(style);
  49 + }
  50 +
  51 + var result = {};
  52 + for (var i = 0, styleLength = style.length; i < styleLength; ++i) {
  53 + var computedStyle = flattenStyle(style[i]);
  54 + if (computedStyle) {
  55 + for (var key in computedStyle) {
  56 + result[key] = computedStyle[key];
  57 + }
  58 + }
  59 + }
  60 + return result;
  61 +}
  62 +
  63 +module.exports = flattenStyle;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +
  27 +/* eslint-disable no-bitwise */
  28 +
  29 +'use strict';
  30 +
  31 +/**
  32 + * Module that provides a function for creating a unique identifier.
  33 + * The returned value does not conform to the GUID standard, but should
  34 + * be globally unique in the context of the browser.
  35 + */
  36 +function guid() {
  37 + return 'f' + (Math.random() * (1 << 30)).toString(16).replace('.', '');
  38 +}
  39 +
  40 +module.exports = guid;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + */
  26 +
  27 +"use strict";
  28 +
  29 +var mergeInto = require('./mergeInto');
  30 +
  31 +/**
  32 + * Shallow merges two structures into a return value, without mutating either.
  33 + *
  34 + * @param {?object} one Optional object with properties to merge from.
  35 + * @param {?object} two Optional object with properties to merge from.
  36 + * @return {object} The shallow extension of one by two.
  37 + */
  38 +var merge = function(one, two) {
  39 + var result = {};
  40 + mergeInto(result, one);
  41 + mergeInto(result, two);
  42 + return result;
  43 +};
  44 +
  45 +module.exports = merge;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * requiresPolyfills: Array.isArray
  26 + */
  27 +
  28 +"use strict";
  29 +
  30 +var invariant = require('fbjs/lib/invariant');
  31 +var keyMirror = require('fbjs/lib/keyMirror');
  32 +
  33 +/**
  34 + * Maximum number of levels to traverse. Will catch circular structures.
  35 + * @const
  36 + */
  37 +var MAX_MERGE_DEPTH = 36;
  38 +
  39 +/**
  40 + * We won't worry about edge cases like new String('x') or new Boolean(true).
  41 + * Functions are considered terminals, and arrays are not.
  42 + * @param {*} o The item/object/value to test.
  43 + * @return {boolean} true iff the argument is a terminal.
  44 + */
  45 +var isTerminal = function(o) {
  46 + return typeof o !== 'object' || o === null;
  47 +};
  48 +
  49 +var mergeHelpers = {
  50 +
  51 + MAX_MERGE_DEPTH: MAX_MERGE_DEPTH,
  52 +
  53 + isTerminal: isTerminal,
  54 +
  55 + /**
  56 + * Converts null/undefined values into empty object.
  57 + *
  58 + * @param {?Object=} arg Argument to be normalized (nullable optional)
  59 + * @return {!Object}
  60 + */
  61 + normalizeMergeArg: function(arg) {
  62 + return arg === undefined || arg === null ? {} : arg;
  63 + },
  64 +
  65 + /**
  66 + * If merging Arrays, a merge strategy *must* be supplied. If not, it is
  67 + * likely the caller's fault. If this function is ever called with anything
  68 + * but `one` and `two` being `Array`s, it is the fault of the merge utilities.
  69 + *
  70 + * @param {*} one Array to merge into.
  71 + * @param {*} two Array to merge from.
  72 + */
  73 + checkMergeArrayArgs: function(one, two) {
  74 + invariant(
  75 + Array.isArray(one) && Array.isArray(two),
  76 + 'Tried to merge arrays, instead got %s and %s.',
  77 + one,
  78 + two
  79 + );
  80 + },
  81 +
  82 + /**
  83 + * @param {*} one Object to merge into.
  84 + * @param {*} two Object to merge from.
  85 + */
  86 + checkMergeObjectArgs: function(one, two) {
  87 + mergeHelpers.checkMergeObjectArg(one);
  88 + mergeHelpers.checkMergeObjectArg(two);
  89 + },
  90 +
  91 + /**
  92 + * @param {*} arg
  93 + */
  94 + checkMergeObjectArg: function(arg) {
  95 + invariant(
  96 + !isTerminal(arg) && !Array.isArray(arg),
  97 + 'Tried to merge an object, instead got %s.',
  98 + arg
  99 + );
  100 + },
  101 +
  102 + /**
  103 + * @param {*} arg
  104 + */
  105 + checkMergeIntoObjectArg: function(arg) {
  106 + invariant(
  107 + (!isTerminal(arg) || typeof arg === 'function') && !Array.isArray(arg),
  108 + 'Tried to merge into an object, instead got %s.',
  109 + arg
  110 + );
  111 + },
  112 +
  113 + /**
  114 + * Checks that a merge was not given a circular object or an object that had
  115 + * too great of depth.
  116 + *
  117 + * @param {number} Level of recursion to validate against maximum.
  118 + */
  119 + checkMergeLevel: function(level) {
  120 + invariant(
  121 + level < MAX_MERGE_DEPTH,
  122 + 'Maximum deep merge depth exceeded. You may be attempting to merge ' +
  123 + 'circular structures in an unsupported way.'
  124 + );
  125 + },
  126 +
  127 + /**
  128 + * Checks that the supplied merge strategy is valid.
  129 + *
  130 + * @param {string} Array merge strategy.
  131 + */
  132 + checkArrayStrategy: function(strategy) {
  133 + invariant(
  134 + strategy === undefined || strategy in mergeHelpers.ArrayStrategies,
  135 + 'You must provide an array strategy to deep merge functions to ' +
  136 + 'instruct the deep merge how to resolve merging two arrays.'
  137 + );
  138 + },
  139 +
  140 + /**
  141 + * Set of possible behaviors of merge algorithms when encountering two Arrays
  142 + * that must be merged together.
  143 + * - `clobber`: The left `Array` is ignored.
  144 + * - `indexByIndex`: The result is achieved by recursively deep merging at
  145 + * each index. (not yet supported.)
  146 + */
  147 + ArrayStrategies: keyMirror({
  148 + Clobber: true,
  149 + IndexByIndex: true
  150 + })
  151 +
  152 +};
  153 +
  154 +module.exports = mergeHelpers;
  1 +/**
  2 + * Copyright (c) 2015, Facebook, Inc. All rights reserved.
  3 + *
  4 + * Facebook, Inc. ("Facebook") owns all right, title and interest, including
  5 + * all intellectual property and other proprietary rights, in and to the React
  6 + * Native CustomComponents software (the "Software"). Subject to your
  7 + * compliance with these terms, you are hereby granted a non-exclusive,
  8 + * worldwide, royalty-free copyright license to (1) use and copy the Software;
  9 + * and (2) reproduce and distribute the Software as part of your own software
  10 + * ("Your Software"). Facebook reserves all rights not expressly granted to
  11 + * you in this license agreement.
  12 + *
  13 + * THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
  14 + * OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
  16 + * IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
  17 + * EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  18 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  19 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  20 + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  21 + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  22 + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
  23 + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24 + *
  25 + * @typechecks static-only
  26 + */
  27 +
  28 +"use strict";
  29 +
  30 +var mergeHelpers = require('./mergeHelpers');
  31 +
  32 +var checkMergeObjectArg = mergeHelpers.checkMergeObjectArg;
  33 +var checkMergeIntoObjectArg = mergeHelpers.checkMergeIntoObjectArg;
  34 +
  35 +/**
  36 + * Shallow merges two structures by mutating the first parameter.
  37 + *
  38 + * @param {object|function} one Object to be merged into.
  39 + * @param {?object} two Optional object with properties to merge from.
  40 + */
  41 +function mergeInto(one, two) {
  42 + checkMergeIntoObjectArg(one);
  43 + if (two != null) {
  44 + checkMergeObjectArg(two);
  45 + for (var key in two) {
  46 + if (!two.hasOwnProperty(key)) {
  47 + continue;
  48 + }
  49 + one[key] = two[key];
  50 + }
  51 + }
  52 +}
  53 +
  54 +module.exports = mergeInto;