<commit_before>'use strict';

const fs = require('fs');
const { basename, join, resolve } = require('path');
const project = require('./project');
const html = require('./html');
const consoleModule = require('./console.mdl');
const messages = require('./messages');
const dual = require('./dual');

const ldir = join(process.cwd(), 'licenses');

const reportFileName = (name, type) => {
  let rfn = `${name}_${type}.txt`;
  rfn = rfn.replace(/@/gi, '');
  rfn = rfn.replace(/\//gi, '-');
  return rfn;
};

const reportFileData = (projectMetaData) => {
  projectMetaData.dependencies.dependency.forEach(d => {
    if (d.file && d.file.includes(d.licenses.license[0].name.split(' ')[0].toUpperCase())) {
      const link = join('./', d.file);
      d.localLicense = require('querystring').escape(link).replace(/%2F/gi, '/');
      d.linkLabel = link;
    } else {
      d.localLicense = '#';
      d.linkLabel = 'No local license could be found for the dependency';
    }
  });
  return projectMetaData;
};

const htmlReport = (reportData) => {
  html.parse(reportData).then(output => {
    fs.writeFileSync(join(ldir, 'license.html'), output);
  });
};

const copyCSS = (css) => {
  if (fs.existsSync(css)) {
    fs.createReadStream(resolve(css))
    .pipe(fs.createWriteStream(join(ldir, 'licenses.css')));
  } else {
    console.error(`CSS file not found: ${css}`);
  }
};

const copyLicenseFile = (name, type, file) => {
  let fileName;
  if (basename(file).toUpperCase() !== 'README.MD') {
    fileName = reportFileName(name, type).toUpperCase();
    fs.createReadStream(file)
     .pipe(fs.createWriteStream(join(ldir, fileName)));
  }
  return fileName;
};

const licenseMetaData = (licenses, canonicalNameMapper, ilc, file) => {
  const licenseData = { license: [] };
  if (dual.isDual(licenses)) {
    let nameUrl = consoleModule.licenseNameUrl(canonicalNameMapper, dual.first(licenses));
    licenseData.license.push({name: dual.first(licenses), url: nameUrl.url});

    nameUrl = consoleModule.licenseNameUrl(canonicalNameMapper, dual.last(licenses));
    licenseData.license.push({name: dual.last(licenses), url: nameUrl.url});
  } else {
    const nameUrl = consoleModule.licenseNameUrl(canonicalNameMapper, licenses);
    licenseData.license.push({name: licenses, url: nameUrl.url});
  }
  return licenseData;
};

const transform = (data, canonicalNameMapper, cwd, ilc) => {
  const projectMetaData = project.projectMetaData(cwd);
  data = consoleModule.declaredDependencies(data, project.dependencies(cwd));
  data = consoleModule.packageMetaData(data);
  data.forEach((d) => {
    if (d.file) {
      d.file = copyLicenseFile(d.packageName, d.licenses, d.file);
    } else {
      console.log(`No license file found for ${d.packageName}`);
    }
    d.licenses = licenseMetaData(d.licenses, canonicalNameMapper, ilc, d.file);
  });
  data.forEach((d) => {
    if (d.licenses.license.length === 2) {
      const clone = JSON.parse(JSON.stringify(d));
      d.licenses.license.pop();
      clone.licenses.license.shift();
      data.push(clone);
    }
  });
  data.sort((a, b) => a.packageName.localeCompare(b.packageName));
  projectMetaData.dependencies.dependency = data;
  messages.show(project.dependencies(cwd), projectMetaData);
  return projectMetaData;
};

const report = (projectMetaData, css) => {
  copyCSS(css);
  htmlReport(reportFileData(projectMetaData));
};

module.exports = {
  report,
  transform
};
<commit_msg>🐛 Handle names as array in report module<commit_after>'use strict';

const fs = require('fs');
const { basename, join, resolve } = require('path');
const project = require('./project');
const html = require('./html');
const consoleModule = require('./console.mdl');
const messages = require('./messages');
const dual = require('./dual');

const ldir = join(process.cwd(), 'licenses');

const reportFileName = (name, type) => {
  let rfn = `${name}_${type}.txt`;
  rfn = rfn.replace(/@/gi, '');
  rfn = rfn.replace(/\//gi, '-');
  return rfn;
};

const reportFileData = (projectMetaData) => {
  projectMetaData.dependencies.dependency.forEach(d => {
    const nameUpperCase = typeof d.licenses.license[0].name === 'string'
          ? d.licenses.license[0].name.split(' ')[0].toUpperCase()
          : d.licenses.license[0].name[0];

    if (d.file && d.file.includes(nameUpperCase)) {
      const link = join('./', d.file);
      d.localLicense = require('querystring').escape(link).replace(/%2F/gi, '/');
      d.linkLabel = link;
    } else {
      d.localLicense = '#';
      d.linkLabel = 'No local license could be found for the dependency';
    }
  });
  return projectMetaData;
};

const htmlReport = (reportData) => {
  html.parse(reportData).then(output => {
    fs.writeFileSync(join(ldir, 'license.html'), output);
  });
};

const copyCSS = (css) => {
  if (fs.existsSync(css)) {
    fs.createReadStream(resolve(css))
    .pipe(fs.createWriteStream(join(ldir, 'licenses.css')));
  } else {
    console.error(`CSS file not found: ${css}`);
  }
};

const copyLicenseFile = (name, type, file) => {
  let fileName;
  if (basename(file).toUpperCase() !== 'README.MD') {
    fileName = reportFileName(name, type).toUpperCase();
    fs.createReadStream(file)
     .pipe(fs.createWriteStream(join(ldir, fileName)));
  }
  return fileName;
};

const licenseMetaData = (licenses, canonicalNameMapper, ilc, file) => {
  const licenseData = { license: [] };
  if (dual.isDual(licenses)) {
    let nameUrl = consoleModule.licenseNameUrl(canonicalNameMapper, dual.first(licenses));
    licenseData.license.push({name: dual.first(licenses), url: nameUrl.url});

    nameUrl = consoleModule.licenseNameUrl(canonicalNameMapper, dual.last(licenses));
    licenseData.license.push({name: dual.last(licenses), url: nameUrl.url});
  } else {
    const nameUrl = consoleModule.licenseNameUrl(canonicalNameMapper, licenses);
    licenseData.license.push({name: licenses, url: nameUrl.url});
  }
  return licenseData;
};

const transform = (data, canonicalNameMapper, cwd, ilc) => {
  const projectMetaData = project.projectMetaData(cwd);
  data = consoleModule.declaredDependencies(data, project.dependencies(cwd));
  data = consoleModule.packageMetaData(data);
  data.forEach((d) => {
    if (d.file) {
      d.file = copyLicenseFile(d.packageName, d.licenses, d.file);
    } else {
      console.log(`No license file found for ${d.packageName}`);
    }
    d.licenses = licenseMetaData(d.licenses, canonicalNameMapper, ilc, d.file);
  });
  data.forEach((d) => {
    if (d.licenses.license.length === 2) {
      const clone = JSON.parse(JSON.stringify(d));
      d.licenses.license.pop();
      clone.licenses.license.shift();
      data.push(clone);
    }
  });
  data.sort((a, b) => a.packageName.localeCompare(b.packageName));
  projectMetaData.dependencies.dependency = data;
  messages.show(project.dependencies(cwd), projectMetaData);
  return projectMetaData;
};

const report = (projectMetaData, css) => {
  copyCSS(css);
  htmlReport(reportFileData(projectMetaData));
};

module.exports = {
  report,
  transform
};
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import styled from 'styled-components'

const Wrapper = styled.div`
  border-radius: 8px;
  background: white;

  &:before {
    content: '.';
    display: block;
    color: transparent;
    position: absolute;
    position: absolute;
    top: 5%;
    left: 5%;
    width: 90%;
    height: 90%;
    transition: all 0.25s ease-out;
    box-shadow: 0 16px 32px rgba(14,21,47,0.25), 0 8px 8px rgba(14,21,47,0.25);
  }
`

const Link = styled.a`
  display: block;
  border-radius: 8px;
  height: 21vh;
  background-image: url(${props => props.image});
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  position: relative;
  overflow: hidden;
  text-decoration: none;
`

const Header = styled.h3`
  margin: 0;
  color: white;
  font-size: 18px;
  position: absolute;
  padding: 10px 10px 0 10px;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: linear-gradient(to bottom, black, transparent);
  text-shadow: black 0 2px 4px;
`

const MediumPost = ({ url, title, image }) =>
  <Wrapper>
    <Link image={image} href={url} target="_blank">
      {title}
    </Link>
  </Wrapper>

MediumPost.propTypes = {
  url: PropTypes.object.isRequired,
  title: PropTypes.string.isRequired,
  image: PropTypes.string.isRequired,
}

export default MediumPost
<commit_msg>Update MediumPost.js<commit_after>import PropTypes from 'prop-types'
import styled from 'styled-components'

const Wrapper = styled.div`
  border-radius: 8px;
  background: white;
  position: relative;

  &:before {
    content: '.';
    display: block;
    color: transparent;
    position: absolute;
    position: absolute;
    top: 5%;
    left: 5%;
    width: 90%;
    height: 90%;
    transition: all 0.25s ease-out;
    box-shadow: 0 16px 32px rgba(14,21,47,0.25), 0 8px 8px rgba(14,21,47,0.25);
  }
`

const Link = styled.a`
  display: block;
  border-radius: 8px;
  height: 21vh;
  background-image: url(${props => props.image});
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
  position: relative;
  overflow: hidden;
  text-decoration: none;
`

const Header = styled.h3`
  margin: 0;
  color: white;
  font-size: 18px;
  position: absolute;
  padding: 10px 10px 0 10px;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: linear-gradient(to bottom, black, transparent);
  text-shadow: black 0 2px 4px;
`

const MediumPost = ({ url, title, image }) =>
  <Wrapper>
    <Link image={image} href={url} target="_blank">
      {title}
    </Link>
  </Wrapper>

MediumPost.propTypes = {
  url: PropTypes.object.isRequired,
  title: PropTypes.string.isRequired,
  image: PropTypes.string.isRequired,
}

export default MediumPost
<|endoftext|>


<commit_before>ResourceManager = {};

ResourceManager._store = window.localStorage;

ResourceManager.get = function(key) {
  return ResourceManager._store.getItem(key);
};
ResourceManager.save = function(key, data) {
  ResourceManager._store.setItem(key, JSON.stringify(data));
};

ResourceManager.remove = function(key){
  ResourceManager._store.removeItem(key);
}

ResourceManager.keys = function(){
  Object.keys(ResourceManager._store);
}

ResourceManager.getCacheExpiryDate = function (url) {
	var req = new XMLHttpRequest();
  req.open('GET', url , false);
  req.send(null);
  var date = req.getAllResponseHeaders().split('expires: ')[1].split('\n');
  var d = new Date(date);
  return d.getTime();
};

<commit_msg>removing unused code<commit_after>ResourceManager = {};

ResourceManager._store = window.localStorage;

ResourceManager.get = function(key) {
  return ResourceManager._store.getItem(key);
};
ResourceManager.save = function(key, data) {
  ResourceManager._store.setItem(key, JSON.stringify(data));
};

ResourceManager.remove = function(key){
  ResourceManager._store.removeItem(key);
}

ResourceManager.keys = function(){
  Object.keys(ResourceManager._store);
}

<|endoftext|>


<commit_before>"use strict";

const test = require("tape");

const csv = require("../lib/parse-csv");
const quotes = require("../lib/yahoo-quotes");

test("Yahoo Quotes tests", t => {
    t.plan(5);

    quotes.getHistoricalQuotesFromYahoo({
        symbol: "GOOGL",
        beginDate: new Date(2013, 0, 2), // Jan 2nd, 2013
        endDate: new Date(2013, 3, 6) // Apr 6th, 2013
    }, (err, symbol, res) => {
        t.notOk(err, "check error for getting prices");
        if (!err) {
            const prices = csv.parse(res, {
                skipHeader: true,
                column: 4, // close prices
                reverse: true // from oldest to newest
            });

            t.equal("391.916931", prices[0], "get oldest price");
            t.equal("367.742737", prices[prices.length - 1], "get newest price");
        }
    });

    quotes.getHistoricalQuotesFromYahoo({
        symbol: "IBM190118C00100000",
        beginDate: new Date(2013, 0, 2), // Jan 2nd, 2013
        endDate: new Date(2013, 3, 6) // Apr 6th, 2013
    }, (err, symbol, res) => {
        t.equal(err, "Not Found", "not found for unknown symbol");
        t.notOk(res, "no data for unknown symbol");
    });
});
<commit_msg>Add missing values test for quotes.<commit_after>"use strict";

const test = require("tape");

const csv = require("../lib/parse-csv");
const quotes = require("../lib/yahoo-quotes");

test("Yahoo Quotes tests", t => {
    t.plan(7);

    quotes.getHistoricalQuotesFromYahoo({
        symbol: "GOOGL",
        beginDate: new Date(2013, 0, 2), // Jan 2nd, 2013
        endDate: new Date(2013, 3, 6) // Apr 6th, 2013
    }, (err, symbol, res) => {
        t.notOk(err, "check error for getting prices");
        if (!err) {
            const prices = csv.parse(res, {
                skipHeader: true,
                column: 4, // close prices
                reverse: true // from oldest to newest
            });

            t.equal("391.916931", prices[0], "get oldest price");
            t.equal("367.742737", prices[prices.length - 1], "get newest price");
        }
    });

    quotes.getHistoricalQuotesFromYahoo({
        symbol: "IBM190118C00100000",
        beginDate: new Date(2013, 0, 2), // Jan 2nd, 2013
        endDate: new Date(2013, 3, 6) // Apr 6th, 2013
    }, (err, symbol, res) => {
        t.equal(err, "Not Found", "not found for unknown symbol");
        t.notOk(res, "no data for unknown symbol");
    });

    quotes.getHistoricalQuotesFromYahoo({
        symbol: "CEC.VI",
        beginDate: new Date(2017, 7, 13), // Aug 13th, 2017
        endDate: new Date(2018, 5, 23) // Jun 23th, 2018
    }, (err, symbol, res) => {
        t.equal(err, "contains missing values", "missing values");
        t.notOk(res, "no data for missing values");
    });

});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Linking, Alert, TouchableOpacity, Text, View, Image, TextInput, ListView, TouchableHighlight } from 'react-native';
import helpers from '../helpers/helpers.js';
import styles from '../styles.ios.js';
import AddRecipe from './AddRecipe.ios.js';
import ShareFacebook from './ShareFacebook.ios.js';
import { connect } from "react-redux";

class Recipe extends Component {
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      steps: ds.cloneWithRows(props.recipe.analyzedInstructions[0].steps),
      ingredients: []
    }
  }

  componentWillMount() {
    this.renderIngredients();
    this.renderOptions();
  }

  goBack() {
    this.props.navigator.pop();
  }

  renderIngredients() {
    let ingredients = [];
    for (let ingredient of this.props.recipe.extendedIngredients) {
      ingredients.push(ingredient.originalString);
    }
    this.setState({
      ingredients: ingredients
    })
  }

  renderOptions() {
    const recipe = this.props.recipe;
    recipe.vegan = recipe.vegan ? '∙ Vegan' : false;
    recipe.glutenFree = recipe.glutenFree ? '∙ Gluten Free' : false;
    recipe.dairyFree = recipe.dairyFree ? '∙ Dairy Free' : false;
    if (!recipe.vegan && !recipe.glutenFree && !recipe.dairyFree) {
      recipe.dairyFree = 'No Dietary Restrictions';
    }
    recipe.cheap = recipe.cheap ? '$' : '$$$';
    recipe.sustainable = recipe.sustainable ? '∙ Recipe Sustainable' : false;
    recipe.servings = recipe.servings ? recipe.servings : 'Not available';
    recipe.healthscore = recipe.healthscore ? recipe.healthscore : 'Not available';
    // recipe.sourceUrl
  }

  //DRY click handler for social media deep linking
  shareToSocialMedia(deepLink, appName, storeURL) {
    //deep link into the provided app if installed, else direct user to the proper app store link
    Linking.canOpenURL(appName.toLowerCase() + '://').then(supported => {
      if (!supported) {
        Alert.alert(`You must install the ${appName} app in order to use this feature.`,
                    '',
                    [{text: `Install ${appName}`, onPress: () => Linking.openURL(storeURL)},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(deepLink);
      }
    }).catch(err => /*Alert.alert('Error; ', err), */console.error('Error: ', err));

  shareToInstagram() {
    //deep link into the instagram app if installed
    Linking.canOpenURL('instagram://camera').then(supported => {
      if (!supported) {
        Alert.alert('You must install the Instagram app in order to use this feature.',
                    '',
                    [{text: 'Install Instagram', onPress: () => Linking.openURL('https://itunes.apple.com/us/app/instagram/id389801252?mt=8')},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL('instagram://camera');
      }
    }).catch(err => console.error('An error occurred', err));
  }

  shareToTwitter() {
    //deep link into the twitter app if installed
    Linking.canOpenURL('twitter://camera').then(supported => {
      if (!supported) {
        Alert.alert('You must install the Twitter app in order to use this feature.',
                    '',
                    [{text: 'Install Twitter', onPress: () => Linking.openURL('https://itunes.apple.com/us/app/twitter/id333903271?mt=8')},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(`twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20with%20a%20little%20help%20from%20the%20app%20Snacktime!`);
      }
    }).catch(err => console.error('An error occurred', err));
  }

  render() {
    const ingredients = this.state.ingredients.map((ingredient, i) => {
      return (
        <Text style = {styles.ingredientListText2} key={i}>∙ {ingredient} </Text>
      )
    });
    const recipe = this.props.recipe;
    return (
      <View style={styles.recipe}>
        <View style={styles.resultsTitle}>
          <TouchableHighlight style={styles.backButton} onPress={this.goBack.bind(this)}>
            <Image style={styles.backButtonImage} source={{uri: 'https://cdn0.iconfinder.com/data/icons/vector-basic-tab-bar-icons/48/back_button-128.png'}} />
          </TouchableHighlight>
          <Text style={styles.resultsTitleText}>Instructions for {this.props.food}:</Text>
        </View>
        <View style={styles.ingredientContainer}>
          <View style={styles.ingredientList}>
            <Text> {recipe.vegan} {recipe.glutenFree} {recipe.dairyFree}</Text>
            <Text> {recipe.cheap} {recipe.sustainable} Servings: {recipe.servings} Healthscore: {recipe.healthscore}</Text>
            <Text style={styles.recipeTitle2}>Ingredients:</Text>
            <Text style={styles.ingredientListText2}>{ingredients}</Text>
          </View>
          <View style={styles.recipeImageAndIcons}>
            <Image source={{uri: this.props.image}} style={styles.recipeImage} />
            <View style={styles.iconsContainer}>
              {
                this.props.state.isAuthenticated
                  ? (<ShareFacebook url={this.props.recipe.sourceUrl} />)
                  : (<Text></Text>)
              }
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia('instagram://camera', 'Instagram', 'https://itunes.apple.com/us/app/instagram/id389801252?mt=8')
              }>
                <Image source={require('../public/instagram_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia(
                  `twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20(${this.props.food})%20with%20a%20little%20help%20from%20the%20app%20Snacktime!

                  ${this.props.recipe.sourceUrl}`,
                  'Twitter',
                  'https://itunes.apple.com/us/app/twitter/id333903271?mt=8'
                )
              }>
                <Image source={require('../public/twitter_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <Text style={styles.recipeTitle2}>Directions:</Text>
              <AddRecipe info={this.props.recipe} ingredients={this.state.ingredients} userid={this.props.state.payload}/>
            </View>
          </View>
        </View>
        <View>
          <Text style={styles.recipeTitle2}>Directions:</Text>
        </View>
        <ListView
          style={styles.recipe}
          dataSource={this.state.steps}
          renderRow={(step, i) => {
            let image = step.ingredients[0] ? step.ingredients[0].image : 'https://s3-us-west-1.amazonaws.com/filmedin/food+(1).png';
            return (
            <View
              key={i}
              style={styles.recipeStep}
              underlayColor="grey"
            >
              <Image source={{uri: image}} style={styles.recipeImage} />
              <Text style={styles.recipeStepText}>step {step.number}: {step.step}</Text>
            </View>
            )}}
        />
      </View>
    )
  }
}

export default connect(state => ({
    state: state.facebook
  }), null
)(Recipe);
<commit_msg>recipe save button<commit_after>import React, { Component } from 'react';
import { Linking, Alert, TouchableOpacity, Text, View, Image, TextInput, ListView, TouchableHighlight } from 'react-native';
import helpers from '../helpers/helpers.js';
import styles from '../styles.ios.js';
import AddRecipe from './AddRecipe.ios.js';
import ShareFacebook from './ShareFacebook.ios.js';
import { connect } from "react-redux";

class Recipe extends Component {
  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      steps: ds.cloneWithRows(props.recipe.analyzedInstructions[0].steps),
      ingredients: []
    }
  }

  componentWillMount() {
    this.renderIngredients();
    this.renderOptions();
  }

  goBack() {
    this.props.navigator.pop();
  }

  renderIngredients() {
    let ingredients = [];
    for (let ingredient of this.props.recipe.extendedIngredients) {
      ingredients.push(ingredient.originalString);
    }
    this.setState({
      ingredients: ingredients
    })
  }

  renderOptions() {
    const recipe = this.props.recipe;
    recipe.vegan = recipe.vegan ? '∙ Vegan' : false;
    recipe.glutenFree = recipe.glutenFree ? '∙ Gluten Free' : false;
    recipe.dairyFree = recipe.dairyFree ? '∙ Dairy Free' : false;
    if (!recipe.vegan && !recipe.glutenFree && !recipe.dairyFree) {
      recipe.dairyFree = 'No Dietary Restrictions';
    }
    recipe.cheap = recipe.cheap ? '$' : '$$$';
    recipe.sustainable = recipe.sustainable ? '∙ Recipe Sustainable' : false;
    recipe.servings = recipe.servings ? recipe.servings : 'Not available';
    recipe.healthscore = recipe.healthscore ? recipe.healthscore : 'Not available';
    // recipe.sourceUrl
  }

  //DRY click handler for social media deep linking
  shareToSocialMedia(deepLink, appName, storeURL) {
    //deep link into the provided app if installed, else direct user to the proper app store link
    Linking.canOpenURL(appName.toLowerCase() + '://').then(supported => {
      if (!supported) {
        Alert.alert(`You must install the ${appName} app in order to use this feature.`,
                    '',
                    [{text: `Install ${appName}`, onPress: () => Linking.openURL(storeURL)},
                     {text: 'Not Now'}]
        );
      } else {
        return Linking.openURL(deepLink);
      }
    }).catch(err => /*Alert.alert('Error; ', err), */console.error('Error: ', err));
  }

  render() {
    const ingredients = this.state.ingredients.map((ingredient, i) => {
      return (
        <Text style = {styles.ingredientListText2} key={i}>∙ {ingredient} </Text>
      )
    });
    const recipe = this.props.recipe;

    return (
      <View style={styles.recipe}>
        <View style={styles.resultsTitle}>
          <TouchableHighlight style={styles.backButton} onPress={this.goBack.bind(this)}>
            <Image style={styles.backButtonImage} source={{uri: 'https://cdn0.iconfinder.com/data/icons/vector-basic-tab-bar-icons/48/back_button-128.png'}} />
          </TouchableHighlight>
          <Text style={styles.resultsTitleText}>Instructions for {this.props.food}:</Text>
        </View>
        <View style={styles.ingredientContainer}>
          <View style={styles.ingredientList}>
            <Text> {recipe.vegan} {recipe.glutenFree} {recipe.dairyFree}</Text>
            <Text> {recipe.cheap} {recipe.sustainable} Servings: {recipe.servings} Healthscore: {recipe.healthscore}</Text>
            <Text style={styles.recipeTitle2}>Ingredients:</Text>
            <Text style={styles.ingredientListText2}>{ingredients}</Text>
          </View>
          <View style={styles.recipeImageAndIcons}>
            <Image source={{uri: this.props.image}} style={styles.recipeImage} />
            <View style={styles.iconsContainer}>
              {
                this.props.state.isAuthenticated
                  ? (<ShareFacebook url={this.props.recipe.sourceUrl} />)
                  : (<Text></Text>)
              }
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia('instagram://camera', 'Instagram', 'https://itunes.apple.com/us/app/instagram/id389801252?mt=8')
              }>
                <Image source={require('../public/instagram_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
              <TouchableOpacity onPress={
                () => this.shareToSocialMedia(
                  `twitter://post?message=I%20just%20made%20${this.props.food.split(' ').join('%20')}%20(${this.props.food})%20with%20a%20little%20help%20from%20the%20app%20Snacktime!
                  ${this.props.recipe.sourceUrl}`,
                  'Twitter',
                  'https://itunes.apple.com/us/app/twitter/id333903271?mt=8'
                )
              }>
                <Image source={require('../public/twitter_icon.png')} style={styles.shareIcons} />
              </TouchableOpacity>
            </View>
            <AddRecipe info={this.props.recipe} ingredients={this.state.ingredients} userid={this.props.state.payload}/>
          </View>
        </View>
        <View>
          <Text style={styles.recipeTitle2}>Directions:</Text>
        </View>
        <ListView
          style={styles.recipe}
          dataSource={this.state.steps}
          renderRow={(step, i) => {
            let image = step.ingredients[0] ? step.ingredients[0].image : 'https://s3-us-west-1.amazonaws.com/filmedin/food+(1).png';
            return (
            <View
              key={i}
              style={styles.recipeStep}
              underlayColor="grey"
            >
              <Image source={{uri: image}} style={styles.recipeImage} />
              <Text style={styles.recipeStepText}>step {step.number}: {step.step}</Text>
            </View>
            )}}
        />
      </View>
    )
  }
}

export default connect(state => ({
    state: state.facebook
  }), null
)(Recipe);
<|endoftext|>


<commit_before>// Copyright (c) 2012 HIRAKI Satoru, https://github.com/Layzie
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

(function(window, undefined) {
  'use strict';

  var _selectStorage = function _selectStorage(flag) {
    var storage;

    if (typeof flag !== 'boolean' && flag !== undefined) {
      throw new Error('2nd argument should be boolean');
    } else {
      if (flag === true || flag === undefined) {
        storage = localStorage;
      } else {
        storage = sessionStorage;
      }
    }

    return storage;
  };

  if (typeof window.RS === 'undefined') {
    window.RS = {};
  }

  window.RS = {
    put: function put(obj, flag) {
      var storage = _selectStorage(flag);

      if (typeof obj === 'object' && !(obj instanceof Array)) {
        for (var i in obj) {
          if (obj.hasOwnProperty(i)) {
            storage.setItem(i, JSON.stringify(obj[i]));
          }
        }
      } else {
        throw new Error('1st argument should be object');
      }
    },
    get: function get(key, flag) {
      var storage = _selectStorage(flag),
          selectKey = storage.getItem(key);

      if (typeof key === 'string') {
        if (selectKey) {
          return JSON.parse(selectKey);
        } else {
          return console.log('This key is not in storage');
        }
      } else {
        throw new Error('1st argument should be string');
      }
    },
    remove: function remove(key, flag) {
      var storage = _selectStorage(flag);

      storage.removeItem(key);
    },
    clear: function clear(flag) {
      var storage = _selectStorage(flag);

      storage.clear();
    },
    is: function(key, flag) {
      var storage = _selectStorage(flag),
          selectKey = storage.getItem(key);

      if (typeof key === 'string') {
        if (selectKey) {
          return true;
        } else {
          return false;
        }
      } else {
        throw new Error('1st argument should be strings');
      }
    }
  };
})(this);

<commit_msg>add if statement for RS.remove();<commit_after>// Copyright (c) 2012 HIRAKI Satoru, https://github.com/Layzie
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

(function(window, undefined) {
  'use strict';

  var _selectStorage = function _selectStorage(flag) {
    var storage;

    if (typeof flag !== 'boolean' && flag !== undefined) {
      throw new Error('2nd argument should be boolean');
    } else {
      if (flag === true || flag === undefined) {
        storage = localStorage;
      } else {
        storage = sessionStorage;
      }
    }

    return storage;
  };

  if (typeof window.RS === 'undefined') {
    window.RS = {};
  }

  window.RS = {
    put: function put(obj, flag) {
      var storage = _selectStorage(flag);

      if (typeof obj === 'object' && !(obj instanceof Array)) {
        for (var i in obj) {
          if (obj.hasOwnProperty(i)) {
            storage.setItem(i, JSON.stringify(obj[i]));
          }
        }
      } else {
        throw new Error('1st argument should be object');
      }
    },
    get: function get(key, flag) {
      var storage = _selectStorage(flag),
          selectKey = storage.getItem(key);

      if (typeof key === 'string') {
        if (selectKey) {
          return JSON.parse(selectKey);
        } else {
          return console.log('This key is not in storage');
        }
      } else {
        throw new Error('1st argument should be string');
      }
    },
    remove: function remove(key, flag) {
      var storage = _selectStorage(flag),
          selectKey = storage.getItem(key);

      if (typeof key === 'string') {
        if (selectKey) {
          storage.removeItem(key);
        } else {
          console.log('This key is not in storage');
        }
      }
    },
    clear: function clear(flag) {
      var storage = _selectStorage(flag);

      storage.clear();
    },
    is: function(key, flag) {
      var storage = _selectStorage(flag),
          selectKey = storage.getItem(key);

      if (typeof key === 'string') {
        if (selectKey) {
          return true;
        } else {
          return false;
        }
      } else {
        throw new Error('1st argument should be strings');
      }
    }
  };
})(this);

<|endoftext|>


<commit_before>import React from 'react'
import {Glyphicon} from 'react-bootstrap'
import style from '../css/Style'
import {selectionArray, occurrencesInString, normalizeString} from '../utils/selectionHelpers'
import MyLanguageModal from './MyLanguageModal'

class SelectArea extends React.Component {
  constructor() {
    super();
    this.state = {
      inBox: false,
      modalVisibility: false
    }
  }
/*
 * @description
 * Implementation notes on why you can't just use the window.getSelection()
 * getSelection is limited by same innerText node, and does not include span siblings
 * indexOfTextSelection is broken by any other previous selection since it only knows its innerText node.
 */
  getSelectionText() {
    if (!this.props.loginReducer.loggedInUser) {
      this.props.actions.selectModalTab(1, 1, true);
      this.props.actions.openAlertDialog("You must be logged in to make a selection");
      return;
    }
    // windowSelection is an object with lots of data
    let windowSelection = window.getSelection();
    // get the current text selected
    let selectedText = windowSelection.toString();

    // do nothing since an empty space was selected
    if (selectedText === '') {} else {
      // get the text after the presceding selection and current span selection is in.
      let selectionRange = windowSelection.getRangeAt(0)
      // get the character index of what is selected in context of the span it is in.
      let indexOfTextSelection = selectionRange.startOffset;
      // get the container of the selection, this is a strange object, that logs as a string.
      let textContainer = selectionRange.commonAncestorContainer;
      // get the parent span that contains the textContainer.
      let textSpan = textContainer ? textContainer.parentElement : undefined;
      // get all of the text in the selection's container similar to the span's innerText.
      let textSpanContent = textSpan.innerText;
      // get the text presceding the selection but after the selection just prior to it.
      let postPrescedingText = textContainer ? textSpanContent.slice(0,indexOfTextSelection) : '';
      // start with an empty string to prepend to for text presceding current span selection is in.
      let prescedingText = '';
      // if we have a span that holds text, see what presceding text we can extract.
      if (textSpan) {
        // get the previous sibling to start the loop
        let previousSibling = textSpan.previousSibling;
        // loop through previous spans to get their text
        while (previousSibling) {
          // prepend the spans innerText to the prescedingText
          prescedingText = previousSibling.innerText + prescedingText;
          // move to the previous span, if none, it ends the loop
          previousSibling = previousSibling.previousSibling;
        }
      }

      // There can be a gap between prescedingText and current selection
      let textBeforeSelection = prescedingText + postPrescedingText + selectedText;
      // get the occurrence of the selection
      let occurrence = occurrencesInString(textBeforeSelection, selectedText);
      // verseText is used to get all of the occurrences
      let verseText = this.props.verseText;
      // replace more than one contiguous space with a single one since HTML/selection only renders 1
      verseText = normalizeString(verseText);
      // get the total occurrences from the verse
      let occurrences = occurrencesInString(verseText, selectedText);
      let selection = {
        text: selectedText,
        occurrence: occurrence,
        occurrences: occurrences
      };
      // add the selection to the selections
      this.addSelection(selection);
    }
  }

  addSelection(selection) {
    let selections = this.props.selectionsReducer.selections;
    if (selections.length >= 4) {
      this.props.actions.openAlertDialog('Click a previous selection to remove it before adding a new one. To select more than 4 words, highlight phrases instead of individual words.')
      return false
    } else {
      selections.push(selection);
    }
    this.props.actions.changeSelections(selections);
  }

  removeSelection(selection) {
    let selections = this.props.selectionsReducer.selections;
    selections = selections.filter(_selection =>
      _selection.occurrence !== selection.occurrence || _selection.text !== selection.text
    )
    this.props.actions.changeSelections(selections);
  }

  displayText() {
    let { selections } = this.props.selectionsReducer;
    let verseText = this.props.verseText;
    // normalize whitespace for text rendering in order to display highlights with more than one space since html selections show one space
    verseText = normalizeString(verseText);
    let verseTextSpans = {verseText};
    if (selections && selections.length > 0) {
      let _selectionArray = selectionArray(verseText, selections);
      selections.forEach(selection => {
        if (!verseText.includes(selection.text)) {
          // validate selections and remove ones that do not apply
          console.log(verseText, selection);
          this.props.actions.validateSelections(verseText);
        }
      })
      verseTextSpans = _selectionArray.map((selection, index) =>
         this.removeSelection(selection) : () => { }}>
          {selection.text}

      )
    }

    if (this.props.mode == "select") {
      return (

 this.getSelectionText()} onMouseLeave={()=>this.inDisplayBox(false)} onMouseEnter={()=>this.inDisplayBox(true)}>
          {verseTextSpans}

      );
    } else {
      return (

          {verseTextSpans}

      )
    }

  }

  inDisplayBox(insideDisplayBox) {
    this.setState({ inBox: insideDisplayBox });
    if (!insideDisplayBox && Math.abs(window.getSelection().extentOffset - window.getSelection().baseOffset) > 0) {
      this.getSelectionText()
    }
    window.getSelection().empty();
  }

  render() {
    let {verseText, projectDetailsReducer} = this.props
    // normalize whitespace, since html selections will not include more than 1 contiguous space
    verseText = normalizeString(verseText);
    const { manifest, bookName } = projectDetailsReducer;

    let reference = this.props.contextIdReducer.contextId.reference;
    let bibles = this.props.resourcesReducer.bibles;
    let languageName = manifest.target_language ? manifest.target_language.name : null;
    let modal =
;

    let dir = manifest.target_language ? manifest.target_language.direction : null;

    if (this.state.modalVisibility) {
      modal = (
        <MyLanguageModal
          show={this.state.modalVisibility}
          targetLangBible={bibles.targetLanguage}
          chapter={reference.chapter}
          currentVerse={reference.verse}
          dir = {dir ? dir : "ltr"}
          onHide={
            () => {
              this.setState({modalVisibility: false})
            }
          }
        />
      )
    }

    return (

                {languageName}

                {bookName} {reference.chapter + ':' + reference.verse}

 {
            this.setState({modalVisibility: true})
          }}>
            <Glyphicon glyph="fullscreen" title="Click to show expanded verses" style={{cursor: "pointer"}}/>
            {modal}

          {this.displayText()}

    )
  }
}

export default SelectArea
<commit_msg>removed console.log<commit_after>import React from 'react'
import {Glyphicon} from 'react-bootstrap'
import style from '../css/Style'
import {selectionArray, occurrencesInString, normalizeString} from '../utils/selectionHelpers'
import MyLanguageModal from './MyLanguageModal'

class SelectArea extends React.Component {
  constructor() {
    super();
    this.state = {
      inBox: false,
      modalVisibility: false
    }
  }
/*
 * @description
 * Implementation notes on why you can't just use the window.getSelection()
 * getSelection is limited by same innerText node, and does not include span siblings
 * indexOfTextSelection is broken by any other previous selection since it only knows its innerText node.
 */
  getSelectionText() {
    if (!this.props.loginReducer.loggedInUser) {
      this.props.actions.selectModalTab(1, 1, true);
      this.props.actions.openAlertDialog("You must be logged in to make a selection");
      return;
    }
    // windowSelection is an object with lots of data
    let windowSelection = window.getSelection();
    // get the current text selected
    let selectedText = windowSelection.toString();

    // do nothing since an empty space was selected
    if (selectedText === '') {} else {
      // get the text after the presceding selection and current span selection is in.
      let selectionRange = windowSelection.getRangeAt(0)
      // get the character index of what is selected in context of the span it is in.
      let indexOfTextSelection = selectionRange.startOffset;
      // get the container of the selection, this is a strange object, that logs as a string.
      let textContainer = selectionRange.commonAncestorContainer;
      // get the parent span that contains the textContainer.
      let textSpan = textContainer ? textContainer.parentElement : undefined;
      // get all of the text in the selection's container similar to the span's innerText.
      let textSpanContent = textSpan.innerText;
      // get the text presceding the selection but after the selection just prior to it.
      let postPrescedingText = textContainer ? textSpanContent.slice(0,indexOfTextSelection) : '';
      // start with an empty string to prepend to for text presceding current span selection is in.
      let prescedingText = '';
      // if we have a span that holds text, see what presceding text we can extract.
      if (textSpan) {
        // get the previous sibling to start the loop
        let previousSibling = textSpan.previousSibling;
        // loop through previous spans to get their text
        while (previousSibling) {
          // prepend the spans innerText to the prescedingText
          prescedingText = previousSibling.innerText + prescedingText;
          // move to the previous span, if none, it ends the loop
          previousSibling = previousSibling.previousSibling;
        }
      }

      // There can be a gap between prescedingText and current selection
      let textBeforeSelection = prescedingText + postPrescedingText + selectedText;
      // get the occurrence of the selection
      let occurrence = occurrencesInString(textBeforeSelection, selectedText);
      // verseText is used to get all of the occurrences
      let verseText = this.props.verseText;
      // replace more than one contiguous space with a single one since HTML/selection only renders 1
      verseText = normalizeString(verseText);
      // get the total occurrences from the verse
      let occurrences = occurrencesInString(verseText, selectedText);
      let selection = {
        text: selectedText,
        occurrence: occurrence,
        occurrences: occurrences
      };
      // add the selection to the selections
      this.addSelection(selection);
    }
  }

  addSelection(selection) {
    let selections = this.props.selectionsReducer.selections;
    if (selections.length >= 4) {
      this.props.actions.openAlertDialog('Click a previous selection to remove it before adding a new one. To select more than 4 words, highlight phrases instead of individual words.')
      return false
    } else {
      selections.push(selection);
    }
    this.props.actions.changeSelections(selections);
  }

  removeSelection(selection) {
    let selections = this.props.selectionsReducer.selections;
    selections = selections.filter(_selection =>
      _selection.occurrence !== selection.occurrence || _selection.text !== selection.text
    )
    this.props.actions.changeSelections(selections);
  }

  displayText() {
    let { selections } = this.props.selectionsReducer;
    let verseText = this.props.verseText;
    // normalize whitespace for text rendering in order to display highlights with more than one space since html selections show one space
    verseText = normalizeString(verseText);
    let verseTextSpans = {verseText};
    if (selections && selections.length > 0) {
      let _selectionArray = selectionArray(verseText, selections);
      selections.forEach(selection => {
        if (!verseText.includes(selection.text)) {
          // validate selections and remove ones that do not apply
          this.props.actions.validateSelections(verseText);
        }
      })
      verseTextSpans = _selectionArray.map((selection, index) =>
         this.removeSelection(selection) : () => { }}>
          {selection.text}

      )
    }

    if (this.props.mode == "select") {
      return (

 this.getSelectionText()} onMouseLeave={()=>this.inDisplayBox(false)} onMouseEnter={()=>this.inDisplayBox(true)}>
          {verseTextSpans}

      );
    } else {
      return (

          {verseTextSpans}

      )
    }

  }

  inDisplayBox(insideDisplayBox) {
    this.setState({ inBox: insideDisplayBox });
    if (!insideDisplayBox && Math.abs(window.getSelection().extentOffset - window.getSelection().baseOffset) > 0) {
      this.getSelectionText()
    }
    window.getSelection().empty();
  }

  render() {
    let {verseText, projectDetailsReducer} = this.props
    // normalize whitespace, since html selections will not include more than 1 contiguous space
    verseText = normalizeString(verseText);
    const { manifest, bookName } = projectDetailsReducer;

    let reference = this.props.contextIdReducer.contextId.reference;
    let bibles = this.props.resourcesReducer.bibles;
    let languageName = manifest.target_language ? manifest.target_language.name : null;
    let modal =
;

    let dir = manifest.target_language ? manifest.target_language.direction : null;

    if (this.state.modalVisibility) {
      modal = (
        <MyLanguageModal
          show={this.state.modalVisibility}
          targetLangBible={bibles.targetLanguage}
          chapter={reference.chapter}
          currentVerse={reference.verse}
          dir = {dir ? dir : "ltr"}
          onHide={
            () => {
              this.setState({modalVisibility: false})
            }
          }
        />
      )
    }

    return (

                {languageName}

                {bookName} {reference.chapter + ':' + reference.verse}

 {
            this.setState({modalVisibility: true})
          }}>
            <Glyphicon glyph="fullscreen" title="Click to show expanded verses" style={{cursor: "pointer"}}/>
            {modal}

          {this.displayText()}

    )
  }
}

