<commit_before>/**
 * Copyright (c) 2015, Facebook, Inc.  All rights reserved.
 *
 * Facebook, Inc. ("Facebook") owns all right, title and interest, including
 * all intellectual property and other proprietary rights, in and to the React
 * Native CustomComponents 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.
 *
 * 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.
 *
 */
import React, {
  PropTypes,
} from 'react';
import {
  Platform,
  Animated,
  Image,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import Actions from './Actions';
import _drawerImage from './menu_burger.png';
import _backButtonImage from './back_chevron.png';

const styles = StyleSheet.create({
  title: {
    textAlign: 'center',
    color: '#0A0A0A',
    fontSize: 18,
    width: 240,
    paddingLeft: 60,
    alignSelf: 'center',
  },
  titleWrapper: {
    marginTop: 10,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 20,
      },
      android: {
        top: 5,
      },
    }),
    left: 0,
    right: 0,
  },
  header: {
    backgroundColor: '#EFEFF2',
    paddingTop: 0,
    top: 0,
    ...Platform.select({
      ios: {
        height: 64,
      },
      android: {
        height: 54,
      },
    }),
    right: 0,
    left: 0,
    borderBottomWidth: 0.5,
    borderBottomColor: '#828287',
    position: 'absolute',
  },
  backButton: {
    width: 100,
    height: 37,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 22,
      },
      android: {
        top: 10,
      },
    }),
    left: 2,
    padding: 8,
    flexDirection: 'row',
  },
  rightButton: {
    width: 100,
    height: 37,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 22,
      },
      android: {
        top: 10,
      },
    }),
    right: 2,
    padding: 8,
  },
  leftButton: {
    width: 100,
    height: 54,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 20,
      },
      android: {
        top: 0,
      },
    }),
    left: -30,
    padding: 0,
  },
  barRightButtonText: {
    color: 'rgb(0, 122, 255)',
    textAlign: 'right',
    fontSize: 17,
  },
  barBackButtonText: {
    color: 'rgb(0, 122, 255)',
    textAlign: 'left',
    fontSize: 17,
    paddingLeft: 6,
  },
  barLeftButtonText: {
    color: 'rgb(0, 122, 255)',
    textAlign: 'left',
    fontSize: 17,
  },
  backButtonImage: {
    width: 13,
    height: 21,
  },
  rightButtonIconStyle: {

  },
  defaultImageStyle: {
    height: 54,
    resizeMode: 'contain',
  },
  titleImage: {
    height: 54,
    left: 40,
    resizeMode: 'contain'
  }
});

const propTypes = {
  navigationState: PropTypes.object,
  backButtonImage: Image.propTypes.source,
  wrapBy: PropTypes.any,
  component: PropTypes.any,
  backButtonTextStyle: Text.propTypes.style,
  leftButtonStyle: View.propTypes.style,
  leftButtonIconStyle: Image.propTypes.style,
  getTitle: PropTypes.func,
  titleWrapperStyle: Text.propTypes.style,
  titleStyle: Text.propTypes.style,
  titleOpacity: PropTypes.number,
  titleProps: PropTypes.any,
  position: PropTypes.object,
  navigationBarStyle: View.propTypes.style,
  navigationBarBackgroundImage: Image.propTypes.source,
  renderTitle: PropTypes.any,
};

const contextTypes = {
  drawer: PropTypes.object,
};

const defaultProps = {
  drawerImage: _drawerImage,
  backButtonImage: _backButtonImage,
  titleOpacity: 1,
};

class NavBar extends React.Component {

  constructor(props) {
    super(props);

    this.renderRightButton = this.renderRightButton.bind(this);
    this.renderBackButton = this.renderBackButton.bind(this);
    this.renderLeftButton = this.renderLeftButton.bind(this);
    this.renderTitle = this.renderTitle.bind(this);
  }

  renderBackButton() {
    const state = this.props.navigationState;
    const childState = state.children[state.index];
    const BackButton = (childState.component && childState.component.backButton) || state.backButton
      || childState.backButton;
    const textButtonStyle = [
      styles.barBackButtonText,
      this.props.backButtonTextStyle,
      state.backButtonTextStyle,
      childState.backButtonTextStyle,
    ];
    const style = [
      styles.backButton,
      this.props.leftButtonStyle,
      state.leftButtonStyle,
      childState.leftButtonStyle,
    ];

    if (state.index === 0 && (!state.parentIndex || state.parentIndex === 0)) {
      return null;
    }

    if (BackButton) {
      return (
        <BackButton
          testID="backNavButton"
          textButtonStyle={textButtonStyle}
          {...childState}
          style={style}
        />
      );
    }
    let buttonImage = childState.backButtonImage ||
      state.backButtonImage || this.props.backButtonImage;
    let onPress = childState.onBack || childState.component.onBack;
    if (onPress) {
      onPress = onPress.bind(null, state);
    } else {
      onPress = Actions.pop;
    }

    let text = childState.backTitle ?
      <Text style={textButtonStyle}>
        {childState.backTitle}
      </Text>
      : null;

    return (
      <TouchableOpacity
        testID="backNavButton"
        style={style}
        onPress={onPress}
      >
        {buttonImage && !childState.hideBackImage &&
          <Image
            source={buttonImage}
            style={[
              styles.backButtonImage,
              this.props.leftButtonIconStyle,
              state.barButtonIconStyle,
              state.leftButtonIconStyle,
              childState.leftButtonIconStyle,
            ]}
          />
        }
        {text}
      </TouchableOpacity>
    );
  }

  renderRightButton(navProps) {
    const self = this;
    function tryRender(state, wrapBy) {
      if (!state) {
        return null;
      }
      const rightTitle = state.getRightTitle ? state.getRightTitle(navProps) : state.rightTitle;

      const textStyle = [styles.barRightButtonText, self.props.rightButtonTextStyle,
        state.rightButtonTextStyle];
      const style = [styles.rightButton, self.props.rightButtonStyle, state.rightButtonStyle];
      if (state.rightButton) {
        let Button = state.rightButton;
        if (wrapBy) {
          Button = wrapBy(Button);
        }
        return (
          <Button
            {...self.props}
            {...state}
            key={'rightNavBarBtn'}
            testID="rightNavButton"
            style={style}
            textButtonStyle={textStyle}
          />
        );
      }
      if (state.onRight && (rightTitle || state.rightButtonImage)) {
        const onPress = state.onRight.bind(null, state);
        return (
          <TouchableOpacity
            key={'rightNavBarBtn'}
            testID="rightNavButton"
            style={style}
            onPress={onPress}
          >
            {rightTitle &&
              <Text style={textStyle}>
                {rightTitle}
              </Text>
            }
            {state.rightButtonImage &&
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'flex-end' }}>
                <Image
                  source={state.rightButtonImage}
                  style={state.rightButtonIconStyle}
                />
              </View>
            }
          </TouchableOpacity>
        );
      }
      if ((!!state.onRight ^ !!(typeof(rightTitle) !== 'undefined'
        || typeof(state.rightButtonImage) !== 'undefined'))) {
        console.warn(
          `Both onRight and rightTitle/rightButtonImage
            must be specified for the scene: ${state.name}`
        );
      }
      return null;
    }
    return tryRender(this.props.component, this.props.wrapBy) || tryRender(this.props);
  }

  renderLeftButton(navProps) {
    const self = this;
    const drawer = this.context.drawer;
    function tryRender(state, wrapBy) {
      let onPress = state.onLeft;
      let buttonImage = state.leftButtonImage;
      let menuIcon = state.drawerIcon;
      const style = [styles.leftButton, self.props.leftButtonStyle, state.leftButtonStyle];
      const textStyle = [styles.barLeftButtonText, self.props.leftButtonTextStyle,
        state.leftButtonTextStyle];
      const leftButtonStyle = [styles.defaultImageStyle, state.leftButtonIconStyle];
      const leftTitle = state.getLeftTitle ? state.getLeftTitle(navProps) : state.leftTitle;

      if (state.leftButton) {
        let Button = state.leftButton;
        if (wrapBy) {
          Button = wrapBy(Button);
        }
        return (
          <Button
            {...self.props}
            {...state}
            key={'leftNavBarBtn'}
            testID="leftNavButton"
            style={style}
            textStyle={textStyle}
          />
        );
      }

      if (!onPress && !!drawer && typeof drawer.toggle === 'function') {
        buttonImage = state.drawerImage;
        if (buttonImage || menuIcon) {
          onPress = drawer.toggle;
        }
        if (!menuIcon) {
          menuIcon = (
            <Image
              source={buttonImage}
              style={leftButtonStyle}
            />
          );
        }
      }

      if (onPress && (leftTitle || buttonImage)) {
        onPress = onPress.bind(null, state);
        return (
          <TouchableOpacity
            key={'leftNavBarBtn'}
            testID="leftNavButton"
            style={style}
            onPress={onPress}
          >
            {leftTitle &&
              <Text style={textStyle}>
                {leftTitle}
              </Text>
            }
            {buttonImage &&
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'flex-start' }}>
                {menuIcon || <Image
                  source={buttonImage}
                  style={state.leftButtonIconStyle || styles.defaultImageStyle}
                />
                }
              </View>
            }
          </TouchableOpacity>
        );
      }
      if ((!!state.onLeft ^ !!(leftTitle || buttonImage))) {
        console.warn(
          `Both onLeft and leftTitle/leftButtonImage
            must be specified for the scene: ${state.name}`
        );
      }
      return null;
    }
    return tryRender(this.props.component, this.props.wrapBy) || tryRender(this.props);
  }

  renderTitle(childState, index:number) {
    let title = this.props.getTitle ? this.props.getTitle(childState) : childState.title;
    if (title === undefined && childState.component && childState.component.title) {
      title = childState.component.title;
    }
    if (typeof(title) === 'function') {
      title = title(childState);
    }
    return (
      <Animated.View
        key={childState.key}
        style={[
          styles.titleWrapper,
          this.props.titleWrapperStyle,
        ]}
      >
        <Animated.Text
          lineBreakMode="tail"
          numberOfLines={1}
          {...this.props.titleProps}
          style={[
            styles.title,
            this.props.titleStyle,
            this.props.navigationState.titleStyle,
            childState.titleStyle,
            {
              opacity: this.props.position.interpolate({
                inputRange: [index - 1, index, index + 1],
                outputRange: [0, this.props.titleOpacity, 0],
              }),
              left: this.props.position.interpolate({
                inputRange: [index - 1, index + 1],
                outputRange: [200, -200],
              }),
              right: this.props.position.interpolate({
                inputRange: [index - 1, index + 1],
                outputRange: [-200, 200],
              }),
            },
          ]}
        >
          {title}
        </Animated.Text>
      </Animated.View>
    );
  }

  render() {
    let state = this.props.navigationState;
    let selected = state.children[state.index];
    while (selected.hasOwnProperty('children')) {
      state = selected;
      selected = selected.children[selected.index];
    }
    const navProps = { ...this.props, ...selected };

    const wrapByStyle = (component, wrapStyle) => {
      if (!component) { return null; }
      return (props) => <View style={wrapStyle}>{component(props)}</View>;
    };

    const leftButtonStyle = [styles.leftButton, { alignItems: 'flex-start' }];
    const rightButtonStyle = [styles.rightButton, { alignItems: 'flex-end' }];

    const renderLeftButton = wrapByStyle(selected.renderLeftButton, leftButtonStyle) ||
      wrapByStyle(selected.component.renderLeftButton, leftButtonStyle) ||
      this.renderLeftButton;
    const renderRightButton = wrapByStyle(selected.renderRightButton, rightButtonStyle) ||
      wrapByStyle(selected.component.renderRightButton, rightButtonStyle) ||
      this.renderRightButton;
    const renderBackButton = wrapByStyle(selected.renderBackButton, leftButtonStyle) ||
      wrapByStyle(selected.component.renderBackButton, leftButtonStyle) ||
      this.renderBackButton;
    const renderTitle = selected.renderTitle ||
      selected.component.renderTitle ||
      this.props.renderTitle;
    const navigationBarBackgroundImage = this.props.navigationBarBackgroundImage ||
      state.navigationBarBackgroundImage;
    const contents = (
      <View>
        {this.props.titleImage && <Image source={this.props.titleImage} style={styles.titleImage} />}
        {renderTitle ? renderTitle(navProps) : state.children.map(this.renderTitle, this)}
        {renderBackButton(navProps) || renderLeftButton(navProps)}
        {renderRightButton(navProps)}
      </View>
    );
    return (
      <Animated.View
        style={[
          styles.header,
          this.props.navigationBarStyle,
          state.navigationBarStyle,
          selected.navigationBarStyle,
        ]}
      >
        {navigationBarBackgroundImage ? (
          <Image source={navigationBarBackgroundImage}>
            {contents}
          </Image>
        ) : contents}
      </Animated.View>
    );
  }
}

NavBar.propTypes = propTypes;
NavBar.contextTypes = contextTypes;
NavBar.defaultProps = defaultProps;

export default NavBar;
<commit_msg>Update NavBar.js<commit_after>/**
 * Copyright (c) 2015, Facebook, Inc.  All rights reserved.
 *
 * Facebook, Inc. ("Facebook") owns all right, title and interest, including
 * all intellectual property and other proprietary rights, in and to the React
 * Native CustomComponents 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.
 *
 * 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.
 *
 */
import React, {
  PropTypes,
} from 'react';
import {
  Platform,
  Animated,
  Image,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
  Dimensions
} from 'react-native';
import Actions from './Actions';
import _drawerImage from './menu_burger.png';
import _backButtonImage from './back_chevron.png';
const styles = StyleSheet.create({
  title: {
    textAlign: 'center',
    color: '#0A0A0A',
    fontSize: 18,
    ...Platform.select({
      android: {
        width: 240,
        paddingLeft: 60,
      },
    }),
    alignSelf: 'center',
  },
  titleWrapper: {
    marginTop: 10,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 20,
      },
      android: {
        top: 5,
      },
    }),
    ...Platform.select({
      ios: {
        left: Dimensions.get('window').width/2.5,
      },
      android: {
        left: 0,
      },
    }),
    right: 0,
  },
  header: {
    backgroundColor: '#EFEFF2',
    paddingTop: 0,
    top: 0,
    ...Platform.select({
      ios: {
        height: 74,
      },
      android: {
        height: 54,
      },
    }),
    right: 0,
    left: 0,
    borderBottomWidth: 0.5,
    borderBottomColor: '#828287',
    position: 'absolute',
  },
  backButton: {
    width: 100,
    height: 37,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 22,
      },
      android: {
        top: 10,
      },
    }),
    left: 2,
    padding: 8,
    flexDirection: 'row',
  },
  rightButton: {
    width: 100,
    height: 37,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 22,
      },
      android: {
        top: 10,
      },
    }),
    right: 2,
    padding: 8,
  },
  leftButton: {
    width: 100,
    height: 54,
    position: 'absolute',
    ...Platform.select({
      ios: {
        top: 20,
      },
      android: {
        top: 0,
      },
    }),
    left: -30,
    padding: 0,
  },
  barRightButtonText: {
    color: 'rgb(0, 122, 255)',
    textAlign: 'right',
    fontSize: 17,
  },
  barBackButtonText: {
    color: 'rgb(0, 122, 255)',
    textAlign: 'left',
    fontSize: 17,
    paddingLeft: 6,
  },
  barLeftButtonText: {
    color: 'rgb(0, 122, 255)',
    textAlign: 'left',
    fontSize: 17,
  },
  backButtonImage: {
    width: 13,
    height: 21,
  },
  rightButtonIconStyle: {

  },
  defaultImageStyle: {
    height: 54,
    resizeMode: 'contain',
  },
  titleImage: {
    height: 54,
    left: 40,
    resizeMode: 'contain',
    ...Platform.select({
      ios: {
        top: 20,
      },
      android: {
        top: 0,
      },
    })
  }
});

const propTypes = {
  navigationState: PropTypes.object,
  backButtonImage: Image.propTypes.source,
  wrapBy: PropTypes.any,
  component: PropTypes.any,
  backButtonTextStyle: Text.propTypes.style,
  leftButtonStyle: View.propTypes.style,
  leftButtonIconStyle: Image.propTypes.style,
  getTitle: PropTypes.func,
  titleWrapperStyle: Text.propTypes.style,
  titleStyle: Text.propTypes.style,
  titleOpacity: PropTypes.number,
  titleProps: PropTypes.any,
  position: PropTypes.object,
  navigationBarStyle: View.propTypes.style,
  navigationBarBackgroundImage: Image.propTypes.source,
  renderTitle: PropTypes.any,
};

const contextTypes = {
  drawer: PropTypes.object,
};

const defaultProps = {
  drawerImage: _drawerImage,
  backButtonImage: _backButtonImage,
  titleOpacity: 1,
};

class NavBar extends React.Component {

  constructor(props) {
    super(props);

    this.renderRightButton = this.renderRightButton.bind(this);
    this.renderBackButton = this.renderBackButton.bind(this);
    this.renderLeftButton = this.renderLeftButton.bind(this);
    this.renderTitle = this.renderTitle.bind(this);
  }

  renderBackButton() {
    const state = this.props.navigationState;
    const childState = state.children[state.index];
    const BackButton = (childState.component && childState.component.backButton) || state.backButton
      || childState.backButton;
    const textButtonStyle = [
      styles.barBackButtonText,
      this.props.backButtonTextStyle,
      state.backButtonTextStyle,
      childState.backButtonTextStyle,
    ];
    const style = [
      styles.backButton,
      this.props.leftButtonStyle,
      state.leftButtonStyle,
      childState.leftButtonStyle,
    ];

    if (state.index === 0 && (!state.parentIndex || state.parentIndex === 0)) {
      return null;
    }

    if (BackButton) {
      return (
        <BackButton
          testID="backNavButton"
          textButtonStyle={textButtonStyle}
          {...childState}
          style={style}
        />
      );
    }
    let buttonImage = childState.backButtonImage ||
      state.backButtonImage || this.props.backButtonImage;
    let onPress = childState.onBack || childState.component.onBack;
    if (onPress) {
      onPress = onPress.bind(null, state);
    } else {
      onPress = Actions.pop;
    }

    let text = childState.backTitle ?
      <Text style={textButtonStyle}>
        {childState.backTitle}
      </Text>
      : null;

    return (
      <TouchableOpacity
        testID="backNavButton"
        style={style}
        onPress={onPress}
      >
        {buttonImage && !childState.hideBackImage &&
          <Image
            source={buttonImage}
            style={[
              styles.backButtonImage,
              this.props.leftButtonIconStyle,
              state.barButtonIconStyle,
              state.leftButtonIconStyle,
              childState.leftButtonIconStyle,
            ]}
          />
        }
        {text}
      </TouchableOpacity>
    );
  }