export default SelectArea
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function() {

  var isServer = typeof window === 'undefined';
  var isWorker = typeof importScripts !== 'undefined';

  var flags    = this.FOAM_FLAGS || {};
  flags.web    = ! isServer,
  flags.node   = isServer;
  flags.loader = ! isServer;
  if ( ! flags.hasOwnProperty('debug') ) flags.debug = true;

  function createLoadBrowser() {
    var path = document.currentScript && document.currentScript.src;

    // document.currentScript isn't supported on all browsers, so the following
    // hack gets the job done on those browsers.
    if ( ! path ) {
      var scripts = document.getElementsByTagName('script');
      for ( var i = 0 ; i < scripts.length ; i++ ) {
        if ( scripts[i].src.match(/\/foam.js$/) ) {
          path = scripts[i].src;
          break;
        }
      }
    }

    path = path.substring(0, path.lastIndexOf('/')+1);

    return function(filename) {
      document.writeln(
        '<script type="text/javascript" src="' + path + filename + '.js"></script>\n');
    };
  }

  function loadServer(filename) {
    require('./' + filename + '.js');
  }

  function createLoadWorker(filename) {
    var path = FOAM_BOOT_PATH;
    return function(filename) {
      importScripts(path + filename + '.js');
    };
  }

  var load = isServer ? loadServer :
    isWorker ? createLoadWorker() :
    createLoadBrowser();

  this.FOAM_FILES = function(files) {
    files.
      filter(function(f) {
        if ( ! f.flags ) return true;
        for ( var i = 0; i < f.flags.length; i++ ) {
          if ( ! flags[f.flags[i]] ) return false;
        }
        return true;
      }).
      map(function(f) { return f.name; }).
      forEach(load);

  //  delete this.FOAM_FILES;
  };

  load('files');
})();
<commit_msg>foam.js now sets 'js' flag.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function() {

  var isServer = typeof window === 'undefined';
  var isWorker = typeof importScripts !== 'undefined';

  var flags    = this.FOAM_FLAGS || {};
  flags.js     = true;
  flags.web    = ! isServer,
  flags.node   = isServer;
  flags.loader = ! isServer;
  if ( ! flags.hasOwnProperty('debug') ) flags.debug = true;

  function createLoadBrowser() {
    var path = document.currentScript && document.currentScript.src;

    // document.currentScript isn't supported on all browsers, so the following
    // hack gets the job done on those browsers.
    if ( ! path ) {
      var scripts = document.getElementsByTagName('script');
      for ( var i = 0 ; i < scripts.length ; i++ ) {
        if ( scripts[i].src.match(/\/foam.js$/) ) {
          path = scripts[i].src;
          break;
        }
      }
    }

    path = path.substring(0, path.lastIndexOf('/')+1);

    return function(filename) {
      document.writeln(
        '<script type="text/javascript" src="' + path + filename + '.js"></script>\n');
    };
  }

  function loadServer(filename) {
    require('./' + filename + '.js');
  }

  function createLoadWorker(filename) {
    var path = FOAM_BOOT_PATH;
    return function(filename) {
      importScripts(path + filename + '.js');
    };
  }

  var load = isServer ? loadServer :
    isWorker ? createLoadWorker() :
    createLoadBrowser();

  this.FOAM_FILES = function(files) {
    files.
      filter(function(f) {
        if ( ! f.flags ) return true;
        for ( var i = 0; i < f.flags.length; i++ ) {
          if ( ! flags[f.flags[i]] ) return false;
        }
        return true;
      }).
      map(function(f) { return f.name; }).
      forEach(load);

  //  delete this.FOAM_FILES;
  };

  load('files');
})();
<|endoftext|>


<commit_before>var React = require("react");
var TestUtils = require('react-testutils-additions');

var CertificateBuilder = require('../../../../testUtilities/builders/certificateBuilder');
var httpBackend = require('../../../../testUtilities/httpBackend');

var ComponentNameAdapter = require('editor/components/fields/componentName');
var componentNameStore = require('editor/components/fields/componentName/componentNameStore');

fdescribe('The ComponentName Adapter', () => {
  var component;

  beforeAll(done => {
    var certificate = new CertificateBuilder("1234")
      .build();

    var props = {
      certificate,
      options: {
        propertyName: 'componentName',
        resourceKey: 'ComponentName'
      },
      editMode: true
    };

    httpBackend.mock(/api\/certificates\/1234\/audittrail/, [{}]);

    componentNameStore.listen(state => {
      if (state.status === componentNameStore.statusCodes.READY) {
        setTimeout(done);
      }
    });

    component = TestUtils.renderIntoDocument(<ComponentNameAdapter {...props} />);
  });

  it('can be rendered', () => {
    expect(component).toBeDefined();
  });

  it('will render the dropdown', () => {
    var controls = TestUtils.find(component, '.form-control');
    expect(controls.length).toBe(1);
  });

});
<commit_msg>Added naming to the adapter test<commit_after>var React = require("react");
var TestUtils = require('react-testutils-additions');