  renderRightButton(navProps) {
    const self = this;
    function tryRender(state, wrapBy) {
      if (!state) {
        return null;
      }
      const rightTitle = state.getRightTitle ? state.getRightTitle(navProps) : state.rightTitle;

      const textStyle = [styles.barRightButtonText, self.props.rightButtonTextStyle,
        state.rightButtonTextStyle];
      const style = [styles.rightButton, self.props.rightButtonStyle, state.rightButtonStyle];
      if (state.rightButton) {
        let Button = state.rightButton;
        if (wrapBy) {
          Button = wrapBy(Button);
        }
        return (
          <Button
            {...self.props}
            {...state}
            key={'rightNavBarBtn'}
            testID="rightNavButton"
            style={style}
            textButtonStyle={textStyle}
          />
        );
      }
      if (state.onRight && (rightTitle || state.rightButtonImage)) {
        const onPress = state.onRight.bind(null, state);
        return (
          <TouchableOpacity
            key={'rightNavBarBtn'}
            testID="rightNavButton"
            style={style}
            onPress={onPress}
          >
            {rightTitle &&
              <Text style={textStyle}>
                {rightTitle}
              </Text>
            }
            {state.rightButtonImage &&
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'flex-end' }}>
                <Image
                  source={state.rightButtonImage}
                  style={state.rightButtonIconStyle}
                />
              </View>
            }
          </TouchableOpacity>
        );
      }
      if ((!!state.onRight ^ !!(typeof(rightTitle) !== 'undefined'
        || typeof(state.rightButtonImage) !== 'undefined'))) {
        console.warn(
          `Both onRight and rightTitle/rightButtonImage
            must be specified for the scene: ${state.name}`
        );
      }
      return null;
    }
    return tryRender(this.props.component, this.props.wrapBy) || tryRender(this.props);
  }

  renderLeftButton(navProps) {
    const self = this;
    const drawer = this.context.drawer;
    function tryRender(state, wrapBy) {
      let onPress = state.onLeft;
      let buttonImage = state.leftButtonImage;
      let menuIcon = state.drawerIcon;
      const style = [styles.leftButton, self.props.leftButtonStyle, state.leftButtonStyle];
      const textStyle = [styles.barLeftButtonText, self.props.leftButtonTextStyle,
        state.leftButtonTextStyle];
      const leftButtonStyle = [styles.defaultImageStyle, state.leftButtonIconStyle];
      const leftTitle = state.getLeftTitle ? state.getLeftTitle(navProps) : state.leftTitle;

      if (state.leftButton) {
        let Button = state.leftButton;
        if (wrapBy) {
          Button = wrapBy(Button);
        }
        return (
          <Button
            {...self.props}
            {...state}
            key={'leftNavBarBtn'}
            testID="leftNavButton"
            style={style}
            textStyle={textStyle}
          />
        );
      }

      if (!onPress && !!drawer && typeof drawer.toggle === 'function') {
        buttonImage = state.drawerImage;
        if (buttonImage || menuIcon) {
          onPress = drawer.toggle;
        }
        if (!menuIcon) {
          menuIcon = (
            <Image
              source={buttonImage}
              style={leftButtonStyle}
            />
          );
        }
      }

      if (onPress && (leftTitle || buttonImage)) {
        onPress = onPress.bind(null, state);
        return (
          <TouchableOpacity
            key={'leftNavBarBtn'}
            testID="leftNavButton"
            style={style}
            onPress={onPress}
          >
            {leftTitle &&
              <Text style={textStyle}>
                {leftTitle}
              </Text>
            }
            {buttonImage &&
              <View style={{ flex: 1, justifyContent: 'center', alignItems: 'flex-start' }}>
                {menuIcon || <Image
                  source={buttonImage}
                  style={state.leftButtonIconStyle || styles.defaultImageStyle}
                />
                }
              </View>
            }
          </TouchableOpacity>
        );
      }
      if ((!!state.onLeft ^ !!(leftTitle || buttonImage))) {
        console.warn(
          `Both onLeft and leftTitle/leftButtonImage
            must be specified for the scene: ${state.name}`
        );
      }
      return null;
    }
    return tryRender(this.props.component, this.props.wrapBy) || tryRender(this.props);
  }

  renderTitle(childState, index:number) {
    let title = this.props.getTitle ? this.props.getTitle(childState) : childState.title;
    if (title === undefined && childState.component && childState.component.title) {
      title = childState.component.title;
    }
    if (typeof(title) === 'function') {
      title = title(childState);
    }
    return (
      <Animated.View
        key={childState.key}
        style={[
          styles.titleWrapper,
          this.props.titleWrapperStyle,
        ]}
      >
        <Animated.Text
          lineBreakMode="tail"
          numberOfLines={1}
          {...this.props.titleProps}
          style={[
            styles.title,
            this.props.titleStyle,
            this.props.navigationState.titleStyle,
            childState.titleStyle,
            {
              opacity: this.props.position.interpolate({
                inputRange: [index - 1, index, index + 1],
                outputRange: [0, this.props.titleOpacity, 0],
              }),
              left: this.props.position.interpolate({
                inputRange: [index - 1, index + 1],
                outputRange: [200, -200],
              }),
              right: this.props.position.interpolate({
                inputRange: [index - 1, index + 1],
                outputRange: [-200, 200],
              }),
            },
          ]}
        >
          {title}
        </Animated.Text>
      </Animated.View>
    );
  }

  render() {
    let state = this.props.navigationState;
    let selected = state.children[state.index];
    while (selected.hasOwnProperty('children')) {
      state = selected;
      selected = selected.children[selected.index];
    }
    const navProps = { ...this.props, ...selected };

    const wrapByStyle = (component, wrapStyle) => {
      if (!component) { return null; }
      return (props) => <View style={wrapStyle}>{component(props)}</View>;
    };

    const leftButtonStyle = [styles.leftButton, { alignItems: 'flex-start' }];
    const rightButtonStyle = [styles.rightButton, { alignItems: 'flex-end' }];

    const renderLeftButton = wrapByStyle(selected.renderLeftButton, leftButtonStyle) ||
      wrapByStyle(selected.component.renderLeftButton, leftButtonStyle) ||
      this.renderLeftButton;
    const renderRightButton = wrapByStyle(selected.renderRightButton, rightButtonStyle) ||
      wrapByStyle(selected.component.renderRightButton, rightButtonStyle) ||
      this.renderRightButton;
    const renderBackButton = wrapByStyle(selected.renderBackButton, leftButtonStyle) ||
      wrapByStyle(selected.component.renderBackButton, leftButtonStyle) ||
      this.renderBackButton;
    const renderTitle = selected.renderTitle ||
      selected.component.renderTitle ||
      this.props.renderTitle;
    const navigationBarBackgroundImage = this.props.navigationBarBackgroundImage ||
      state.navigationBarBackgroundImage;
    const contents = (
      <View>
        {this.props.titleImage && <Image source={this.props.titleImage} style={styles.titleImage} />}
        {renderTitle ? renderTitle(navProps) : state.children.map(this.renderTitle, this)}
        {renderBackButton(navProps) || renderLeftButton(navProps)}
        {renderRightButton(navProps)}
      </View>
    );
    return (
      <Animated.View
        style={[
          styles.header,
          this.props.navigationBarStyle,
          state.navigationBarStyle,
          selected.navigationBarStyle,
        ]}
      >
        {navigationBarBackgroundImage ? (
          <Image source={navigationBarBackgroundImage}>
            {contents}
          </Image>
        ) : contents}
      </Animated.View>
    );
  }
}

NavBar.propTypes = propTypes;
NavBar.contextTypes = contextTypes;
NavBar.defaultProps = defaultProps;

export default NavBar;
<|endoftext|>


<commit_before>/**
 * Grunt global vars
 * Many of the Grunt tasks use these vars
 */

module.exports = {
	src : "./_grunt-configs/*.js",

	srcDir  : './assets/src',  // <%=config.srcDir%>
	distDir : './assets/dist', // <%=config.distDir%>
	tempDir : './assets/temp', // <%=config.tempDir%>

	css : {
		scssDir  : '<%=config.srcDir%>/scss', // <%=config.css.scssDir%>
		distDir  : '<%=config.distDir%>/css', // <%=config.css.distDir%>

		// Renaming this changes the name of the generated CSS file
		// Make sure you update your template file
		distFile : 'kickoff', // <%=config.css.srcFile%>

		// We are supporting the last 2 browsers, any browsers with >5% market share,
		// and ensuring we support IE8+ with prefixes
		autoprefixer : ['> 5%', 'last 2 versions', 'firefox > 3.6', 'ie > 7'] // <%=config.css.autoprefixer%>
	},

	js : {
		distDir  : '<%=config.distDir%>/js/', // <%=config.js.distDir%>
		distFile : 'app.min.js',              // <%=config.js.distFile%>

		// The files in this array will be concatinated and minified by our build
		// Remove any files that you don't want, & add any that you need

		// <%=config.js.fileList%>
		fileList : [
			// if you would like to remove jQuery from your concatenated JS, comment out the line below
			'bower_modules/jquery/dist/jquery.js',

			// if you would like some basic JS shims (when not using jQuery),
			// uncomment the line below to compile Shimly output
			//'<%=config.srcDir%>/js/helpers/shims.js',

			'<%=config.srcDir%>/js/helpers/console.js',
			'bower_modules/trak/dist/trak.js',
			'bower_modules/swiftclick/js/libs/swiftclick.js',
			'bower_modules/cookies-js/dist/cookies.js',

			'<%=config.srcDir%>/js/script.js'
		]
	},

	img : {
		dir          : '<%=config.srcDir%>/img',      // <%=config.img.dir%>
		grunticonDir : '<%=config.srcDir%>/grunticon' // <%=config.img.grunticonDir%>
	},

	testing: {
		visual : {
			sizes: [ '600', '1000', '1200' ], // <%=config.testing.visual.sizes%>

			// <%=config.testing.visual.urls%>
			urls : [
				'http://localhost:3000',
				'http://localhost:3000/_docs/',
				'http://localhost:3000/_docs/styleguide.html'
			]
		}
	}
};
<commit_msg>add help and comments to Grunt config file<commit_after>/**
 * Global Grunt vars
 * Many of the Grunt tasks use these vars. Change as much as you like :)
 */

module.exports = {
	src : "./_grunt-configs/*.js", // This directory. Has all the Grunt tasks grouped into specific js files

	srcDir  : './assets/src',  // <%=config.srcDir%>
	distDir : './assets/dist', // <%=config.distDir%>
	tempDir : './assets/temp', // <%=config.tempDir%>

	// CSS-related Grunt vars
	css : {
		scssDir  : '<%=config.srcDir%>/scss', // <%=config.css.scssDir%>
		distDir  : '<%=config.distDir%>/css', // <%=config.css.distDir%>

		// Renaming this changes the name of the generated CSS file
		// Make sure you update your template file
		distFile : 'kickoff', // <%=config.css.srcFile%>

		// We are supporting the last 2 browsers, any browsers with >5% market share,
		// and ensuring we support IE8+ with prefixes
		autoprefixer : ['> 5%', 'last 2 versions', 'firefox > 3.6', 'ie > 7'] // <%=config.css.autoprefixer%>
	},

	// Javascript-related Grunt vars
	js : {
		distDir  : '<%=config.distDir%>/js/', // <%=config.js.distDir%>

		// Renaming this changes the name of the generated JS file
		// Make sure you update your template file
		distFile : 'script.js', // <%=config.js.distFile%>

		// The files in this array will be concatinated and minified by our build
		// Remove any files that you don't want, & add any that you need

		// <%=config.js.fileList%>
		fileList : [
			// if you would like to remove jQuery from your concatenated JS, comment out the line below
			'bower_modules/jquery/dist/jquery.js',

			// if you would like some basic JS shims (when not using jQuery),
			// uncomment the line below to compile Shimly output
			//'<%=config.srcDir%>/js/helpers/shims.js',

			'<%=config.srcDir%>/js/helpers/console.js',
			'bower_modules/trak/dist/trak.js',
			'bower_modules/swiftclick/js/libs/swiftclick.js',
			'bower_modules/cookies-js/dist/cookies.js',

			'<%=config.srcDir%>/js/script.js'
		]
	},

	// Image-related Grunt vars
	img : {
		dir          : '<%=config.srcDir%>/img',      // <%=config.img.dir%>
		grunticonDir : '<%=config.srcDir%>/grunticon' // <%=config.img.grunticonDir%>
	},

	// Testing-related Grunt vars
	// Add any other test vars in here
	testing: {

		// Used by Photobox at the moment
		// http://tmwagency.github.io/kickoff/learn/grunt.html#task-photobox
		visual : {
			sizes: [ '600', '1000', '1200' ], // <%=config.testing.visual.sizes%>

			// <%=config.testing.visual.urls%>
			urls : [
				'http://localhost:3000',
				'http://localhost:3000/_docs/',
				'http://localhost:3000/_docs/styleguide.html'
			]
		}
	}
};
<|endoftext|>