var CertificateBuilder = require('../../../../testUtilities/builders/certificateBuilder');
var httpBackend = require('../../../../testUtilities/httpBackend');

var <%= componentNamePC %>Adapter = require('editor/components/fields/<%= componentNameCC %>');
var <%= componentNameCC %>Store = require('editor/components/fields/<%= componentNameCC %>/<%= componentNameCC %>Store');

fdescribe('The <%= componentNamePC %> Adapter', () => {
  var component;

  beforeAll(done => {
    var certificate = new CertificateBuilder("1234")
      .build();

    var props = {
      certificate,
      options: {
        propertyName: '<%= componentNameCC %>',
        resourceKey: '<%= componentNamePC %>'
      },
      editMode: true
    };

    httpBackend.mock(/api\/certificates\/1234\/audittrail/, [{}]);

    <%= componentNameCC %>Store.listen(state => {
      if (state.status === <%= componentNameCC %>Store.statusCodes.READY) {
        setTimeout(done);
      }
    });

    component = TestUtils.renderIntoDocument(<<%= componentNamePC %>Adapter {...props} />);
  });

  it('can be rendered', () => {
    expect(component).toBeDefined();
  });

  it('will render the dropdown', () => {
    var controls = TestUtils.find(component, '.form-control');
    expect(controls.length).toBe(1);
  });

});
<|endoftext|>


<commit_before>var roleHarvester = require('role.harvester');
var roleUpgrader = require('role.upgrader');

module.exports.loop = function () {

    // Always place this memory cleaning code at the very top of your main loop!

    for (var name in Memory.creeps) {
        if (!Game.creeps[name]) {
            delete Memory.creeps[name];
            // console.log('Clearing non-existing creep memory:', name);
        }
    }

    var harvesters = _.filter(Game.creeps, (creep) => creep.memory.role == 'harvester'
    );
    // console.log('Harvesters: ' + harvesters.length);

    if (harvesters.length < 4) {
        var newName = Game.spawns.Spawn1.createCreep([WORK, CARRY, MOVE], undefined, {role: 'harvester'});
        // console.log('Spawning new harvester: ' + newName);
    }

    var upgraders = _.filter(Game.creeps, (creep) => creep.memory.role == 'upgrader'
    );
    // console.log('Updaters: ' + updaters.length);

    if (upgraders.length < 4) {
        var newName = Game.spawns.Spawn1.createCreep([WORK, CARRY, MOVE], undefined, {role: 'upgrader'});
        // console.log('Spawning new upgrader: ' + newName);
    }

    for (var name in Game.creeps) {
        var creep = Game.creeps[name];
        if (creep.memory.role == 'harvester') {
            roleHarvester.run(creep);
        }
        if (creep.memory.role == 'upgrader') {
            roleUpgrader.run(creep);
        }
    }
};