<commit_before>module.exports = function (grunt) {
    'use strict';
    // load all grunt tasks
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
    // show elapsed time at the end
    require('time-grunt')(grunt);

    // configurables
    var config = {
        app: 'app',
        build: 'build',
        sassImagePath: '../images'
    };

    config.jshintInclude = [
        '<%= config.app %>/scripts/{,**/}*.js',
        'Gruntfile.js'
    ];
    config.jshintExclude = [
        '<%= config.app %>/scripts/bower/{,**/}*.js',
    ];

    grunt.initConfig({
        config: config,
        jshint: {
            options: {
                jshintrc: true,
                reporter: require('jshint-stylish'),
                files: {
                    src: config.jshintInclude,
                    ignores: config.jshintExclude
                }
            }
        },
        connect: {
            server: {
                options: {
                    hostname: '127.0.0.1',
                    open: true,
                    livereload: true,
                    port: 1234,
                    keepalive: true,
                    base: '<%= config.app %>'
                }
            }
        },
        watch: {
            js: {
                files: [
                    '<%= config.app %>/scripts/{,**/}*.js'
                ],
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            css: {
                files: [
                    '<%= config.app %>/css/*.css'
                ],
                options: {
                    livereload: true
                }
            },
            sass: {
                files: [
                    '<%= config.app %>/sass/{,**/}*.{scss,sass}'
                ],
                tasks: ['sass']
            },
            configFiles: {
                files: ['Gruntfile.js'],
                options: {
                    reload: true
                },
                tasks: ['jshint', 'sass']
            },
            handlebars: {
                files: [
                    '<%= config.app %>/scripts/apps/**/templates/{,**/}*.hbs'
                ],
                options: {
                    livereload: true
                }
            }
        },

        sass: {
            options: {
                outputStyle: 'compressed',
                sourceMap: true,
                imagePath: config.sassImagePath,
                includePaths: [
                    './node_modules/sass-list-maps'
                ]
            },
            dev: {
                files: {
                    '<%= config.app %>/css/styles.css': '<%= config.app %>/sass/styles.scss',
                }
            }
        },

        clean: {
            build: [
                '.tmp',
                '<%= config.build %>/css',
                '<%= config.build %>/fonts',
                '<%= config.build %>/images',
                '<%= config.build %>/scripts'
            ]
        },

        // require
        requirejs: {
            build: {
                // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
                options: {
                    baseUrl: '<%= config.app %>/scripts',
                    mainConfigFile: '<%= config.app %>/scripts/init.js',
                    name: 'main',
                    optimize: 'uglify2',
                    deps: ['almond'],
                    out: '<%= config.build %>/scripts/main.js',
                    preserveLicenseComments: false,
                    useStrict: false,
                    generateSourceMaps: true
                }
            }
        },

        copy: {
            build: {
                files: [{
                    cwd: '<%= config.app %>',
                    dest: '<%= config.build %>',
                    src: 'images/*',
                    expand: true
                }, {
                    cwd: '<%= config.app %>/scripts/bower/font-awesome/fonts',
                    dest: '<%= config.build %>/fonts',
                    src: '**/*',
                    expand: true
                }, {
                    src: '<%= config.app %>/favicon.ico',
                    dest: '<%= config.build %>/favicon.ico'
                }]
            }
        },

        concurrent: {
            dev: {
                tasks: ['watch', 'connect'],
                options: {
                    logConcurrentOutput: true
                }
            }
        }
    });

    grunt.registerTask('default', [
        'sass',
        'concurrent:dev'
    ]);

    grunt.registerTask('build', [
        'clean',
        'sass',
        'copy',
        'requirejs'
    ]);
};<commit_msg>chore(gruntfile): change `build` folder to `dist`<commit_after>module.exports = function (grunt) {
    'use strict';
    // load all grunt tasks
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
    // show elapsed time at the end
    require('time-grunt')(grunt);

    // configurables
    var config = {
        app: 'app',
        build: 'dist',
        sassImagePath: '../images'
    };

    config.jshintInclude = [
        '<%= config.app %>/scripts/{,**/}*.js',
        'Gruntfile.js'
    ];
    config.jshintExclude = [
        '<%= config.app %>/scripts/bower/{,**/}*.js',
    ];

    grunt.initConfig({
        config: config,
        jshint: {
            options: {
                jshintrc: true,
                reporter: require('jshint-stylish'),
                files: {
                    src: config.jshintInclude,
                    ignores: config.jshintExclude
                }
            }
        },
        connect: {
            server: {
                options: {
                    hostname: '127.0.0.1',
                    open: true,
                    livereload: true,
                    port: 1234,
                    keepalive: true,
                    base: '<%= config.app %>'
                }
            }
        },
        watch: {
            js: {
                files: [
                    '<%= config.app %>/scripts/{,**/}*.js'
                ],
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            css: {
                files: [
                    '<%= config.app %>/css/*.css'
                ],
                options: {
                    livereload: true
                }
            },
            sass: {
                files: [
                    '<%= config.app %>/sass/{,**/}*.{scss,sass}'
                ],
                tasks: ['sass']
            },
            configFiles: {
                files: ['Gruntfile.js'],
                options: {
                    reload: true
                },
                tasks: ['jshint', 'sass']
            },
            handlebars: {
                files: [
                    '<%= config.app %>/scripts/apps/**/templates/{,**/}*.hbs'
                ],
                options: {
                    livereload: true
                }
            }
        },

        sass: {
            options: {
                outputStyle: 'compressed',
                sourceMap: true,
                imagePath: config.sassImagePath,
                includePaths: [
                    './node_modules/sass-list-maps'
                ]
            },
            dev: {
                files: {
                    '<%= config.app %>/css/styles.css': '<%= config.app %>/sass/styles.scss',
                }
            }
        },

        clean: {
            build: [
                '.tmp',
                '<%= config.build %>/css',
                '<%= config.build %>/fonts',
                '<%= config.build %>/images',
                '<%= config.build %>/scripts'
            ]
        },

        // require
        requirejs: {
            build: {
                // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
                options: {
                    baseUrl: '<%= config.app %>/scripts',
                    mainConfigFile: '<%= config.app %>/scripts/init.js',
                    name: 'main',
                    optimize: 'uglify2',
                    deps: ['almond'],
                    out: '<%= config.build %>/scripts/main.js',
                    preserveLicenseComments: false,
                    useStrict: false,
                    generateSourceMaps: true
                }
            }
        },

        copy: {
            build: {
                files: [{
                    cwd: '<%= config.app %>',
                    dest: '<%= config.build %>',
                    src: 'images/*',
                    expand: true
                }, {
                    cwd: '<%= config.app %>/scripts/bower/font-awesome/fonts',
                    dest: '<%= config.build %>/fonts',
                    src: '**/*',
                    expand: true
                }, {
                    src: '<%= config.app %>/favicon.ico',
                    dest: '<%= config.build %>/favicon.ico'
                }]
            }
        },

        concurrent: {
            dev: {
                tasks: ['watch', 'connect'],
                options: {
                    logConcurrentOutput: true
                }
            }
        }
    });

    grunt.registerTask('default', [
        'sass',
        'concurrent:dev'
    ]);

    grunt.registerTask('build', [
        'clean',
        'sass',
        'copy',
        'requirejs'
    ]);
};<|endoftext|>


<commit_before><commit_msg>updated mmtf lib<commit_after><|endoftext|>


<commit_before>// Tables.
exports.tables = {
  filter  : 'filter',     // The default table (if no -t option is passed). It contains the built-in chains INPUT (for packets destined to local sockets), FORWARD (for packets being routed through the box), and OUTPUT (for locally-generated packets).
  nat     : 'nat',        // Consulted when a packet that creates a new connection is encountered.  It consists of three built-ins: PREROUTING (for altering packets as soon as they come  in), OUTPUT (for altering locally-generated packets before routing), and POSTROUTING (for altering packets as they are about to go out).
  mangle  : 'mangle',     // Used for specialized packet alteration.  Until kernel 2.4.17 it had two built-in chains: PREROUTING (for altering incoming packets before routing) and OUTPUT (for altering locally-generated packets before routing).  Since kernel 2.4.18, three other built-in chains are also supported: INPUT (for packets coming into the box  itself),  FORWARD (for altering packets being routed through the box), and POSTROUTING (for altering packets as they are about to go out).
  raw     : 'raw',        // Used mainly for configuring exemptions from connection tracking in combination with the NOTRACK target.  It registers at the netfilter hooks with higher priority and is thus called before ip_conntrack, or any other IP tables.  It provides the following built-in chains: PREROUTING (for packets arriving via any network  interface)  OUTPUT (for packets generated by local processes)
  security: 'security'    // Used for Mandatory Access Control (MAC) networking rules, such as those enabled by the SECMARK and CONNSECMARK targets.  Mandatory Access Control is implemented by Linux Security Modules such as SELinux.  The security table is called after the filter table, allowing any Discretionary Access Control (DAC) rules in the  filter  table  to take effect before MAC rules.  This table provides the following built-in chains: INPUT (for packets coming into the box itself), OUTPUT (for altering locally-generated packets before routing), and FORWARD (for altering packets being routed through the box).
};

/**
 * Process common rule specs for `append`, `check`, `delete`, `insert` and `replace`.
 *
 * @param options
 * @returns {Array}
 */
function manage_arg(property, flag, args) {
  if (property != null) {
    var val = String(property);
    var index = val.indexOf('!');
    if (index !== -1) {
      args.push('!', flag, val.slice(index));
    }
    else {
      args.push(flag, val);
    }
  }
}
function hasOwnProperty(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}
exports.processCommonRuleSpecs = function (options) {
  var args = [];

  if (options.comment != null) {
    args.push('-m', 'comment', '--comment', String(options.comment).slice(0,255).replace(/[^A-Za-z_]/g,'\\$&'));
  }

  manage_arg(options.protocol, '--protocol', args);
  manage_arg(options.source, '--source', args);
  manage_arg(options['in-interface'], '--in-interface', args);
  manage_arg(options['out-interface'], '--out-interface', args);

  if (options.fragment != null) {
    var val = String(options.fragment);
    var index = val.indexOf('!');
    if (index !== -1) {
      args.push('!', '--fragment');
    }
    else {
      args.push('--fragment');
    }
  }

  /*
   * Matches processing.
   */
  if (options.matches != null) {
    for (var match in options.matches) {
      if (hasOwnProperty(options.matches, match)) {
        // Initial match load.
        args.push('--match', match);

        // Specific match options.
        var this_match = options.matches[match];

        if (this_match != null && typeof this_match === 'object') {
          for (var match_option in this_match) {
            if (hasOwnProperty(this_match, match_option)) {
              manage_arg(this_match[match_option], '--' + match_option, args);
            }
          }
        }
      }
    }
  }

  if (options.goto != null) {
    args.push('--goto', String(options.goto));
  }

  if (options.jump != null) {
    args.push('--jump', String(options.jump));

    // Target extension options.
    if (options.target_options != null && typeof options.target_options === 'object') {
      for (var target_option in options.target_options) {
        if (hasOwnProperty(options.target_options, target_option)) {
          manage_arg(options.target_options[target_option], '--' + target_option, args);
        }
      }
    }
  }

  return args;
};
<commit_msg>`iptables.utils`:<commit_after>// Tables.
exports.tables = {
  filter  : 'filter',     // The default table (if no -t option is passed). It contains the built-in chains INPUT (for packets destined to local sockets), FORWARD (for packets being routed through the box), and OUTPUT (for locally-generated packets).
  nat     : 'nat',        // Consulted when a packet that creates a new connection is encountered.  It consists of three built-ins: PREROUTING (for altering packets as soon as they come  in), OUTPUT (for altering locally-generated packets before routing), and POSTROUTING (for altering packets as they are about to go out).
  mangle  : 'mangle',     // Used for specialized packet alteration.  Until kernel 2.4.17 it had two built-in chains: PREROUTING (for altering incoming packets before routing) and OUTPUT (for altering locally-generated packets before routing).  Since kernel 2.4.18, three other built-in chains are also supported: INPUT (for packets coming into the box  itself),  FORWARD (for altering packets being routed through the box), and POSTROUTING (for altering packets as they are about to go out).
  raw     : 'raw',        // Used mainly for configuring exemptions from connection tracking in combination with the NOTRACK target.  It registers at the netfilter hooks with higher priority and is thus called before ip_conntrack, or any other IP tables.  It provides the following built-in chains: PREROUTING (for packets arriving via any network  interface)  OUTPUT (for packets generated by local processes)
  security: 'security'    // Used for Mandatory Access Control (MAC) networking rules, such as those enabled by the SECMARK and CONNSECMARK targets.  Mandatory Access Control is implemented by Linux Security Modules such as SELinux.  The security table is called after the filter table, allowing any Discretionary Access Control (DAC) rules in the  filter  table  to take effect before MAC rules.  This table provides the following built-in chains: INPUT (for packets coming into the box itself), OUTPUT (for altering locally-generated packets before routing), and FORWARD (for altering packets being routed through the box).
};

/**
 * Processes an iptables possible argument to build the actual query args.
 *
 * @param {String}  property      The property value to be processed (f.e: the content of the `protocol` property).
 * @param {String}  flag          The iptables related argument name (f.e: '--destination').
 * @param {Array}   args          The actual arguments construction where the cmd line is being saved to.
 * @param {Boolean} is_boolean    If true then the property is boolean and don't use a value. False otherwise. F.e: `fragment`.
 */
function manage_arg(property, flag, args, is_boolean) {
  if (property != null) {
    var val = String(property);
    var index = val.indexOf('!');
    var is_boolean = is_boolean || false; // Defaults to false.

    if (index !== -1) {
      args.push('!', flag);

      if (!is_boolean) {
        args.push(val.slice(index + 1)); // To not include the ! character.
      }
    }
    else {
      args.push(flag);

      if (!is_boolean) {
        args.push(val);
      }
    }
  }
}

/**
 * TODO: Ask @bmeck the reasoning behind overriding the native method.
 *
 * @param  {Object}   object    Object to be tested for property inclusion.
 * @param  {String}   key       Property to be tested against the object.
 * @return {Boolean}
 */
function hasOwnProperty(object, key) {
  return Object.prototype.hasOwnProperty.call(object, key);
}

/**
 * Process common rule specs for `append`, `check`, `delete`, `insert` and `replace`.
 *
 * @param   {Object}  options
 * @returns {Array}
 */
exports.processCommonRuleSpecs = function (options) {
  var args = [];

  manage_arg(options.protocol, '--protocol', args);
  manage_arg(options.source, '--source', args);
  manage_arg(options.destination, '--destination', args);
  manage_arg(options['in-interface'], '--in-interface', args);
  manage_arg(options['out-interface'], '--out-interface', args);
  manage_arg(options.fragment, '--fragment', args, true);

  /*
   * Matches processing.
   */
  if (options.matches != null) {
    for (var match in options.matches) {
      if (hasOwnProperty(options.matches, match)) {
        // Initial match load.
        args.push('--match', match);

        // Specific match options.
        var this_match = options.matches[match];

        if (this_match != null && typeof this_match === 'object') {
          for (var match_option in this_match) {
            if (hasOwnProperty(this_match, match_option)) {
              manage_arg(this_match[match_option], '--' + match_option, args);
            }
          }
        }
      }
    }
  }

  if (options.goto != null) {
    args.push('--goto', String(options.goto));
  }

  if (options.jump != null) {
    args.push('--jump', String(options.jump));

    // Target extension options.
    if (options.target_options != null && typeof options.target_options === 'object') {
      for (var target_option in options.target_options) {
        if (hasOwnProperty(options.target_options, target_option)) {
          manage_arg(options.target_options[target_option], '--' + target_option, args);
        }
      }
    }
  }

  return args;
};<|endoftext|>


<commit_before>/*
 * The MIT License
 *
 * Copyright 2017 Kenneth Tilton.
 *
 */

const Todos = Todo.loadAllItems();

function todoSSB() {
    /*
    let bits = [
        section({ class: "todoapp", name: "todoapp"}, c => { return [
            header({class: "header"}, c => [
                h1("todos2"),
                input({ class: "new-todo", autofocus: true,
                        placeholder: "What needs doing?",
                        onkeypress: 'todoAddNewOnEnter'})]),

            section({class: "main",
                     hidden: cF( c => Todos.empty)}, c=> [
                input({ id: "toggle-all",
                        class: "toggle-all",
                        checked: cF( c => (Todos.items.length === 0) ? false :
                                    (Todos.items.every( i => i.completed) ? true : false)),

                        type: "checkbox"}),
                label( "Mark all as complete",
                        { for: "toggle-all",
                          onclick: 'toggleAllCompletion'}),
                ul({ class: "todo-list", name: "todo-list",
                        kidValues: cF( c=> Todos.items),
                        kidKey: k => k.todo,
                        kidFactory: mkTodoItem},
                    c =>  c.kidValuesKids())]),

            mkDashboard()]}),

        footer({class: "info"},
            c => ['Double-click a todo to edit it',
                  'Created by... Kenneth Tilton',
                  'Inspired by TodoMVC']
                    .map( s => p({},s)))];
*/

    let bits = [
        section({ class: "todoapp", name: "todoapp"},
            header({class: "header"},
                h1("todos")))];

    /* OK let bits = [
        section({ class: "todoapp", name: "todoapp"},
            h1("todos"))];*/

    // OK let bits = [ h1("booya"), h2("cool2")];
    //clg('bits!!!',bits);

    let h = "".concat(...bits.map( b=>b().toHTML()));

    clg('html!!!!', h);
    return h;
}

// -- toggle all

function toggleAllCompletion (dom,e) {
    let toggall = document.getElementById("toggle-all"),
        action = dom2js(toggall).checked ? 'undo':'do';
    // clg('togg all checked '+ dom2js(toggall).checked + ' act=' + action);
    Todos.items
        .filter( td => xor( td.completed, action === 'do'))
        .map( td => td.completed = (action === 'do'));
}

/// --- structure breakouts just to make page easier to digest --------------

function mkTodoItem( c, todo) {
    return li({ todo: todo,
                class: cF(c => (todo.completed ? "completed" : "")),
                display: cF(c => todoMatchesSelect(todo, c.fmUp('filters').selection) ? "block" : "none")}, c => [

                div({class: "view"}, c => [

                    input({class: "toggle", type: "checkbox",
                            checked: cF( c=> todo.completed),
                            onclick: 'todoToggleComplete',
                            title: cF( c=> `Mark ${todo.completed? "in" : ""}complete.`)}),

                    label( cF( c => todo.title),
                        { todo: todo,
                          ondblclick: 'todoStartEditing'}),

                    button(null, { class: "destroy", onclick: 'todoDelete'})]),

                input({ name: "myEditor", class: "edit",
                        todo: todo,
                        value: cFI( c=> todo.title),
                        onblur: 'todoEdit',
                        onkeydown: 'todoEdit', // hmmm. picks up Escape. Not needed in CLJS version... goog.closure?
                        onkeypress: 'todoEdit'})]);
}

// -- toggle one

function todoToggleComplete (dom, e) {
    let todo = dom2js(dom).fmTag('li').todo;
    todo.completed = !todo.completed;
}

function todoMatchesSelect( todo, selection) {
    return selection==='All'
        || (selection==='Completed' && todo.completed)
        || (selection==='Active' && !todo.completed);
}

function mkDashboard () {
    return footer({class: "footer",
                    hidden: cF( c => Todos.empty)}, c => [

                span({ class: "todo-count",
                        content: cF(c => {
                    let remCt = Todos.items.filter(todo => !todo.completed).length;
                    return `<strong>${remCt}</strong> item${remCt === 1 ? '' : 's'} remaining`;})}),

                ul( { class: "filters",
                      name: "filters",
                      selection: cI("All")}, c =>
                    [["All", "#/"], ["Active","#/active"], ["Completed","#/completed"]]
                        .map( which => {
                            var [ label, route] = which;
                            return li({}, c=> [a({href: route,
                                                  content: label, selector: label,
                                                  selected: cF( c => c.md.selector === c.md.fmTag('ul').selection),
                                                  class: cF( c => c.md.selected ? "selected":"")})])})),

                button("Clear completed",
                    { class: "clear-completed",
                      hidden: cF(c => Todos.items.filter(todo => todo.completed).length === 0),
                      onclick: 'todoCompletedDelete'})]);
}

/// --- on event handlers --------------------------------------------

// --- add new to-do

function todoAddNewOnEnter (dom, e) {
    if (e.key==='Enter') {
        let title = e.target.value.trim();
        if (title === '') {
            alert("A reminder to do nothing? I like it! We should all slow things down now and then. But, no.");
        } else {
            Todos.itemsRaw = Todos.itemsRaw.concat(new Todo({title: title}));
        }
        e.target.value = null;
    }
}

// -- delete to-do

function todoDelete (dom,e) {
    let todo = dom2js(dom).fmTag('li').todo;
    todo.delete(); // flow:TRIGGER
}

// -- editing event handlers

function todoStartEditing (dom,e) {
    let li = dom2js(dom).fmTag('li', 'myLi') // find overarching li, then...
        , edt = li.fmDown('myEditor'); // NAVIG
    edt.dom.li = li; // save a little navigation later
    li.dom.classList.add("editing");
    edt.dom.focus();
    edt.dom.setSelectionRange(0, edt.dom.value.length);
}

function todoEdit ( edtdom, e) {
    // this too can be simplified as part of the above exercise
    let li = edtdom.li,
        title = e.target.value.trim();

    if (e.type === 'blur' || e.key === 'Enter') {
        // editing a title to blank signifies deletion
        if (title==='')
            li.todo.delete();
        else
            li.todo.title = edtdom.value;

        li.dom.classList.remove('editing');
    } else if (e.key === 'Escape') {
        li.dom.classList.remove('editing');
    }
}

 // -- (logically) delete completed

function todoCompletedDelete( dom, e) {
    Todos.items.filter( td => td.completed ).map( td => td.delete());
}

<commit_msg>code gen footer not OK<commit_after>/*
 * The MIT License
 *
 * Copyright 2017 Kenneth Tilton.
 *
 */

const Todos = Todo.loadAllItems();

function todoSSB() {

    /*let bits = [
        section({ class: "todoapp", name: "todoapp"},
            header({class: "header"},
                h1("todos2"),
                input({ class: "new-todo", autofocus: true,
                        placeholder: "What needs doing?",
                        onkeypress: 'todoAddNewOnEnter'})),

            section({class: "main",
                     hidden: cF( c => Todos.empty)},
                input({ id: "toggle-all",
                        class: "toggle-all",
                        checked: cF( c => (Todos.items.length === 0) ? false :
                                    (Todos.items.every( i => i.completed) ? true : false)),

                        type: "checkbox"}),
                label( "Mark all as complete",
                        { for: "toggle-all",
                          onclick: 'toggleAllCompletion'}),
                ul({ class: "todo-list", name: "todo-list",
                        kidValues: cF( c=> Todos.items),
                        kidKey: k => k.todo,
                        kidFactory: mkTodoItem},
                    c =>  c.kidValuesKids())),

            mkDashboard()),

        footer({class: "info"},
            ['Double-click a todo to edit it',
             'Created by... Kenneth Tilton',
             'Inspired by TodoMVC'].map( s => p({},s)))];*/
    let bits = [
        footer({class: "info"},
            ['Double-click a todo to edit it',
                'Created by... Kenneth Tilton',
                'Inspired by TodoMVC'].map( s => p({},s)))
    ];

    /* OK let bits = [
        section({ class: "todoapp", name: "todoapp"},
            header({class: "header"},
                h1("todos")))];*/

    /* OK let bits = [
        section({ class: "todoapp", name: "todoapp"},
            h1("todos"))];*/

    // OK let bits = [ h1("booya"), h2("cool2")];
    //clg('bits!!!',bits);

    let h = "".concat(...bits.map( b=>b().toHTML()));

    clg('html!!!!', h);
    return h;
}

// -- toggle all

function toggleAllCompletion (dom,e) {
    let toggall = document.getElementById("toggle-all"),
        action = dom2js(toggall).checked ? 'undo':'do';
    // clg('togg all checked '+ dom2js(toggall).checked + ' act=' + action);
    Todos.items
        .filter( td => xor( td.completed, action === 'do'))
        .map( td => td.completed = (action === 'do'));
}

/// --- structure breakouts just to make page easier to digest --------------

function mkTodoItem( c, todo) {
    return li({ todo: todo,
                class: cF(c => (todo.completed ? "completed" : "")),
                display: cF(c => todoMatchesSelect(todo, c.fmUp('filters').selection) ? "block" : "none")}, c => [

                div({class: "view"}, c => [

                    input({class: "toggle", type: "checkbox",
                            checked: cF( c=> todo.completed),
                            onclick: 'todoToggleComplete',
                            title: cF( c=> `Mark ${todo.completed? "in" : ""}complete.`)}),

                    label( cF( c => todo.title),
                        { todo: todo,
                          ondblclick: 'todoStartEditing'}),

                    button(null, { class: "destroy", onclick: 'todoDelete'})]),

                input({ name: "myEditor", class: "edit",
                        todo: todo,
                        value: cFI( c=> todo.title),
                        onblur: 'todoEdit',
                        onkeydown: 'todoEdit', // hmmm. picks up Escape. Not needed in CLJS version... goog.closure?
                        onkeypress: 'todoEdit'})]);
}

// -- toggle one

function todoToggleComplete (dom, e) {
    let todo = dom2js(dom).fmTag('li').todo;
    todo.completed = !todo.completed;
}

function todoMatchesSelect( todo, selection) {
    return selection==='All'
        || (selection==='Completed' && todo.completed)
        || (selection==='Active' && !todo.completed);
}

function mkDashboard () {
    return footer({class: "footer",
                    hidden: cF( c => Todos.empty)}, c => [

                span({ class: "todo-count",
                        content: cF(c => {
                    let remCt = Todos.items.filter(todo => !todo.completed).length;
                    return `<strong>${remCt}</strong> item${remCt === 1 ? '' : 's'} remaining`;})}),

                ul( { class: "filters",
                      name: "filters",
                      selection: cI("All")}, c =>
                    [["All", "#/"], ["Active","#/active"], ["Completed","#/completed"]]
                        .map( which => {
                            var [ label, route] = which;
                            return li({}, c=> [a({href: route,
                                                  content: label, selector: label,
                                                  selected: cF( c => c.md.selector === c.md.fmTag('ul').selection),
                                                  class: cF( c => c.md.selected ? "selected":"")})])})),

                button("Clear completed",
                    { class: "clear-completed",
                      hidden: cF(c => Todos.items.filter(todo => todo.completed).length === 0),
                      onclick: 'todoCompletedDelete'})]);
}

/// --- on event handlers --------------------------------------------

// --- add new to-do

function todoAddNewOnEnter (dom, e) {
    if (e.key==='Enter') {
        let title = e.target.value.trim();
        if (title === '') {
            alert("A reminder to do nothing? I like it! We should all slow things down now and then. But, no.");
        } else {
            Todos.itemsRaw = Todos.itemsRaw.concat(new Todo({title: title}));
        }
        e.target.value = null;
    }
}

// -- delete to-do

function todoDelete (dom,e) {
    let todo = dom2js(dom).fmTag('li').todo;
    todo.delete(); // flow:TRIGGER
}

// -- editing event handlers

function todoStartEditing (dom,e) {
    let li = dom2js(dom).fmTag('li', 'myLi') // find overarching li, then...
        , edt = li.fmDown('myEditor'); // NAVIG
    edt.dom.li = li; // save a little navigation later
    li.dom.classList.add("editing");
    edt.dom.focus();
    edt.dom.setSelectionRange(0, edt.dom.value.length);
}

function todoEdit ( edtdom, e) {
    // this too can be simplified as part of the above exercise
    let li = edtdom.li,
        title = e.target.value.trim();

    if (e.type === 'blur' || e.key === 'Enter') {
        // editing a title to blank signifies deletion
        if (title==='')
            li.todo.delete();
        else
            li.todo.title = edtdom.value;

        li.dom.classList.remove('editing');
    } else if (e.key === 'Escape') {
        li.dom.classList.remove('editing');
    }
}

 // -- (logically) delete completed

function todoCompletedDelete( dom, e) {
    Todos.items.filter( td => td.completed ).map( td => td.delete());
}

<|endoftext|>


<commit_before>(function Calcite () {

  // ┌────────────┐
  // │ Public API │
  // └────────────┘
  // define all public api methods (excluding patterns)
  var calcite = {
    version: 'v0.8.0',
    click: click,
    addEvent: addEvent,
    removeEvent: removeEvent,
    eventTarget: eventTarget,
    preventDefault: preventDefault,
    stopPropagation: stopPropagation,
    hasClass: hasClass,
    addClass: addClass,
    removeClass: removeClass,
    toggleClass: toggleClass,
    closest: closest,
    nodeListToArray: nodeListToArray,
    init: init
  };

  // ┌──────────────────────┐
  // │ DOM Event Management │
  // └──────────────────────┘

  // returns standard interaction event, later will add touch support
  function click () {
    return 'click';
  }

  // add a callback function to an event on a DOM node
  function addEvent (domNode, e, fn) {
    if (domNode.addEventListener) {
      return domNode.addEventListener(e, fn, false);
    } else if (domNode.attachEvent) {
      return domNode.attachEvent('on' + e, fn);
    }
  }

  // remove a specific function binding from a DOM node event
  function removeEvent (domNode, e, fn) {
    if (domNode.removeEventListener) {
      return domNode.removeEventListener(e, fn, false);
    } else if (domNode.detachEvent) {
      return domNode.detachEvent('on' + e,  fn);
    }
  }

  // get the target element of an event
  function eventTarget (e) {
    return e.target || e.srcElement;
  }

  // prevent default behavior of an event
  function preventDefault (e) {
    if (e.preventDefault) {
      return e.preventDefault();
    } else if (e.returnValue) {
      e.returnValue = false;
    }
  }

  // stop and event from bubbling up the DOM tree
  function stopPropagation (e) {
    e = e || window.event;
    if (e.stopPropagation) {
      return e.stopPropagation();
    }
    if (e.cancelBubble) {
      e.cancelBubble = true;
    }
  }

  // ┌────────────────────┐
  // │ Class Manipulation │
  // └────────────────────┘

  // check if an element has a specific class
  function hasClass (domNode, className) {
    var elementClass = ' ' + domNode.className + ' ';
    return elementClass.indexOf(' ' + className + ' ') !== -1;
  }

  // add one or more classes to an element
  function addClass (domNode, classes) {
    classes.split(' ').forEach(function (c) {
      if (!hasClass(domNode, c)) {
        domNode.className += ' ' + c;
      }
    });
  }

  // remove one or more classes from an element
  function removeClass (domNode, classes) {
    var elementClass = ' ' + domNode.className + ' ';
    classes.split(' ').forEach(function (c) {
      elementClass = elementClass.replace(' ' + c + ' ', ' ');
    });
    domNode.className = elementClass.trim();
  }

  // if domNode has the class, remove it, else add it
  function toggleClass (domNode, className) {
    if (hasClass(domNode, className)) {
      removeClass(domNode, className);
    } else {
      addClass(domNode, className);
    }
  }

  // ┌─────┐
  // │ DOM │
  // └─────┘

  // returns closest element up the DOM tree matching a given class
  function closest (className, context) {
    var result, current;
    for (current = context; current; current = current.parentNode) {
      if (current.nodeType === 1 && hasClass(current, className)) {
        result = current;
        break;
      }
    }
    return current;
  }

  // turn a domNodeList into an array
  function nodeListToArray (domNodeList) {
    return Array.prototype.slice.call(domNodeList);
  }

  // ┌─────────────┐
  // │ JS Patterns │
  // └─────────────┘
  // helper functions for ui patterns

  // return an array of elements matching a query
  function findElements (query, domNode) {
    var context = domNode || document;
    var elements = context.querySelectorAll(query);
    return nodeListToArray(elements);
  }

  // remove 'is-active' class from every element in an array
  function removeActive (array) {
    if (typeof array == 'object') {
      array = nodeListToArray(array);
    }
    array.forEach(function (item) {
      removeClass(item, 'is-active');
    });
  }

  // remove 'is-active' from array, add to element
  function toggleActive (array, el) {
    var isActive = hasClass(el, 'is-active');
    if (isActive) {
      removeClass(el, 'is-active');
    } else {
      removeActive(array);
      addClass(el, 'is-active');
    }
  }

  // ┌───────────┐
  // │ Accordion │
  // └───────────┘
  // collapsible accordion list
  calcite.accordion = function (domNode) {
    findElements('.js-accordion', domNode).forEach(function (accordion) {
      nodeListToArray(accordion.children).forEach(function (child) {
        addEvent(child, click(), toggleAccordion);
      });
    });

    function toggleAccordion (e) {
      var parent = closest('accordion-section', eventTarget(e));
      toggleClass(parent, 'is-active');
    }
  };

  // ┌──────────┐
  // │ Dropdown │
  // └──────────┘
  // show and hide dropdown menus
  calcite.dropdown = function (domNode) {
    var toggles = findElements('.js-dropdown-toggle', domNode);
    var dropdowns = findElements('.js-dropdown', domNode);

    function closeAllDropdowns () {
      removeEvent(document.body, click(), closeAllDropdowns);
      dropdowns.forEach(function (dropdown) {
        removeClass(dropdown, 'is-active');
      });
    }

    function bindToggle (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);
        stopPropagation(e);
        var dropdown = closest('js-dropdown', toggle);
        closeAllDropdowns();
        addClass(dropdown, 'is-active');
        addEvent(document.body, click(), closeAllDropdowns);
      });
    }

    toggles.forEach(bindToggle);
  };

  // ┌────────┐
  // │ Drawer │
  // └────────┘
  // show and hide drawers
  calcite.drawer = function (domNode) {
    var toggles = findElements('.js-drawer-toggle', domNode);
    var drawers = findElements('.js-drawer', domNode);

    toggles.forEach(function (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);
        var drawerId = toggle.getAttribute('data-drawer');
        var drawer = document.querySelector('.js-drawer[data-drawer="' + drawerId + '"');
        toggleActive(drawers, drawer);
      });
    });

    drawers.forEach(function (drawer) {
      addEvent(drawer, click(), function (e) {
        if (hasClass(eventTarget(e), 'drawer')) {
          toggleActive(drawers, drawer);
        }
      });
    });
  };

  // ┌───────────────┐
  // │ Expanding Nav │
  // └───────────────┘
  // show and hide exanding nav located under topnav
  calcite.expandingNav = function (domNode) {
    var toggles = findElements('.js-expanding-toggle', domNode);
    var expanders = findElements('.js-expanding', domNode);
    var sections = document.querySelectorAll('.js-expanding-nav');

    toggles.forEach(function (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);

        var sectionId = toggle.getAttribute('data-expanding-nav');
        var section = document.querySelector('.js-expanding-nav[data-expanding-nav="' + sectionId + '"]');
        var expander = closest('js-expanding', section);
        var isOpen = hasClass(expander, 'is-active');
        var shouldClose = hasClass(section, 'is-active');

        toggleActive(sections, section);

        if (isOpen && shouldClose) {
          removeClass(expander, 'is-active');
        } else {
          addClass(expander, 'is-active');
        }
      });
    });
  };

  // ┌───────┐
  // │ Modal │
  // └───────┘
  // show and hide modal dialogues
  calcite.modal = function (domNode) {
    var toggles = findElements('.js-modal-toggle', domNode);
    var modals = findElements('.js-modal', domNode);

    toggles.forEach(function (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);
        var modal;
        var modalId = toggle.getAttribute('data-modal');
        if (modalId) {
          modal = document.querySelector('.js-modal[data-modal="' + modalId + '"');
        } else {
          modal = closest('js-modal', toggle);
        }
        toggleActive(modals, modal);
      });
    });

    modals.forEach(function (modal) {
      addEvent(modal, click(), function (e) {
        stopPropagation(e);
        if (eventTarget(e) === modal) {
          toggleActive(modals, modal);
        }
      });
    });
  };

  // ┌──────┐
  // │ Tabs │
  // └──────┘
  // tabbed content pane
  calcite.tabs = function (domNode) {
    var tabs = findElements('.js-tab', domNode);
    var tabGroups = findElements('.js-tab-group', domNode);

    // set max width for each tab
    tabGroups.forEach(function (tab) {
      var tabsInGroup = tab.querySelectorAll('.js-tab');
      var percent = 100 / tabsInGroup.length;
      for (var i = 0; i < tabsInGroup.length; i++) {
        tabsInGroup[i].style.maxWidth = percent + '%';
      }
    });

    function switchTab (e) {
      preventDefault(e);

      var tab = closest('js-tab', eventTarget(e));
      var tabGroup = closest('js-tab-group', tab);
      var tabs = tabGroup.querySelectorAll('.js-tab');
      var contents = tabGroup.querySelectorAll('.js-tab-section');
      var index = nodeListToArray(tabs).indexOf(tab);

      removeActive(tabs);
      removeActive(contents);

      addClass(tab, 'is-active');
      addClass(contents[index], 'is-active');
    }

    tabs.forEach(function (tab) {
      addEvent(tab, click(), switchTab);
    });
  };

  // ┌────────┐
  // │ Sticky │
  // └────────┘
  // sticks things to the window
  calcite.sticky = function () {
    var elements = findElements('.js-sticky');
    var stickies = elements.map(function (el) {
      var offset = el.offsetTop;
      var dataTop = el.getAttribute('data-top') || 0;
      return {
        active: false,
        top: offset - parseInt(dataTop, 0),
        shim: el.cloneNode('deep'),
        element: el
      };
    });

    function handleScroll(item, offset) {
      var el = item.element;
      var parent = el.parentNode;
      var distance = item.top - offset;
      var dataTop = el.getAttribute('data-top');

      if (distance < 1 && !item.active) {
        item.shim.style.visiblity = 'hidden';
        parent.insertBefore(item.shim, el);
        addClass(el, 'is-sticky');
        item.active = true;
        el.style.top = dataTop + 'px';
      } else if (item.active && offset < item.top) {
        parent.removeChild(item.shim);
        removeClass(el, 'is-sticky');
        el.style.top = null;
        item.active = false;
      }
    }

    addEvent(window, 'scroll', function () {
      var offset = window.pageYOffset;
      stickies.forEach(function (sticky) {
        handleScroll(sticky, offset);
      });
    });

  };

  // ┌────────────────────┐
  // │ Initialize Calcite │
  // └────────────────────┘
  // start up Calcite and attach all the patterns
  // optionally pass an array of patterns you'd like to watch
  function init (patterns) {
    patterns = patterns || ['accordion', 'dropdown', 'drawer', 'expandingNav', 'modal', 'tabs', 'sticky'];
    patterns.forEach(function (pattern) {
      calcite[pattern]();
    });
  }

  // ┌────────────────┐
  // │ Expose Calcite │
  // └────────────────┘
  // make calcite available to amd, common-js, or globally
  if (typeof define === 'function' && define.amd) {
    define(function () { return calcite; });
  } else if (typeof exports === 'object') {
    module.exports = calcite;
  } else {
    // if something called calcite already exists,
    // save it for recovery via calcite.noConflict()
    var oldCalcite = window.calcite;
    calcite.noConflict = function () {
      window.calcite = oldCalcite;
      return this;
    };
    window.calcite = calcite;
  }

})();
<commit_msg>aria roles, hidden content, and focus states<commit_after>(function Calcite () {

  // ┌────────────┐
  // │ Public API │
  // └────────────┘
  // define all public api methods (excluding patterns)
  var calcite = {
    version: 'v0.8.0',
    click: click,
    addEvent: addEvent,
    removeEvent: removeEvent,
    eventTarget: eventTarget,
    preventDefault: preventDefault,
    stopPropagation: stopPropagation,
    hasClass: hasClass,
    addClass: addClass,
    removeClass: removeClass,
    toggleClass: toggleClass,
    closest: closest,
    nodeListToArray: nodeListToArray,
    init: init
  };

  // ┌──────────────────────┐
  // │ DOM Event Management │
  // └──────────────────────┘

  // returns standard interaction event, later will add touch support
  function click () {
    return 'click';
  }

  // add a callback function to an event on a DOM node
  function addEvent (domNode, e, fn) {
    if (domNode.addEventListener) {
      return domNode.addEventListener(e, fn, false);
    } else if (domNode.attachEvent) {
      return domNode.attachEvent('on' + e, fn);
    }
  }

  // remove a specific function binding from a DOM node event
  function removeEvent (domNode, e, fn) {
    if (domNode.removeEventListener) {
      return domNode.removeEventListener(e, fn, false);
    } else if (domNode.detachEvent) {
      return domNode.detachEvent('on' + e,  fn);
    }
  }

  // get the target element of an event
  function eventTarget (e) {
    return e.target || e.srcElement;
  }

  // prevent default behavior of an event
  function preventDefault (e) {
    if (e.preventDefault) {
      return e.preventDefault();
    } else if (e.returnValue) {
      e.returnValue = false;
    }
  }

  // stop and event from bubbling up the DOM tree
  function stopPropagation (e) {
    e = e || window.event;
    if (e.stopPropagation) {
      return e.stopPropagation();
    }
    if (e.cancelBubble) {
      e.cancelBubble = true;
    }
  }

  // ┌────────────────────┐
  // │ Class Manipulation │
  // └────────────────────┘

  // check if an element has a specific class
  function hasClass (domNode, className) {
    var elementClass = ' ' + domNode.className + ' ';
    return elementClass.indexOf(' ' + className + ' ') !== -1;
  }

  // add one or more classes to an element
  function addClass (domNode, classes) {
    classes.split(' ').forEach(function (c) {
      if (!hasClass(domNode, c)) {
        domNode.className += ' ' + c;
      }
    });
  }

  // remove one or more classes from an element
  function removeClass (domNode, classes) {
    var elementClass = ' ' + domNode.className + ' ';
    classes.split(' ').forEach(function (c) {
      elementClass = elementClass.replace(' ' + c + ' ', ' ');
    });
    domNode.className = elementClass.trim();
  }

  // if domNode has the class, remove it, else add it
  function toggleClass (domNode, className) {
    if (hasClass(domNode, className)) {
      removeClass(domNode, className);
    } else {
      addClass(domNode, className);
    }
  }

  // ┌─────┐
  // │ DOM │
  // └─────┘

  // returns closest element up the DOM tree matching a given class
  function closest (className, context) {
    var result, current;
    for (current = context; current; current = current.parentNode) {
      if (current.nodeType === 1 && hasClass(current, className)) {
        result = current;
        break;
      }
    }
    return current;
  }

  // turn a domNodeList into an array
  function nodeListToArray (domNodeList) {
    return Array.prototype.slice.call(domNodeList);
  }

  // ┌─────────────┐
  // │ JS Patterns │
  // └─────────────┘
  // helper functions for ui patterns

  // return an array of elements matching a query
  function findElements (query, domNode) {
    var context = domNode || document;
    var elements = context.querySelectorAll(query);
    return nodeListToArray(elements);
  }

  // remove 'is-active' class from every element in an array
  function removeActive (array) {
    if (typeof array == 'object') {
      array = nodeListToArray(array);
    }
    array.forEach(function (item) {
      removeClass(item, 'is-active');
    });
  }

  // remove 'is-active' from array, add to element
  function toggleActive (array, el) {
    var isActive = hasClass(el, 'is-active');
    if (isActive) {
      removeClass(el, 'is-active');
    } else {
      removeActive(array);
      addClass(el, 'is-active');
    }
  }

  // toggles `aria-hidde="true"` on a domNode
  function toggleAriaHidden (array) {
    array.forEach(function (node) {
      var hidden = node.getAttribute('aria-hidden');
      if (hidden !== 'true') {
        node.setAttribute('aria-hidden', true);
      } else {
        node.removeAttribute('aria-hidden');
      }
    });
  }

  // ┌───────────┐
  // │ Accordion │
  // └───────────┘
  // collapsible accordion list
  calcite.accordion = function (domNode) {
    findElements('.js-accordion', domNode).forEach(function (accordion) {
      nodeListToArray(accordion.children).forEach(function (child) {
        addEvent(child, click(), toggleAccordion);
      });
    });

    function toggleAccordion (e) {
      var parent = closest('accordion-section', eventTarget(e));
      toggleClass(parent, 'is-active');
    }
  };

  // ┌──────────┐
  // │ Dropdown │
  // └──────────┘
  // show and hide dropdown menus
  calcite.dropdown = function (domNode) {
    var toggles = findElements('.js-dropdown-toggle', domNode);
    var dropdowns = findElements('.js-dropdown', domNode);

    function closeAllDropdowns () {
      removeEvent(document.body, click(), closeAllDropdowns);
      dropdowns.forEach(function (dropdown) {
        removeClass(dropdown, 'is-active');
      });
    }

    function bindToggle (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);
        stopPropagation(e);
        var dropdown = closest('js-dropdown', toggle);
        closeAllDropdowns();
        addClass(dropdown, 'is-active');
        addEvent(document.body, click(), closeAllDropdowns);
      });
    }

    toggles.forEach(bindToggle);
  };

  // ┌────────┐
  // │ Drawer │
  // └────────┘
  // show and hide drawers
  calcite.drawer = function (domNode) {
    var wrapper = document.querySelector('.wrapper');
    var footer = document.querySelector('.footer');
    var toggles = findElements('.js-drawer-toggle', domNode);
    var drawers = findElements('.js-drawer', domNode);

    toggles.forEach(function (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);
        var drawerId = toggle.getAttribute('data-drawer');
        var drawer = document.querySelector('.js-drawer[data-drawer="' + drawerId + '"');
        toggleActive(drawers, drawer);
        drawer.setAttribute('tabindex', 0)
        drawer.focus();
        toggleAriaHidden([wrapper, footer]);
      });
    });

    drawers.forEach(function (drawer) {
      addEvent(drawer, click(), function (e) {
        if (hasClass(eventTarget(e), 'drawer')) {
          toggleActive(drawers, drawer);
          toggleAriaHidden([wrapper, footer]);
        }
      });
    });
  };

  // ┌───────────────┐
  // │ Expanding Nav │
  // └───────────────┘
  // show and hide exanding nav located under topnav
  calcite.expandingNav = function (domNode) {
    var toggles = findElements('.js-expanding-toggle', domNode);
    var expanders = findElements('.js-expanding', domNode);
    var sections = document.querySelectorAll('.js-expanding-nav');

    toggles.forEach(function (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);

        var sectionId = toggle.getAttribute('data-expanding-nav');
        var section = document.querySelector('.js-expanding-nav[data-expanding-nav="' + sectionId + '"]');
        var expander = closest('js-expanding', section);
        var isOpen = hasClass(expander, 'is-active');
        var shouldClose = hasClass(section, 'is-active');

        toggleActive(sections, section);

        if (isOpen && shouldClose) {
          removeClass(expander, 'is-active');
        } else {
          addClass(expander, 'is-active');
        }
      });
    });
  };

  // ┌───────┐
  // │ Modal │
  // └───────┘
  // show and hide modal dialogues
  calcite.modal = function (domNode) {
    var wrapper = document.querySelector('.wrapper');
    var footer = document.querySelector('.footer');
    var toggles = findElements('.js-modal-toggle', domNode);
    var modals = findElements('.js-modal', domNode);

    toggles.forEach(function (toggle) {
      addEvent(toggle, click(), function (e) {
        preventDefault(e);
        var modal;
        var modalId = toggle.getAttribute('data-modal');
        if (modalId) {
          modal = document.querySelector('.js-modal[data-modal="' + modalId + '"');
        } else {
          modal = closest('js-modal', toggle);
        }
        toggleActive(modals, modal);
        modal.focus();
        toggleAriaHidden([wrapper, footer]);
      });
    });

    modals.forEach(function (modal) {
      addEvent(modal, click(), function (e) {
        stopPropagation(e);
        if (eventTarget(e) === modal) {
          toggleActive(modals, modal);
          toggleAriaHidden([wrapper, footer]);
        }
      });
    });
  };

  // ┌──────┐
  // │ Tabs │
  // └──────┘
  // tabbed content pane
  calcite.tabs = function (domNode) {
    var tabs = findElements('.js-tab', domNode);
    var tabGroups = findElements('.js-tab-group', domNode);

    // set max width for each tab
    tabGroups.forEach(function (tab) {
      var tabsInGroup = tab.querySelectorAll('.js-tab');
      var percent = 100 / tabsInGroup.length;
      for (var i = 0; i < tabsInGroup.length; i++) {
        tabsInGroup[i].style.maxWidth = percent + '%';
      }
    });

    function switchTab (e) {
      preventDefault(e);

      var tab = closest('js-tab', eventTarget(e));
      var tabGroup = closest('js-tab-group', tab);
      var tabs = tabGroup.querySelectorAll('.js-tab');
      var contents = tabGroup.querySelectorAll('.js-tab-section');
      var index = nodeListToArray(tabs).indexOf(tab);

      removeActive(tabs);
      removeActive(contents);

      addClass(tab, 'is-active');
      addClass(contents[index], 'is-active');
    }

    tabs.forEach(function (tab) {
      addEvent(tab, click(), switchTab);
    });
  };

  // ┌────────┐
  // │ Sticky │
  // └────────┘
  // sticks things to the window
  calcite.sticky = function () {
    var elements = findElements('.js-sticky');
    var stickies = elements.map(function (el) {
      var offset = el.offsetTop;
      var dataTop = el.getAttribute('data-top') || 0;
      return {
        active: false,
        top: offset - parseInt(dataTop, 0),
        shim: el.cloneNode('deep'),
        element: el
      };
    });

    function handleScroll(item, offset) {
      var el = item.element;
      var parent = el.parentNode;
      var distance = item.top - offset;
      var dataTop = el.getAttribute('data-top');

      if (distance < 1 && !item.active) {
        item.shim.style.visiblity = 'hidden';
        parent.insertBefore(item.shim, el);
        addClass(el, 'is-sticky');
        item.active = true;
        el.style.top = dataTop + 'px';
      } else if (item.active && offset < item.top) {
        parent.removeChild(item.shim);
        removeClass(el, 'is-sticky');
        el.style.top = null;
        item.active = false;
      }
    }

    addEvent(window, 'scroll', function () {
      var offset = window.pageYOffset;
      stickies.forEach(function (sticky) {
        handleScroll(sticky, offset);
      });
    });

  };

  // ┌────────────────────┐
  // │ Initialize Calcite │
  // └────────────────────┘
  // start up Calcite and attach all the patterns
  // optionally pass an array of patterns you'd like to watch
  function init (patterns) {
    patterns = patterns || ['accordion', 'dropdown', 'drawer', 'expandingNav', 'modal', 'tabs', 'sticky'];
    patterns.forEach(function (pattern) {
      calcite[pattern]();
    });
  }

  // ┌────────────────┐
  // │ Expose Calcite │
  // └────────────────┘
  // make calcite available to amd, common-js, or globally
  if (typeof define === 'function' && define.amd) {
    define(function () { return calcite; });
  } else if (typeof exports === 'object') {
    module.exports = calcite;
  } else {
    // if something called calcite already exists,
    // save it for recovery via calcite.noConflict()
    var oldCalcite = window.calcite;
    calcite.noConflict = function () {
      window.calcite = oldCalcite;
      return this;
    };
    window.calcite = calcite;
  }

})();
<|endoftext|>