<commit_msg>Code reformat<commit_after>var roleHarvester = require('role.harvester');
var roleUpgrader = require('role.upgrader');

module.exports.loop = function () {

    // Always place this memory cleaning code at the very top of your main loop!

    for (var dead in Memory.creeps) {
        if (!Game.creeps[dead]) {
            delete Memory.creeps[dead];
        }
    }

    var harvesters = _.filter(Game.creeps, (creep) => creep.memory.role == 'harvester');
    if (harvesters.length < 4) {
        Game.spawns.Spawn1.createCreep([WORK, CARRY, MOVE], undefined, {role: 'harvester'});
    }

    var upgraders = _.filter(Game.creeps, (creep) => creep.memory.role == 'upgrader');
    if (upgraders.length < 4) {
        Game.spawns.Spawn1.createCreep([WORK, CARRY, MOVE], undefined, {role: 'upgrader'});
    }

    for (var c in Game.creeps) {
        var creep = Game.creeps[c];
        if (creep.memory.role == 'harvester') {
            roleHarvester.run(creep);
        }
        if (creep.memory.role == 'upgrader') {
            roleUpgrader.run(creep);
        }
    }
};

<|endoftext|>


<commit_before>/**
 * Scheduler
 *
 * @param  {boolean} options.logSuccessfulJob logs output when job is executed succesfully (verbose)
 * @return {object}  scheduler
 */

module.exports = function(options) {

  options = options || {};

  var scheduler = {
    domain: require('domain').create(),

    initialize: function() {
      this.domain.on("error", function(error) {
        console.error(error.stack);
      });
    },

    schedule: function(job_worker, widgets) {

      var thiz = this;
      var task = this.domain.bind(job_worker.task);

      if (!job_worker.config.interval){
        job_worker.config.interval = 60 * 1000; // default to 60 secs if not provided
      }
      else if (job_worker.config.interval < 1000){
        job_worker.config.interval = 1000; // minium 1 sec
      }

      if (!job_worker.config.original_interval) // set original interval so we can get back to it
        job_worker.config.original_interval = job_worker.config.interval;

      try {

        // TODO
        // - We are still passing job_worker.dependencies as a parameter for backwards compatibility
        // but makes more sense to be passed as a property of job_worker.
        // - The same with config
        task.call(job_worker, job_worker.config, job_worker.dependencies, function(err, data){
          if (err){
            job_worker.dependencies.logger.error('executed with errors: ' + err);

            // in case of error retry in one third of the original interval or 1 min, whatever is lower
            job_worker.config.interval = Math.min(job_worker.config.original_interval / 3, 60000);

            // -------------------------------------------------------------
            // Decide if we hold error notification according to widget config.
            // if the retryOnErrorTimes property found in config, the error notification
            // won´t be sent until we reach that number of consecutive errrors.
            // This will prevent showing too many error when connection to flaky, unreliable
            // servers.
            // -------------------------------------------------------------
            var sendError = true;
            if (job_worker.config.retryOnErrorTimes){
              job_worker.retryOnErrorCounter = job_worker.retryOnErrorCounter || 0;
              if (job_worker.retryOnErrorCounter <= job_worker.config.retryOnErrorTimes){
                job_worker.dependencies.logger.warn('widget with retryOnErrorTimes. attempts: '+ job_worker.retryOnErrorCounter);
                sendError = false;
                job_worker.retryOnErrorCounter ++;
              }
            }
            if (sendError){
              widgets.sendData({error : err, config: {interval: job_worker.config.interval}});
            }
          }
          else{
            job_worker.retryOnErrorCounter = 0; //reset error counter on success
            if (options.logSuccessfulJob !== false) {
              job_worker.dependencies.logger.log('executed OK');
            }
            job_worker.config.interval = job_worker.config.original_interval;
            data.config = {interval: job_worker.config.interval}; // give access to interval to client
            widgets.sendData(data);
          }
        });
      }
      catch (e){
        job_worker.dependencies.logger.error('Uncaught exception executing job: ' + e);
      }

      setTimeout(function(){ //schedule next job
        thiz.schedule(job_worker, widgets);
      }, job_worker.config.interval || 60 * 1000);
    }

  };
  scheduler.initialize();
  return scheduler;
};
<commit_msg>Fixing schedulers: in the old implementation next job was scheduled before old executed, so if job is working really long, then this would execute many jobs.<commit_after>/**
 * Scheduler
 *
 * @param  {boolean} options.logSuccessfulJob logs output when job is executed succesfully (verbose)
 * @return {object}  scheduler
 */

module.exports = function(options) {

  options = options || {};

  var scheduler = {
    domain: require('domain').create(),

    initialize: function() {
      this.domain.on("error", function(error) {
        console.error(error.stack);
      });
    },

    scheduleNext: function(job_worker, widgets) {
      setTimeout(function(){
        this.schedule(job_worker, widgets);
      }, job_worker.config.interval || 60 * 1000);
    },

    schedule: function(job_worker, widgets) {

      var thiz = this;
      var task = this.domain.bind(job_worker.task);

      if (!job_worker.config.interval){
        job_worker.config.interval = 60 * 1000; // default to 60 secs if not provided
      }
      else if (job_worker.config.interval < 1000){
        job_worker.config.interval = 1000; // minium 1 sec
      }

      if (!job_worker.config.original_interval) // set original interval so we can get back to it
        job_worker.config.original_interval = job_worker.config.interval;

      try {

        // TODO
        // - We are still passing job_worker.dependencies as a parameter for backwards compatibility
        // but makes more sense to be passed as a property of job_worker.
        // - The same with config
        task.call(job_worker, job_worker.config, job_worker.dependencies, function(err, data){
          if (err){
            job_worker.dependencies.logger.error('executed with errors: ' + err);

            // in case of error retry in one third of the original interval or 1 min, whatever is lower
            job_worker.config.interval = Math.min(job_worker.config.original_interval / 3, 60000);

            // -------------------------------------------------------------
            // Decide if we hold error notification according to widget config.
            // if the retryOnErrorTimes property found in config, the error notification
            // won´t be sent until we reach that number of consecutive errrors.
            // This will prevent showing too many error when connection to flaky, unreliable
            // servers.
            // -------------------------------------------------------------
            var sendError = true;
            if (job_worker.config.retryOnErrorTimes){
              job_worker.retryOnErrorCounter = job_worker.retryOnErrorCounter || 0;
              if (job_worker.retryOnErrorCounter <= job_worker.config.retryOnErrorTimes){
                job_worker.dependencies.logger.warn('widget with retryOnErrorTimes. attempts: '+ job_worker.retryOnErrorCounter);
                sendError = false;
                job_worker.retryOnErrorCounter ++;
              }
            }
            if (sendError){
              widgets.sendData({error : err, config: {interval: job_worker.config.interval}});
            }
          }
          else{
            job_worker.retryOnErrorCounter = 0; //reset error counter on success
            if (options.logSuccessfulJob !== false) {
              job_worker.dependencies.logger.log('executed OK');
            }
            job_worker.config.interval = job_worker.config.original_interval;
            data.config = {interval: job_worker.config.interval}; // give access to interval to client
            widgets.sendData(data);
          }
          thiz.scheduleNext(job_worker, widgets);
        });
      }
      catch (e){
        job_worker.dependencies.logger.error('Uncaught exception executing job: ' + e);

        // job failed, schedule next
        thiz.scheduleNext(job_worker, widgets);
      }
    }

  };
  scheduler.initialize();
  return scheduler;
};
<|endoftext|>


<commit_before>import React from 'react';

import { storiesOf }                                        from '@storybook/react';
import { action }                                           from '@storybook/addon-actions';
import { withKnobs, boolean, object, text, select, number } from '@storybook/addon-knobs';
import { withInfo }                                         from '@storybook/addon-info';

import ReactCodeInput from './ReactCodeInput.js';
import CodeInputField from "./ReactCodeInput";

const stories = storiesOf('ReactCodeInput', module);
const propVariantStories = storiesOf('ReactCodeInput/Props', module);

const typeLabel = 'Type';
const typeOptions = ['text', 'number', 'password', 'tel'];
const typeDefaultValue = 'text';
const typeGroupId = 'Type';

const inputModeOptions = [
  'verbatim', 'latin', 'latin-name', 'latin-prose',
  'full-width-latin', 'kana', 'kana-name', 'katakana',
  'numeric', 'tel', 'email', 'url',
];

const inputStyle = {
  fontFamily:      'monospace',
  MozAppearance:   'textfield',
  borderRadius:    '6px',
  border:          '1px solid',
  boxShadow:       '0px 0px 10px 0px rgba(0,0,0,.10)',
  margin:          '4px',
  paddingLeft:     '8px',
  width:           '36px',
  height:          '42px',
  fontSize:        '32px',
  boxSizing:       'border-box',
  color:           'black',
  backgroundColor: 'white',
  borderColor:     'lightgrey',
};

const darkStyle = {
  fontFamily:      'monospace',
  MozAppearance:   'textfield',
  borderRadius:    '3px',
  border:          '1px solid',
  margin:          '4px',
  paddingLeft:     '7px',
  width:           '15px',
  height:          '26px',
  fontSize:        '14px',
  color:           'lightskyblue',
  backgroundColor: 'black',
  borderColor:     'lightskyblue',
};

// Creation of Stories
stories.addDecorator((story, context) => withInfo('Details')(story)(context));
stories.addDecorator(withKnobs);

// Stories for Input Field
stories
  .add(
    'Default', () =>
      <ReactCodeInput />
  )
  .add(
    'Dark Themed', () =>

        <ReactCodeInput inputStyle={darkStyle} type="number" />

  )
  .add(
    'Force Uppercase', () =>
      <CodeInputField fields={4} type="text" forceUppercase value="test" onChange={action('onChange')}/>
  );

// Creation of Props
propVariantStories.addDecorator((story, context) => withInfo('Details')(story)(context));
propVariantStories.addDecorator(withKnobs);

propVariantStories
  .add('type', () =>
    <ReactCodeInput type={select(typeLabel, typeOptions, typeDefaultValue, typeGroupId)} />
  )
  .add('fields', () =>
    <ReactCodeInput fields={number('fields', 6)} />
  )
  .add('inputStyle', () =>
    <ReactCodeInput inputStyle={object('inputStyle', inputStyle)} />
  )
  .add('value', () =>
    <ReactCodeInput value={text('value', '1234')} />
  )
  .add('isValid', () =>
    <ReactCodeInput isValid={boolean('isValid', false)} />
  )
  .add('inputMode', () =>
    <ReactCodeInput inputMode={select('inputMode', inputModeOptions)} />
  )
  .add('onChange', () =>
    <ReactCodeInput onChange={action('onChange')} />
  )
  .add('pattern', () =>
    <form>
      <ReactCodeInput value={text('value', '123')} pattern={text('pattern', '[A-Z]')} />
        <input style={{ ...inputStyle, width: 'auto', backgroundColor: 'lightgray' }} type="submit" value="Submit" />
    </form>
  )
  .add('touch', () =>
    <ReactCodeInput touch={action('touch')} />
  )
  .add('untouch', () =>
    <ReactCodeInput untouch={action('untouch')} />
  )
  .add('disabled', () =>
    <ReactCodeInput disabled={boolean('disabled', true)} />
  );
<commit_msg>docs(forceUppercase): added onChange event<commit_after>import React from 'react';

import { storiesOf }                                        from '@storybook/react';
import { action }                                           from '@storybook/addon-actions';
import { withKnobs, boolean, object, text, select, number } from '@storybook/addon-knobs';
import { withInfo }                                         from '@storybook/addon-info';

import ReactCodeInput from './ReactCodeInput.js';
import CodeInputField from "./ReactCodeInput";

const stories = storiesOf('ReactCodeInput', module);
const propVariantStories = storiesOf('ReactCodeInput/Props', module);

const typeLabel = 'Type';
const typeOptions = ['text', 'number', 'password', 'tel'];
const typeDefaultValue = 'text';
const typeGroupId = 'Type';

const inputModeOptions = [
  'verbatim', 'latin', 'latin-name', 'latin-prose',
  'full-width-latin', 'kana', 'kana-name', 'katakana',
  'numeric', 'tel', 'email', 'url',
];

const inputStyle = {
  fontFamily:      'monospace',
  MozAppearance:   'textfield',
  borderRadius:    '6px',
  border:          '1px solid',
  boxShadow:       '0px 0px 10px 0px rgba(0,0,0,.10)',
  margin:          '4px',
  paddingLeft:     '8px',
  width:           '36px',
  height:          '42px',
  fontSize:        '32px',
  boxSizing:       'border-box',
  color:           'black',
  backgroundColor: 'white',
  borderColor:     'lightgrey',
};

const darkStyle = {
  fontFamily:      'monospace',
  MozAppearance:   'textfield',
  borderRadius:    '3px',
  border:          '1px solid',
  margin:          '4px',
  paddingLeft:     '7px',
  width:           '15px',
  height:          '26px',
  fontSize:        '14px',
  color:           'lightskyblue',
  backgroundColor: 'black',
  borderColor:     'lightskyblue',
};

// Creation of Stories
stories.addDecorator((story, context) => withInfo('Details')(story)(context));
stories.addDecorator(withKnobs);

// Stories for Input Field
stories
  .add(
    'Default', () =>
      <ReactCodeInput />
  )
  .add(
    'Dark Themed', () =>

        <ReactCodeInput inputStyle={darkStyle} type="number" />

  )
  .add(
    'Force Uppercase', () =>
      <CodeInputField fields={4} type="text" forceUppercase value="test" onChange={action('onChange')} />
  );

// Creation of Props
propVariantStories.addDecorator((story, context) => withInfo('Details')(story)(context));
propVariantStories.addDecorator(withKnobs);

propVariantStories
  .add('type', () =>
    <ReactCodeInput type={select(typeLabel, typeOptions, typeDefaultValue, typeGroupId)} />
  )
  .add('fields', () =>
    <ReactCodeInput fields={number('fields', 6)} />
  )
  .add('inputStyle', () =>
    <ReactCodeInput inputStyle={object('inputStyle', inputStyle)} />
  )
  .add('value', () =>
    <ReactCodeInput value={text('value', '1234')} />
  )
  .add('isValid', () =>
    <ReactCodeInput isValid={boolean('isValid', false)} />
  )
  .add('inputMode', () =>
    <ReactCodeInput inputMode={select('inputMode', inputModeOptions)} />
  )
  .add('onChange', () =>
    <ReactCodeInput onChange={action('onChange')} />
  )
  .add('pattern', () =>
    <form>
      <ReactCodeInput value={text('value', '123')} pattern={text('pattern', '[A-Z]')} />
        <input style={{ ...inputStyle, width: 'auto', backgroundColor: 'lightgray' }} type="submit" value="Submit" />
    </form>
  )
  .add('touch', () =>
    <ReactCodeInput touch={action('touch')} />
  )
  .add('untouch', () =>
    <ReactCodeInput untouch={action('untouch')} />
  )
  .add('disabled', () =>
    <ReactCodeInput disabled={boolean('disabled', true)} />
  );
<|endoftext|>


<commit_before>const EphermalStorage = require('./storage');
const crypto = require('crypto');
const Error = require('@cardstack/plugin-utils/error');
const PendingChange = require('@cardstack/plugin-utils/pending-change');

const pendingChanges = new WeakMap();