<commit_before>import Controller from '@ember/controller';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';

export default class LaunchController extends Controller {
    @service config;
    @service router;
    @service settings;

    queryParams = ['step'];

    @tracked previewGuid = (new Date()).valueOf();
    @tracked previewSrc = '';

    steps = {
        'customise-design': {
            title: 'Customise your site',
            position: 'Step 1',
            next: 'connect-stripe'
        },
        'connect-stripe': {
            title: 'Connect to Stripe',
            position: 'Step 2',
            next: 'set-pricing',
            back: 'customise-design',
            skip: 'finalise'
        },
        'set-pricing': {
            title: 'Set up subscriptions',
            position: 'Step 3',
            next: 'finalise',
            back: 'connect-stripe'
        },
        finalise: {
            title: 'Launch your site',
            position: 'Final step',
            back: 'set-pricing'
        }
    }

    @tracked step = 'customise-design';

    get currentStep() {
        return this.steps[this.step];
    }

    @action
    goToStep(step) {
        if (step) {
            this.step = step;
        }
    }

    @action
    nextStep() {
        this.step = this.currentStep.next;
    }

    @action
    backStep() {
        this.step = this.currentStep.back;
    }

    // TODO: remember when a step is skipped so "back" works as expected
    @action
    skipStep() {
        this.step = this.currentStep.skip;
    }

    @action
    refreshPreview() {
        this.previewGuid = (new Date()).valueOf();
    }

    @action
    updatePreview(url) {
        console.log({url});
        this.previewSrc = url;
    }

    @action
    close() {
        this.router.transitionTo('dashboard');
    }

    @action
    reset() {
        this.step = 'customise-design';
    }
}
<commit_msg>Removed errant console.log<commit_after>import Controller from '@ember/controller';
import {action} from '@ember/object';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';

export default class LaunchController extends Controller {
    @service config;
    @service router;
    @service settings;

    queryParams = ['step'];

    @tracked previewGuid = (new Date()).valueOf();
    @tracked previewSrc = '';

    steps = {
        'customise-design': {
            title: 'Customise your site',
            position: 'Step 1',
            next: 'connect-stripe'
        },
        'connect-stripe': {
            title: 'Connect to Stripe',
            position: 'Step 2',
            next: 'set-pricing',
            back: 'customise-design',
            skip: 'finalise'
        },
        'set-pricing': {
            title: 'Set up subscriptions',
            position: 'Step 3',
            next: 'finalise',
            back: 'connect-stripe'
        },
        finalise: {
            title: 'Launch your site',
            position: 'Final step',
            back: 'set-pricing'
        }
    }

    @tracked step = 'customise-design';

    get currentStep() {
        return this.steps[this.step];
    }

    @action
    goToStep(step) {
        if (step) {
            this.step = step;
        }
    }

    @action
    nextStep() {
        this.step = this.currentStep.next;
    }

    @action
    backStep() {
        this.step = this.currentStep.back;
    }

    // TODO: remember when a step is skipped so "back" works as expected
    @action
    skipStep() {
        this.step = this.currentStep.skip;
    }

    @action
    refreshPreview() {
        this.previewGuid = (new Date()).valueOf();
    }

    @action
    updatePreview(url) {
        this.previewSrc = url;
    }

    @action
    close() {
        this.router.transitionTo('dashboard');
    }

    @action
    reset() {
        this.step = 'customise-design';
    }
}
<|endoftext|>