module.exports = class Writer {
  constructor({ storageKey }) {
    this.storage = EphermalStorage.create(storageKey);
  }

  async prepareCreate(branch, user, type, document, isSchema) {
    if (branch !== 'master') {
      throw new Error("ephemeral storage only supports branch master");
    }

    let id = document.id;
    if (id == null) {
      id = this._generateId();
    }

    if (this.storage.lookup(type, id)) {
      throw new Error(`id ${id} is already in use`, { status: 409, source: { pointer: '/data/id'}});
    }

    let pending = new PendingChange(null, document, finalizer);
    pendingChanges.set(pending, { type, id, storage: this.storage, isSchema: isSchema });
    return pending;
  }

  async prepareUpdate(branch, user, type, id, document, isSchema) {
    if (!document.meta || !document.meta.version) {
      throw new Error('missing required field "meta.version"', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }

    let before = this.storage.lookup(type, id);
    if (!before) {
      throw new Error(`${type} with id ${id} does not exist`, {
        status: 404,
        source: { pointer: '/data/id' }
      });
    }
    let after = patch(before, document);
    let pending = new PendingChange(before, after, finalizer);
    pendingChanges.set(pending, { type, id, storage: this.storage, isSchema, ifMatch: document.meta.version });
    return pending;
  }

  async prepareDelete(branch, user, version, type, id, isSchema) {
    if (!version) {
      throw new Error('version is required', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }
    let before = this.storage.lookup(type, id);
    if (!before) {
      throw new Error(`${type} with id ${id} does not exist`, {
        status: 404,
        source: { pointer: '/data/id' }
      });
    }
    let pending = new PendingChange(before, null, finalizer);
    pendingChanges.set(pending, { type, id, storage: this.storage, isSchema, ifMatch: version });
    return pending;
  }

  _generateId() {
    return crypto.randomBytes(20).toString('hex');
  }

};

function patch(before, diffDocument) {
  let after = Object.assign({}, before);
  for (let section of ['attributes', 'relationships']) {
    if (diffDocument[section]) {
      after[section] = Object.assign(
        {},
        before[section],
        diffDocument[section]
      );
    }
  }
  return after;
}

async function finalizer(pendingChange) {
  let { storage, isSchema, ifMatch, type, id } = pendingChanges.get(pendingChange);
  return { version: storage.store(type, id, pendingChange.finalDocument, isSchema, ifMatch) };
}
<commit_msg>make writers capable of dependency injection<commit_after>const EphermalStorage = require('./storage');
const crypto = require('crypto');
const Error = require('@cardstack/plugin-utils/error');
const PendingChange = require('@cardstack/plugin-utils/pending-change');

const pendingChanges = new WeakMap();

module.exports = class Writer {
  static create(params) {
    return new this(params);
  }

  constructor({ storageKey }) {
    this.storage = EphermalStorage.create(storageKey);
  }

  async prepareCreate(branch, user, type, document, isSchema) {
    if (branch !== 'master') {
      throw new Error("ephemeral storage only supports branch master");
    }

    let id = document.id;
    if (id == null) {
      id = this._generateId();
    }

    if (this.storage.lookup(type, id)) {
      throw new Error(`id ${id} is already in use`, { status: 409, source: { pointer: '/data/id'}});
    }

    let pending = new PendingChange(null, document, finalizer);
    pendingChanges.set(pending, { type, id, storage: this.storage, isSchema: isSchema });
    return pending;
  }

  async prepareUpdate(branch, user, type, id, document, isSchema) {
    if (!document.meta || !document.meta.version) {
      throw new Error('missing required field "meta.version"', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }

    let before = this.storage.lookup(type, id);
    if (!before) {
      throw new Error(`${type} with id ${id} does not exist`, {
        status: 404,
        source: { pointer: '/data/id' }
      });
    }
    let after = patch(before, document);
    let pending = new PendingChange(before, after, finalizer);
    pendingChanges.set(pending, { type, id, storage: this.storage, isSchema, ifMatch: document.meta.version });
    return pending;
  }

  async prepareDelete(branch, user, version, type, id, isSchema) {
    if (!version) {
      throw new Error('version is required', {
        status: 400,
        source: { pointer: '/data/meta/version' }
      });
    }
    let before = this.storage.lookup(type, id);
    if (!before) {
      throw new Error(`${type} with id ${id} does not exist`, {
        status: 404,
        source: { pointer: '/data/id' }
      });
    }
    let pending = new PendingChange(before, null, finalizer);
    pendingChanges.set(pending, { type, id, storage: this.storage, isSchema, ifMatch: version });
    return pending;
  }

  _generateId() {
    return crypto.randomBytes(20).toString('hex');
  }

};

function patch(before, diffDocument) {
  let after = Object.assign({}, before);
  for (let section of ['attributes', 'relationships']) {
    if (diffDocument[section]) {
      after[section] = Object.assign(
        {},
        before[section],
        diffDocument[section]
      );
    }
  }
  return after;
}

async function finalizer(pendingChange) {
  let { storage, isSchema, ifMatch, type, id } = pendingChanges.get(pendingChange);
  return { version: storage.store(type, id, pendingChange.finalDocument, isSchema, ifMatch) };
}
<|endoftext|>


<commit_before>const child_process = require('child_process');
const {spawn} = child_process;
const path = require('path');

const tar = require('tar-stream');
const tarfs = require('tar-fs');

const log = require('@cardstack/plugin-utils/logger')('hub/build-image');

module.exports = function buildAppImage(packages, appName) {
  let context = buildContext(packages, appName);

  let proc = spawn('docker', [
      'build',
      '--label', 'com.cardstack',
      '-t', 'cardstack-app',
      '-'
  ]);

  context.pipe(proc.stdin);

  return proc;
};

// Creates the context for the docker build.
// It contains the Dockerfile, and a folder for each linked module:
//
// - Dockerfile
// - packages
//   - basic-cardstack
//     - package.json
//     - ...
//   - @cardstack
//     - hub
//       - package.json
//       - ...
//     - codgen
//       - ...

function buildContext(packages, appName) {
  let archive = tar.pack();
  archive.entry({name: 'Dockerfile'}, dockerfile(packages, appName));

  async function archivePackages() {
    for (let package of packages) {
      await archivePackage(archive, package);
    }
    archive.finalize();
  }
  archivePackages();

  return archive;
}

async function archivePackage(archive, package) {
  return new Promise(function(resolve, reject) {
    tarfs.pack(package.path, {
      pack: archive,
      finalize: false,
      ignore: dirs('node_modules', 'tmp', '.git'),
      map(header) {
        header.name = path.normalize(path.join('packages', package.name, header.name));
      },
      finish: resolve
    });
    archive.on('error', reject);
  });
}

function dirs() {
  let directories = [].slice.call(arguments);
  return function(x) {
    return directories.some(dir=>x.endsWith('/'+dir));
  };
}

function dockerfile(packages, appName) {
  let file = [];

  file.push('FROM cardstack/hub:alpha');
  file.push('WORKDIR /hub');

  file.push(`RUN ln -s packages/${appName} app`);

  // copy in only package.json/yarn.lock at first.
  // This means normal code changes won't invalidate the long
  // yarn install step in the docker build cache
  for (let pack of packages) {
    let dir = `packages/${pack.name}/`; // Don't use path.join, in case host is Windows
    let json = dir+'package.json';
    let yarn = dir+'yarn.lock?';        // COPY errors out if a directly specified file is missing, but is ok with a pattern matching 0 files
    let files = JSON.stringify([json, yarn, dir]);
    file.push(`COPY ${files}`);
  }

  // install all dependencies
  for (let pack of packages) {
    let dir = `packages/${pack.name}`;
    file.push(`WORKDIR /hub/${dir}`);
    file.push('RUN yarn install');
    file.push('RUN yarn link');
  }

  // link modules together as they are on the host
  for (let pack of packages) {
    if (pack.links.length) {
      let dir = `packages/${pack.name}`;
      let cmd = JSON.stringify(['yarn', 'link', ...pack.links]);
      file.push(`WORKDIR /hub/${dir}`);
      file.push(`RUN ${cmd}`);
    }
  }

  file.push('WORKDIR /hub');

  for (let pack of packages) {
    let dir = `packages/${pack.name}/`;
    file.push(`COPY ${dir} ${dir}`);
  }

  file.push('WORKDIR /hub/app');

  file.push('ENV ELASTICSEARCH=http://elasticsearch:9200 DEBUG=cardstack/*');

  let flags = ['--allow-dev-dependencies', '--containerized'];
  if (process.env.CARDSTACK_LEAVE_SERVICES) {
    log.info('Will leave docker services running after exit');
    flags.push('--leave-services-running');
  }

  let entry = JSON.stringify([
    'npx',
    '--no-install',
    'cardstack-hub',
    '/hub/app/cardstack/seeds/development'
  ]);
  let cmd = JSON.stringify(flags);

  file.push(`ENTRYPOINT ${entry}`);
  file.push(`CMD ${cmd}`);

  return file.join('\n');
}

<commit_msg>Properly copy in `yarn.lock` from the app<commit_after>const child_process = require('child_process');
const {spawn} = child_process;
const path = require('path');

const tar = require('tar-stream');
const tarfs = require('tar-fs');

const log = require('@cardstack/plugin-utils/logger')('hub/build-image');

module.exports = function buildAppImage(packages, appName) {
  let context = buildContext(packages, appName);

  let proc = spawn('docker', [
      'build',
      '--label', 'com.cardstack',
      '-t', 'cardstack-app',
      '-'
  ]);

  context.pipe(proc.stdin);

  return proc;
};

// Creates the context for the docker build.
// It contains the Dockerfile, and a folder for each linked module:
//
// - Dockerfile
// - packages
//   - basic-cardstack
//     - package.json
//     - ...
//   - @cardstack
//     - hub
//       - package.json
//       - ...
//     - codgen
//       - ...

function buildContext(packages, appName) {
  let archive = tar.pack();
  archive.entry({name: 'Dockerfile'}, dockerfile(packages, appName));

  async function archivePackages() {
    for (let package of packages) {
      await archivePackage(archive, package);
    }
    archive.finalize();
  }
  archivePackages();

  return archive;
}

async function archivePackage(archive, package) {
  return new Promise(function(resolve, reject) {
    tarfs.pack(package.path, {
      pack: archive,
      finalize: false,
      ignore: dirs('node_modules', 'tmp', '.git'),
      map(header) {
        header.name = path.normalize(path.join('packages', package.name, header.name));
      },
      finish: resolve
    });
    archive.on('error', reject);
  });
}

function dirs() {
  let directories = [].slice.call(arguments);
  return function(x) {
    return directories.some(dir=>x.endsWith('/'+dir));
  };
}

function dockerfile(packages, appName) {
  let file = [];

  file.push('FROM cardstack/hub:alpha');
  file.push('WORKDIR /hub');

  file.push(`RUN ln -s packages/${appName} app`);

  // copy in only package.json/yarn.lock at first.
  // This means normal code changes won't invalidate the long
  // yarn install step in the docker build cache
  for (let pack of packages) {
    let dir = `packages/${pack.name}/`; // Don't use path.join, in case host is Windows
    let json = dir+'package.json';
    let yarn = dir+'yarn.loc[k]';        // COPY errors out if a directly specified file is missing, but is ok with a pattern matching 0 files
    let files = JSON.stringify([json, yarn, dir]);
    file.push(`COPY ${files}`);
  }

  // install all dependencies
  for (let pack of packages) {
    let dir = `packages/${pack.name}`;
    file.push(`WORKDIR /hub/${dir}`);
    file.push('RUN yarn install');
    file.push('RUN yarn link');
  }

  // link modules together as they are on the host
  for (let pack of packages) {
    if (pack.links.length) {
      let dir = `packages/${pack.name}`;
      let cmd = JSON.stringify(['yarn', 'link', ...pack.links]);
      file.push(`WORKDIR /hub/${dir}`);
      file.push(`RUN ${cmd}`);
    }
  }

  file.push('WORKDIR /hub');

  for (let pack of packages) {
    let dir = `packages/${pack.name}/`;
    file.push(`COPY ${dir} ${dir}`);
  }

  file.push('WORKDIR /hub/app');

  file.push('ENV ELASTICSEARCH=http://elasticsearch:9200 DEBUG=cardstack/*');

  let flags = ['--allow-dev-dependencies', '--containerized'];
  if (process.env.CARDSTACK_LEAVE_SERVICES) {
    log.info('Will leave docker services running after exit');
    flags.push('--leave-services-running');
  }

  let entry = JSON.stringify([
    'npx',
    '--no-install',
    'cardstack-hub',
    '/hub/app/cardstack/seeds/development'
  ]);
  let cmd = JSON.stringify(flags);

  file.push(`ENTRYPOINT ${entry}`);
  file.push(`CMD ${cmd}`);

  return file.join('\n');
}

<|endoftext|>