<commit_before>import React from "react";

import {
  Appear, BlockQuote, Cite, CodePane, Deck, Fill,
  Heading, Image, Layout, Link, ListItem, List, Markdown, Quote, Slide, Spectacle,
  TableHeaderItem, TableItem, TableRow, Table, Text
} from "../src";

import preloader from "../src/utils/preloader";

import createTheme from "../src/themes/default";

import Rd3BinaryTree from "../assets/rd3binaryTree";

import BadTree from "../assets/badBinaryTree";

import Features from "../assets/Features";

require("normalize.css");
require("../src/themes/default/index.css");

const images = {
  falseComponent: require("../assets/falseComponent.png"),
  noise: require("../assets/reactd3problem.jpg"),
  chart: require("../assets/chart.png"),
  rerouteD3: require("../assets/rerouteD3.gif"),
  badCode1: require("../assets/badCode1.png"),
  badCode2: require("../assets/badCode2.png"),
  badCode3: require("../assets/badCode3.png"),
  badCode4: require("../assets/badCode4.png"),
  badCode5: require("../assets/badCode5.png"),
  ourD3: require("../assets/ourD3.png"),
  ourReact: require("../assets/ourReact.png"),
  d31: require("../assets/d31.png"),
  d32: require("../assets/d32.png"),
  d33: require("../assets/d33.png")
};

preloader(images);

const theme = createTheme({
  primary: "#46AAE8"
});

export default class Presentation extends React.Component {
  render() {
    return (
      <Spectacle theme={theme}>
        <Deck transition={["zoom", "slide"]} transitionDuration={500}>

          <Slide transition={["slide"]} bgColor="primary">
            <Heading size={1} fit caps lineHeight={1} textColor="black">
              React D3 Library
            </Heading>
            <Heading size={1} fit caps textColor="black">
              where you can use d3 in react
            </Heading>
            <Text bold caps textColor="black">it's super sweet</Text>
            <Heading size={1} fit caps>
              A codesmith Presentation
            </Heading>
            <Text bold caps textColor="tertiary">in React</Text>
            <Text textSize="1.5em" margin="20px 0px 0px" bold>by andrew burke, danny lee, and dave loyst!</Text>
          </Slide>

          <Slide transition={["slide"]} bgColor="rgb(34, 34, 34)" notes="You can even put notes on your slide. How awesome is that?">
            <Image src="https://s3.amazonaws.com/media-p.slid.es/uploads/473947/images/2571254/How-to-integrate-React-D3-The-right-way-adeveloperdiary.com-4.jpg" margin="0px auto 40px" height="293px"/>
            <Heading size={2} caps fit textColor="primary" textFont="primary">
              Wait what?
            </Heading>
          </Slide>

          <Slide transition={["zoom"]} bgImage="primary">
            <Heading size={1} caps fit textColor="white">
              The Problem
            </Heading>
            <Image style={
              {
                position: "absolute",
                top: -345,
                left: -365

              }
            }
            src={images.noise.replace("/", "")} height="900px" width="1750px"/>
          </Slide>

          <Slide transition={["slide"]} bgColor="primary">

              <Heading size={1} caps fit textColor="tertiary">
                Example of the problem
              </Heading>

            <BadTree />
          </Slide>

          <Slide transition={["slide"]} bgImage="primary">

          </Slide>

          <Slide transition={["slide"]} bgColor="primary">
            <Rd3BinaryTree />
          </Slide>

          <Slide transition={["slide"]} bgColor="primary">

              <Image
                src={images.d31.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -200,
                    top: -400
                  }
                } height="400px" width="300px"/>
              <Image
                src={images.d32.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -175,
                    top: -25
                  }
                } height="350px" width="400px"/>
               <Image
                src={images.d33.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -150,
                    top: 300
                  }
                } height="150px" width="200px"/>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -400,
                  left: 425
                }
              } fid='1'>
                <Image
                  src={images.badCode1.replace("/", "")} height="400px" width="800px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -275,
                  left: 475
                }
              } fid='2'>
                <Image
                  src={images.badCode2.replace("/", "")} height="300px" width="450px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -200,
                  left: 500
                }
              } fid='3'>
                <Image
                  src={images.badCode3.replace("/", "")} height="620px" width="640px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -150,
                  left: 525
                }
              } fid='4'>
                <Image
                  src={images.badCode4.replace("/", "")} height="200px" width="700px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -50,
                  left: 550
                }
              } fid='5'>
                <Image
                  src={images.badCode5.replace("/", "")} height="250px" width="375px"/>
              </Appear>

            <Appear style={
              {
                border: '1px solid white',
                position: 'absolute',
                left: 500,
                top: -400
              }
            }fid='6'>
              <Image
                src={images.ourD3.replace("/", "")} height="500px" width="600px"/>
            </Appear>
            <Appear style={
              {
                border: '1px solid white',
                position: 'absolute',
                left: 500,
                top: 110
              }
            } fid='6'>
              <Image
                src={images.ourReact.replace("/", "")} height="350px" width="500px"/>
            </Appear>
          </Slide>

          <Slide transition={["slide", "spin"]} bgColor="secondary" textColor="primary">
            <Features />
          </Slide>

          <Slide transition={["slide", "spin"]} bgColor="secondary" textColor="primary">
            <Heading size={1} caps fit textColor="tertiary">
              Challenges
            </Heading>
            <List>
              <ListItem>Recursive Build</ListItem>
              <ListItem>Integrating D3 into React's Lifecycle</ListItem>
              <ListItem>Altering D3 Source Code</ListItem>
            </List>
          </Slide>

          <Slide transition={["fade"]} bgColor="tertiary">
            <Heading size={1} caps fit lineHeight={1.5} textColor="primary">
              Made with love in Codesmith by
            </Heading>
            <Image width="25%" src="http://cdn.shopify.com/s/files/1/0080/8372/products/tattly_bekka_palmer_classic_red_heart_web_design_01_grande.jpg?v=1444763442"/>
            <Heading size={.75} caps fit textColor="black">
              Andrew, Daniel, and Dave
            </Heading>
            <Heading size={.25} textColor="black">
              https://github.com/react-d3-library/react-d3-library
            </Heading>

          </Slide>

        </Deck>
      </Spectacle>
    );
  }
}

//Other Problems
// <Slide transition={["slide"]} bgColor="primary">
//   <OtherProblems/>
//   <Image style={
//     {
//       position: "absolute",
//       top: -300,
//       left: -300
//     }
//   }
//   src={images.chart.replace("/", "")} margin="0px auto 40px" height="750px"/>
//   <Image style={
//     {
//       position: "absolute",
//       top: -300,
//       left: 650

//     }
//   }
//   src={images.falseComponent.replace("/", "")} margin="0px auto 40px" height="620px" width="640px"/>
// </Slide>
<commit_msg>added new example<commit_after>import React from "react";

import {
  Appear, BlockQuote, Cite, CodePane, Deck, Fill,
  Heading, Image, Layout, Link, ListItem, List, Markdown, Quote, Slide, Spectacle,
  TableHeaderItem, TableItem, TableRow, Table, Text
} from "../src";

import preloader from "../src/utils/preloader";

import createTheme from "../src/themes/default";

import Rd3BinaryTree from "../assets/rd3binaryTree";

import BadTree from "../assets/badBinaryTree";

import Features from "../assets/Features";

require("normalize.css");
require("../src/themes/default/index.css");

const images = {
  falseComponent: require("../assets/falseComponent.png"),
  noise: require("../assets/reactd3problem.jpg"),
  chart: require("../assets/chart.png"),
  rerouteD3: require("../assets/rerouteD3.gif"),
  badCode1: require("../assets/badCode1.png"),
  badCode2: require("../assets/badCode2.png"),
  badCode3: require("../assets/badCode3.png"),
  badCode4: require("../assets/badCode4.png"),
  badCode5: require("../assets/badCode5.png"),
  ourD3: require("../assets/ourD3.png"),
  ourReact: require("../assets/ourReact.png"),
  d31: require("../assets/d31.png"),
  d32: require("../assets/d32.png"),
  d33: require("../assets/d33.png")
};

preloader(images);

const theme = createTheme({
  primary: "#46AAE8"
});

export default class Presentation extends React.Component {
  render() {
    return (
      <Spectacle theme={theme}>
        <Deck transition={["zoom", "slide"]} transitionDuration={500}>

          <Slide transition={["slide"]} bgColor="primary">
            <Heading size={1} fit caps lineHeight={1} textColor="black">
              React D3 Library
            </Heading>
            <Heading size={1} fit caps textColor="black">
              where you can use d3 in react
            </Heading>
            <Text bold caps textColor="black">it's super sweet</Text>
            <Heading size={1} fit caps>
              A codesmith Presentation
            </Heading>
            <Text bold caps textColor="tertiary">in React</Text>
            <Text textSize="1.5em" margin="20px 0px 0px" bold>by andrew burke, danny lee, and dave loyst!</Text>
          </Slide>

          <Slide transition={["slide"]} bgColor="rgb(34, 34, 34)" notes="You can even put notes on your slide. How awesome is that?">
            <Image src="https://s3.amazonaws.com/media-p.slid.es/uploads/473947/images/2571254/How-to-integrate-React-D3-The-right-way-adeveloperdiary.com-4.jpg" margin="0px auto 40px" height="293px"/>
            <Heading size={2} caps fit textColor="primary" textFont="primary">
              Wait what?
            </Heading>
          </Slide>

          <Slide transition={["zoom"]} bgImage="primary">
            <Heading size={1} caps fit textColor="white">
              The Problem
            </Heading>
            <Image style={
              {
                position: "absolute",
                top: -345,
                left: -365

              }
            }
            src={images.noise.replace("/", "")} height="900px" width="1750px"/>
          </Slide>

          <Slide transition={["slide"]} bgColor="primary">

              <Heading size={1} caps fit textColor="tertiary">
                Example of the problem
              </Heading>

            <BadTree />
          </Slide>

          <Slide transition={["slide"]} bgImage="primary">

          </Slide>

          <Slide transition={["slide"]} bgColor="primary">
            <Rd3BinaryTree />
          </Slide>

          <Slide transition={["slide"]} bgColor="primary">
              <Image
                src={images.d31.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -200,
                    top: -400
                  }
                } height="400px" width="300px"/>
              <Image
                src={images.d32.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -175,
                    top: -25
                  }
                } height="350px" width="400px"/>
               <Image
                src={images.d33.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -150,
                    top: 300
                  }
                } height="150px" width="200px"/>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -400,
                  left: 425
                }
              } fid='1'>
                <Image
                  src={images.badCode1.replace("/", "")} height="400px" width="800px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -275,
                  left: 475
                }
              } fid='2'>
                <Image
                  src={images.badCode2.replace("/", "")} height="300px" width="450px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -200,
                  left: 500
                }
              } fid='3'>
                <Image
                  src={images.badCode3.replace("/", "")} height="620px" width="640px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -150,
                  left: 525
                }
              } fid='4'>
                <Image
                  src={images.badCode4.replace("/", "")} height="200px" width="700px"/>
              </Appear>
              <Appear style={
                {
                  border: '1px solid white',
                  position: 'absolute',
                  top: -50,
                  left: 550
                }
              } fid='5'>
                <Image
                  src={images.badCode5.replace("/", "")} height="250px" width="375px"/>
              </Appear>
          </Slide>

          <Slide transition={["slide"]} bgImage="primary">
              <Image
                src={images.d31.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -200,
                    top: -400
                  }
                } height="400px" width="300px"/>
              <Image
                src={images.d32.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -175,
                    top: -25
                  }
                } height="350px" width="400px"/>
               <Image
                src={images.d33.replace("/", "")} style={
                  {
                    border: '1px solid white',
                    position: 'absolute',
                    left: -150,
                    top: 300
                  }
                } height="150px" width="200px"/>
            <Appear style={
              {
                border: '1px solid white',
                position: 'absolute',
                left: 500,
                top: -400
              }
            }fid='1'>
              <Image
                src={images.ourD3.replace("/", "")} height="500px" width="600px"/>
            </Appear>
            <Appear style={
              {
                border: '1px solid white',
                position: 'absolute',
                left: 500,
                top: 110
              }
            } fid='2'>
              <Image
                src={images.ourReact.replace("/", "")} height="350px" width="500px"/>
            </Appear>
          </Slide>

          <Slide transition={["slide", "spin"]} bgColor="secondary" textColor="primary">
            <Features />
          </Slide>

          <Slide transition={["slide", "spin"]} bgColor="secondary" textColor="primary">
            <Heading size={1} caps fit textColor="tertiary">
              Challenges
            </Heading>
            <List>
              <ListItem>Recursive Build</ListItem>
              <ListItem>Integrating D3 into React's Lifecycle</ListItem>
              <ListItem>Altering D3 Source Code</ListItem>
            </List>
          </Slide>

          <Slide transition={["fade"]} bgColor="tertiary">
            <Heading size={1} caps fit lineHeight={1.5} textColor="primary">
              Made with love in Codesmith by
            </Heading>
            <Image width="25%" src="http://cdn.shopify.com/s/files/1/0080/8372/products/tattly_bekka_palmer_classic_red_heart_web_design_01_grande.jpg?v=1444763442"/>
            <Heading size={.75} caps fit textColor="black">
              Andrew, Daniel, and Dave
            </Heading>
            <Heading size={.25} textColor="black">
              https://github.com/react-d3-library/react-d3-library
            </Heading>

          </Slide>

        </Deck>
      </Spectacle>
    );
  }
}

//Other Problems
// <Slide transition={["slide"]} bgColor="primary">
//   <OtherProblems/>
//   <Image style={
//     {
//       position: "absolute",
//       top: -300,
//       left: -300
//     }
//   }
//   src={images.chart.replace("/", "")} margin="0px auto 40px" height="750px"/>
//   <Image style={
//     {
//       position: "absolute",
//       top: -300,
//       left: 650

//     }
//   }
//   src={images.falseComponent.replace("/", "")} margin="0px auto 40px" height="620px" width="640px"/>
// </Slide>
<|endoftext|>


<commit_before>/* global Window */
"use strict";
window.welsh = require('./index');
<commit_msg>update comment<commit_after>/* global window */
"use strict";
window.welsh = require('./index');
<|endoftext|>


<commit_before>/*
    ////////////////////////////////////////////////////////////////
    Fat Controller - Javascript Signal Transmitter
    https://github.com/mfinger/fatcontroller
    mark.finger@gmail.com
    ////////////////////////////////////////////////////////////////

    This is free and unencumbered software released into the public domain.

    Anyone is free to copy, modify, publish, use, compile, sell, or
    distribute this software, either in source code form or as a compiled
    binary, for any purpose, commercial or non-commercial, and by any
    means.

    In jurisdictions that recognize copyright laws, the author or authors
    of this software dedicate any and all copyright interest in the
    software to the public domain. We make this dedication for the benefit
    of the public at large and to the detriment of our heirs and
    successors. We intend this dedication to be an overt act of
    relinquishment in perpetuity of all present and future rights to this
    software under copyright law.

    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 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.

    For more information, please refer to <http://unlicense.org/>
*/

fatcontroller = {};

(function() {

    // Debug switch, reports all events to the console.
    // Defaults to true if the console exists
    fatcontroller.debug = window.console ? true : false;

    // Registry of receivers
    fatcontroller.receivers = [];

    fatcontroller.register = function(receiver) {
        // Register a new reciever
        if (fatcontroller.debug)
            console.log('Fat Controller registering new receiver \''+receiver.name+'\':', receiver);
        fatcontroller.receivers.push(receiver);
    };

    fatcontroller.transmit = function (name, data) {
        // Transmit the arguments out in fatcontroller.signal form.

        var signal = new fatcontroller.signal(name, data ? data : undefined);
        if (fatcontroller.debug)
            console.log('Fat Controller transmitting \''+signal.name+'\':', signal);
        // Transmit to all receivers
        var receivers = fatcontroller.receivers;
        for (receiver in receivers) {
            receivers[receiver].receive(signal);
        }
    };

    //----------------Fat Controller's models---------------------

    fatcontroller.signal = function Signal(name, data) {
        // Signals are transmitted to all registered receivers.

        // The value used by receivers to identify signals
        this.name = name;
        // Optional payload with the signal
        if (data)
            this.data = data;
        // Set a timestamp
        this.time = new Date;
    };

    fatcontroller.receiver = function Receiver(name) {
        // Receivers map transmitted signals to bound functions.

        // name is used for identifying receivers in debug logging
        if (name)
            this.name = name;
        else
            this.name = 'Unidentified Receiver';

        // Format: { 'signal name' : [function() {}, ...], ... }
        this.signals = {};

        this.listen = function(signal, binding) {
            // Listen for signals with a name matching :signal and bind
            // :binding as a response.

            if (fatcontroller.debug)
                console.log('Receiver \''+this.name+'\' listening for \''+signal+'\' signal, with binding:', binding);
            var signals = this.signals;
            // If the signal is already being listened for, add the binding,
            // otherwise add the signal with the binding.
            if (signals[signal])
                signals[signal].push(binding);
            else
                signals[signal] = [binding];
        }

        this.receive = function(signal) {
            // Passes :signal to any functions bound to :signal.name.

            if (fatcontroller.debug)
                console.log('Receiver \''+this.name+'\' received \''+signal.name+'\': ', signal);
            var signals = this.signals;
            // If listening for this signal
            if (signals[signal.name]) {
                var bindings = signals[signal.name];
                // Pass the signal into each of the bound functions
                for(var binding in bindings) {
                    if (fatcontroller.debug)
                        console.log('Receiver \''+this.name+'\' passing', signal, 'to', bindings[binding]);
                    bindings[binding](signal);
                }
            }
        }
    };

})();
<commit_msg>Added some clarity in debug logging.<commit_after>/*
    ////////////////////////////////////////////////////////////////
    Fat Controller - Signal Transmission and Receipt
    https://github.com/mfinger/fatcontroller
    mark.finger@gmail.com
    ////////////////////////////////////////////////////////////////

    This is free and unencumbered software released into the public domain.

    Anyone is free to copy, modify, publish, use, compile, sell, or
    distribute this software, either in source code form or as a compiled
    binary, for any purpose, commercial or non-commercial, and by any
    means.

    In jurisdictions that recognize copyright laws, the author or authors
    of this software dedicate any and all copyright interest in the
    software to the public domain. We make this dedication for the benefit
    of the public at large and to the detriment of our heirs and
    successors. We intend this dedication to be an overt act of
    relinquishment in perpetuity of all present and future rights to this
    software under copyright law.

    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 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.

    For more information, please refer to <http://unlicense.org/>
*/

fatcontroller = {};

(function() {

    // Debug switch, reports all events to the console.
    // Defaults to true if the console exists
    fatcontroller.debug = window.console ? true : false;

    // Registry of receivers
    fatcontroller.receivers = [];

    fatcontroller.register = function(receiver) {
        // Register a new reciever
        if (fatcontroller.debug)
            console.log('Fat Controller registering new receiver \''+receiver.name+'\':', receiver);
        fatcontroller.receivers.push(receiver);
    };

    fatcontroller.transmit = function (name, data) {
        // Transmit the arguments out in fatcontroller.signal form.

        var signal = new fatcontroller.signal(name, data ? data : undefined);
        // Transmit to all receivers
        var receivers = fatcontroller.receivers;
        for (var receiver in receivers) {
            receiver = receivers[receiver];
            if (fatcontroller.debug)
                console.log('Fat Controller transmitting \''+signal.name+'\':', signal, 'to receiver \''+receiver.name+'\'', receiver);
            receiver.receive(signal);
        }
    };

    //----------------Fat Controller's models---------------------

    fatcontroller.signal = function Signal(name, data) {
        // Signals are transmitted to all registered receivers.

        // The value used by receivers to identify signals
        this.name = name;
        // Optional payload with the signal
        if (data)
            this.data = data;
        // Set a timestamp
        this.time = new Date;
    };

    fatcontroller.receiver = function Receiver(name) {
        // Receivers map transmitted signals to bound functions.

        // name is used for identifying receivers in debug logging
        if (name)
            this.name = name;
        else
            this.name = 'Unidentified Receiver';

        // Format: { 'signal name' : [function() {}, ...], ... }
        this.signals = {};

        this.listen = function(signal, binding) {
            // Listen for signals with a name matching :signal and bind
            // :binding as a response.

            if (fatcontroller.debug)
                console.log('Receiver \''+this.name+'\' listening for \''+signal+'\' signal, with binding:', binding);
            var signals = this.signals;
            // If the signal is already being listened for, add the binding,
            // otherwise add the signal with the binding.
            if (signals[signal])
                signals[signal].push(binding);
            else
                signals[signal] = [binding];
        }

        this.receive = function(signal) {
            // Passes :signal to any functions bound to :signal.name.

            if (fatcontroller.debug)
                console.log('Receiver \''+this.name+'\' received \''+signal.name+'\': ', signal);
            var signals = this.signals;
            // If listening for this signal
            if (signals[signal.name]) {
                var bindings = signals[signal.name];
                // Pass the signal into each of the bound functions
                for(var binding in bindings) {
                    binding = bindings[binding];
                    if (fatcontroller.debug)
                        console.log('Receiver \''+this.name+'\' passing', signal, 'to', binding);
                    binding(signal);
                }
            }
        }
    };

})();
<|endoftext|>


<commit_before>'use strict';

const async = require('async');
const request = require('request');
const connection = require('./connection');
const config = require('../config');
const logger = require('../logging').getLogger();

/**
 * @description Storage driver that persists:
 *
 * * Assets in a CDN-enabled Cloud Files container.
 * * Metadata envelopes and API keys in MongoDB.
 *
 * This is used in deployed clusters.
 */
function RemoteStorage () {}

exports.RemoteStorage = RemoteStorage;

/**
 * @description Initialize connections to external systems.
 */
RemoteStorage.prototype.setup = function (callback) {
  const mongoIndices = (cb) => {
    mongoCollection('envelopes').createIndex({ contentID: 1 }, { unique: true }, cb);
  };

  const elasticIndices = (cb) => {
    if (!connection.elastic) return cb(null);

    // Attempt to create the latch index. If we can, we're responsible for setting up the initial
    // index and alias. Otherwise, another content service is on it.
    connection.elastic.indices.create({ index: 'latch', ignore: 400 }, (err, response, status) => {
      if (err) return cb(err);

      if (status === 400) {
        // The latch index already existed, so another service is creating the search indices.
        // Note that there's a race condition that occurs when a service that *isn't* creating
        // the search indices attempts to store content between the creation of the latch index
        // and the makeIndexActive() call below in the service that is.
        return cb(null);
      }

      let indexName = `envelopes_${Date.now()}`;
      this.createNewIndex(indexName, (err) => {
        if (err) return cb(err);

        this.makeIndexActive(indexName, cb);
      });
    });
  };

  connection.setup((err) => {
    if (err) return callback(err);

    async.parallel([mongoIndices, elasticIndices], callback);
  });
};

/**
 * @description Do nothing, because writing code to wipe production clean feels like a *bad* idea.
 */
RemoteStorage.prototype.clear = function (callback) {
  callback(null);
};

/**
 * @description Return prefix of the URL that assets are served under.
 */
RemoteStorage.prototype.assetURLPrefix = function () {
  return connection.assetContainer.cdnSslUri + '/';
};

/**
 * @description Return the public CDN URL of a (possibly hypothetical) asset filename.
 */
RemoteStorage.prototype.assetPublicURL = function (filename) {
  return this.assetURLPrefix() + encodeURIComponent(filename);
};

/**
 * @description Upload an asset to the Cloud Files asset container.
 */
RemoteStorage.prototype.storeAsset = function (stream, filename, contentType, callback) {
  const up = connection.cloud.upload({
    container: config.assetContainer(),
    remote: filename,
    contentType: contentType,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  });

  up.on('error', callback);

  up.on('success', () => {
    const publicURL = this.assetPublicURL(filename);
    callback(null, publicURL);
  });

  stream.pipe(up);
};

/**
 * @description Upload many assets to Cloud Files in one request.
 */
RemoteStorage.prototype.bulkStoreAssets = function (stream, callback) {
  connection.cloud.extract({
    container: config.assetContainer(),
    stream,
    format: 'tar.gz',
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Content-Type': ''
    }
  }, callback);
};

/**
 * @description Store this asset in the MongoDB named asset collection, overwriting one with the
 * same name if present.
 */
RemoteStorage.prototype.nameAsset = function (name, publicURL, callback) {
  const filter = { key: name };
  const op = { $set: { key: name, publicURL } };
  const options = { upsert: true };

  mongoCollection('layoutAssets').findOneAndUpdate(filter, op, options, callback);
};

/**
 * @description List all assets that have been persisted in Mongo with nameAsset.
 */
RemoteStorage.prototype.findNamedAssets = function (callback) {
  mongoCollection('layoutAssets').find().toArray(callback);
};

/**
 * @description Yield true if an asset exists or false if it does not.
 */
RemoteStorage.prototype.assetExists = function (filename, callback) {
  const u = this.assetPublicURL(filename);

  request({ url: u, method: 'HEAD' }, (err, response, body) => {
    if (err) return callback(err);

    if (response.statusCode === 404) return callback(null, false);
    if (response.statusCode === 200) return callback(null, true);

    const e = new Error('Unexpected status code from Cloud Files');
    e.statusCode = response.statusCode;
    e.body = body;
    callback(e, false);
  });
};

/**
 * @description Retrieve an asset directly through the content service API. This is useless for
 *  remote storage (because you can and should use the CDN url instead) but implemented for
 *  parity with memory storage.
 */
RemoteStorage.prototype.getAsset = function (filename, callback) {
  const source = connection.cloud.download({
    container: config.assetContainer(),
    remote: filename
  });
  const chunks = [];

  source.on('error', callback);

  source.on('data', (chunk) => chunks.push(chunk));

  source.on('complete', (resp) => {
    const complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      const err = new Error('Cloud Files error');

      err.statusCode = resp.statusCode;
      err.responseBody = complete;

      return callback(err);
    }

    callback(null, { contentType: resp.contentType, body: complete });
  });
};

/**
 * @description Store a newly generated API key in the keys collection.
 */
RemoteStorage.prototype.storeKey = function (key, callback) {
  mongoCollection('apiKeys').insertOne(key, callback);
};

/**
 * @description Forget a previously stored API key by key value.
 */
RemoteStorage.prototype.deleteKey = function (apikey, callback) {
  mongoCollection('apiKeys').deleteOne({
    apikey: apikey
  }, callback);
};

/**
 * @description Return an Array of keys that match the provided API key. Will most frequently
 *   return either zero or one results, but you never know.
 */
RemoteStorage.prototype.findKeys = function (apikey, callback) {
  mongoCollection('apiKeys').find({
    apikey: apikey
  }).toArray(callback);
};

RemoteStorage.prototype._storeEnvelope = function (contentID, doc, callback) {
  const filter = { contentID };
  const options = { upsert: true };

  mongoCollection('envelopes').findOneAndReplace(filter, doc, options, callback);
};

RemoteStorage.prototype._getEnvelope = function (contentID, callback) {
  let results = {};
  let failures = {};
  let responded = false;

  const success = (envelope, source) => {
    if (responded) return;

    results[source] = envelope;

    // Prefer the result from MongoDB.
    if (source === 'mongodb') {
      responded = true;
      return callback(null, envelope);
    }

    // If MongoDB has already failed, prefer Cloud Files.
    if (failures.mongodb) {
      responded = true;

      logger.warn('Returning content from Cloud Files.', {
        contentID,
        deprecation: 'https://github.com/deconst/content-service/issues/93'
      });

      return callback(null, envelope);
    }
  };

  const failure = (err, source) => {
    if (responded) return;

    failures[source] = err;

    // If MongoDB fails but Cloud Files has already succeeded, return the saved result.
    if (source === 'mongodb' && results.cloudfiles) {
      responded = true;

      // TODO Crank this up to warn once we've deployed this and run some builds to catch
      // straggling content.
      logger.debug('Returning content from Cloud Files.', { contentID });

      return callback(null, results.cloudfiles);
    }

    // If Cloud Files fails but MongoDB has already succeeded, return the saved result.
    if (source === 'cloudfiles' && results.mongodb) {
      responded = true;
      return callback(null, results.mongodb);
    }

    // If both have failed, return the Mongo error.
    if (failures.mongodb && failures.cloudfiles) {
      responded = true;
      return callback(failures.mongodb);
    }
  };

  // MongoDB fetch
  mongoCollection('envelopes').find({ contentID }).limit(1).next((err, envelope) => {
    if (err) return failure(err, 'mongodb');

    if (envelope === null) {
      let err = new Error('Envelope not found');
      err.statusCode = 404;
      return failure(err, 'mongodb');
    }

    return success(envelope, 'mongodb');
  });

  // Cloud Files fetch
  const source = connection.cloud.download({
    container: config.contentContainer(),
    remote: encodeURIComponent(contentID)
  });
  const chunks = [];

  source.on('error', (err) => failure(err, 'cloudfiles'));

  source.on('data', (chunk) => chunks.push(chunk));

  source.on('complete', (resp) => {
    const complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      // Content not found in Cloud Files.
      let message = resp.statusCode === 404 ? 'Envelope not found' : 'Cloud files error';

      let err = new Error(message);
      err.statusCode = resp.statusCode;
      err.responseBody = complete;
      return failure(err, 'cloudfiles');
    }

    let envelope = null;
    try {
      envelope = JSON.parse(complete);
    } catch (err) {
      return failure(err, 'cloudfiles');
    }

    success({ contentID, envelope }, 'cloudfiles');
  });
};

RemoteStorage.prototype.deleteEnvelope = function (contentID, callback) {
  mongoCollection('envelopes').deleteOne({ contentID }, callback);
};

RemoteStorage.prototype.bulkDeleteEnvelopes = function (contentIDs, callback) {
  const ops = contentIDs.map((contentID) => {
    return { deleteOne: { filter: { contentID } } };
  });

  const options = { ordered: false };

  mongoCollection('envelopes').bulkWrite(ops, options, callback);
};

/**
 * @description Query for the existence and fingerprint matches of a set of content IDs. The result
 * object will have the structure:
 *
 * { contentID: { present: Boolean, matches: Boolean }}
 */
RemoteStorage.prototype.envelopesExist = function (contentIDMap, callback) {
  const query = { contentID: { $in: Object.keys(contentIDMap) } };
  const projection = { contentID: 1, fingerprint: 1 };

  const results = Object.keys(contentIDMap).reduce((object, contentID) => {
    object[contentID] = { present: false, matches: false };
    return object;
  }, {});

  mongoCollection('envelopes').find(query).project(projection).forEach((envelope) => {
    results[envelope.contentID].present = true;
    results[envelope.contentID].matches = envelope.fingerprint === contentIDMap[envelope.contentID];
  }, (err) => callback(err, results));
};

RemoteStorage.prototype.listEnvelopes = function (prefix, eachCallback, endCallback) {
  let filter = {};

  if (prefix) {
    filter = { contentID: { $regex: `^${prefix}` } };
  }

  const iter = (doc) => eachCallback(null, doc);
  const end = (err) => endCallback(err);

  mongoCollection('envelopes').find(filter).forEach(iter, end);
};

RemoteStorage.prototype.createNewIndex = function (indexName, callback) {
  if (!connection.elastic) return callback(null);

  let envelopeMapping = {
    properties: {
      title: { type: 'string', index: 'analyzed' },
      body: { type: 'string', index: 'analyzed' },
      keywords: { type: 'string', index: 'analyzed' },
      categories: { type: 'string', index: 'not_analyzed' }
    }
  };

  connection.elastic.indices.create({ index: indexName }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.putMapping({
      index: indexName,
      type: 'envelope',
      body: {
        envelope: envelopeMapping
      }
    }, callback);
  });
};

RemoteStorage.prototype._indexEnvelope = function (contentID, envelope, indexName, callback) {
  if (!connection.elastic) return callback(null);

  connection.elastic.index({
    index: indexName,
    type: 'envelope',
    id: contentID,
    body: envelope
  }, callback);
};

RemoteStorage.prototype.makeIndexActive = function (indexName, callback) {
  if (!connection.elastic) return callback(null);

  connection.elastic.indices.updateAliases({
    body: {
      actions: [
        { remove: { index: '*', alias: 'envelopes_current' } },
        { add: { index: indexName, alias: 'envelopes_current' } }
      ]
    }
  }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.get({
      index: 'envelopes*',
      ignoreUnavailable: true,
      feature: '_settings'
    }, (err, response, status) => {
      if (err) return callback(err);

      let indexNames = Object.keys(response).filter((n) => n !== indexName);

      async.each(indexNames, (name, cb) => {
        connection.elastic.indices.delete({ index: name }, cb);
      }, callback);
    });
  });
};

RemoteStorage.prototype.queryEnvelopes = function (query, categories, pageNumber, perPage, callback) {
  if (!connection.elastic) {
    return callback(null, {
      hits: {
        hits: [],
        total: 0
      }
    });
  }

  const q = {};

  if (!categories) {
    q.match = { _all: query };
  } else {
    q.filtered = {
      query: { match: { _all: query } },
      filter: { terms: { categories: categories } }
    };
  }

  connection.elastic.search({
    index: 'envelopes_current',
    type: 'envelope',
    from: (pageNumber - 1) * perPage,
    size: perPage,
    ignoreUnavailable: true,
    body: {
      query: q,
      highlight: {
        fields: {
          body: {}
        }
      }
    }
  }, callback);
};

RemoteStorage.prototype.unindexEnvelope = function (contentID, callback) {
  if (!connection.elastic) return callback(null);

  connection.elastic.delete({
    index: 'envelopes_current',
    type: 'envelope',
    id: contentID
  }, (err) => {
    if (err && err.status === '404') {
      // It's already gone. Disregard.
      return callback(null);
    }

    callback(err);
  });
};

RemoteStorage.prototype.bulkUnindexEnvelopes = function (contentIDs, callback) {
  if (!connection.elastic) return callback(null);

  const actions = contentIDs.map((id) => {
    return { delete: { _index: 'envelopes_current', _type: 'envelope', _id: id } };
  });

  connection.elastic.bulk({ body: actions }, callback);
};

RemoteStorage.prototype.storeSHA = function (sha, callback) {
  mongoCollection('sha').updateOne({
    key: 'controlRepository'
  }, {
    $set: {
      key: 'controlRepository',
      sha: sha
    }
  }, {
    upsert: true
  }, callback);
};

RemoteStorage.prototype.getSHA = function (callback) {
  mongoCollection('sha').findOne({key: 'controlRepository'}, (err, doc) => {
    if (err) return callback(err);

    if (doc === null) {
      return callback(null, null);
    }

    callback(null, doc.sha);
  });
};

function mongoCollection (name) {
  return connection.mongo.collection(config.mongodbPrefix() + name);
}
<commit_msg>:hocho: Cloud Files<commit_after>'use strict';

const async = require('async');
const request = require('request');
const connection = require('./connection');
const config = require('../config');

/**
 * @description Storage driver that persists:
 *
 * * Assets in a CDN-enabled Cloud Files container.
 * * Metadata envelopes and API keys in MongoDB.
 *
 * This is used in deployed clusters.
 */
function RemoteStorage () {}

exports.RemoteStorage = RemoteStorage;

/**
 * @description Initialize connections to external systems.
 */
RemoteStorage.prototype.setup = function (callback) {
  const mongoIndices = (cb) => {
    mongoCollection('envelopes').createIndex({ contentID: 1 }, { unique: true }, cb);
  };

  const elasticIndices = (cb) => {
    if (!connection.elastic) return cb(null);

    // Attempt to create the latch index. If we can, we're responsible for setting up the initial
    // index and alias. Otherwise, another content service is on it.
    connection.elastic.indices.create({ index: 'latch', ignore: 400 }, (err, response, status) => {
      if (err) return cb(err);

      if (status === 400) {
        // The latch index already existed, so another service is creating the search indices.
        // Note that there's a race condition that occurs when a service that *isn't* creating
        // the search indices attempts to store content between the creation of the latch index
        // and the makeIndexActive() call below in the service that is.
        return cb(null);
      }

      let indexName = `envelopes_${Date.now()}`;
      this.createNewIndex(indexName, (err) => {
        if (err) return cb(err);

        this.makeIndexActive(indexName, cb);
      });
    });
  };

  connection.setup((err) => {
    if (err) return callback(err);

    async.parallel([mongoIndices, elasticIndices], callback);
  });
};

/**
 * @description Do nothing, because writing code to wipe production clean feels like a *bad* idea.
 */
RemoteStorage.prototype.clear = function (callback) {
  callback(null);
};

/**
 * @description Return prefix of the URL that assets are served under.
 */
RemoteStorage.prototype.assetURLPrefix = function () {
  return connection.assetContainer.cdnSslUri + '/';
};

/**
 * @description Return the public CDN URL of a (possibly hypothetical) asset filename.
 */
RemoteStorage.prototype.assetPublicURL = function (filename) {
  return this.assetURLPrefix() + encodeURIComponent(filename);
};

/**
 * @description Upload an asset to the Cloud Files asset container.
 */
RemoteStorage.prototype.storeAsset = function (stream, filename, contentType, callback) {
  const up = connection.cloud.upload({
    container: config.assetContainer(),
    remote: filename,
    contentType: contentType,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  });

  up.on('error', callback);

  up.on('success', () => {
    const publicURL = this.assetPublicURL(filename);
    callback(null, publicURL);
  });

  stream.pipe(up);
};

/**
 * @description Upload many assets to Cloud Files in one request.
 */
RemoteStorage.prototype.bulkStoreAssets = function (stream, callback) {
  connection.cloud.extract({
    container: config.assetContainer(),
    stream,
    format: 'tar.gz',
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Content-Type': ''
    }
  }, callback);
};

/**
 * @description Store this asset in the MongoDB named asset collection, overwriting one with the
 * same name if present.
 */
RemoteStorage.prototype.nameAsset = function (name, publicURL, callback) {
  const filter = { key: name };
  const op = { $set: { key: name, publicURL } };
  const options = { upsert: true };

  mongoCollection('layoutAssets').findOneAndUpdate(filter, op, options, callback);
};

/**
 * @description List all assets that have been persisted in Mongo with nameAsset.
 */
RemoteStorage.prototype.findNamedAssets = function (callback) {
  mongoCollection('layoutAssets').find().toArray(callback);
};

/**
 * @description Yield true if an asset exists or false if it does not.
 */
RemoteStorage.prototype.assetExists = function (filename, callback) {
  const u = this.assetPublicURL(filename);

  request({ url: u, method: 'HEAD' }, (err, response, body) => {
    if (err) return callback(err);

    if (response.statusCode === 404) return callback(null, false);
    if (response.statusCode === 200) return callback(null, true);

    const e = new Error('Unexpected status code from Cloud Files');
    e.statusCode = response.statusCode;
    e.body = body;
    callback(e, false);
  });
};

/**
 * @description Retrieve an asset directly through the content service API. This is useless for
 *  remote storage (because you can and should use the CDN url instead) but implemented for
 *  parity with memory storage.
 */
RemoteStorage.prototype.getAsset = function (filename, callback) {
  const source = connection.cloud.download({
    container: config.assetContainer(),
    remote: filename
  });
  const chunks = [];

  source.on('error', callback);

  source.on('data', (chunk) => chunks.push(chunk));

  source.on('complete', (resp) => {
    const complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      const err = new Error('Cloud Files error');

      err.statusCode = resp.statusCode;
      err.responseBody = complete;

      return callback(err);
    }

    callback(null, { contentType: resp.contentType, body: complete });
  });
};

/**
 * @description Store a newly generated API key in the keys collection.
 */
RemoteStorage.prototype.storeKey = function (key, callback) {
  mongoCollection('apiKeys').insertOne(key, callback);
};

/**
 * @description Forget a previously stored API key by key value.
 */
RemoteStorage.prototype.deleteKey = function (apikey, callback) {
  mongoCollection('apiKeys').deleteOne({
    apikey: apikey
  }, callback);
};

/**
 * @description Return an Array of keys that match the provided API key. Will most frequently
 *   return either zero or one results, but you never know.
 */
RemoteStorage.prototype.findKeys = function (apikey, callback) {
  mongoCollection('apiKeys').find({
    apikey: apikey
  }).toArray(callback);
};

RemoteStorage.prototype._storeEnvelope = function (contentID, doc, callback) {
  const filter = { contentID };
  const options = { upsert: true };

  mongoCollection('envelopes').findOneAndReplace(filter, doc, options, callback);
};

RemoteStorage.prototype._getEnvelope = function (contentID, callback) {
  mongoCollection('envelopes').find({ contentID }).limit(1).next((err, envelope) => {
    if (err) {
      err.contentID = contentID;
      err.statusCode = 500;
      return callback(err);
    }

    if (envelope === null) {
      let err = new Error('Envelope not found');
      err.contentID = contentID;
      err.statusCode = 404;
      return callback(err);
    }

    callback(null, envelope);
  });
};

RemoteStorage.prototype.deleteEnvelope = function (contentID, callback) {
  mongoCollection('envelopes').deleteOne({ contentID }, callback);
};

RemoteStorage.prototype.bulkDeleteEnvelopes = function (contentIDs, callback) {
  const ops = contentIDs.map((contentID) => {
    return { deleteOne: { filter: { contentID } } };
  });

  const options = { ordered: false };

  mongoCollection('envelopes').bulkWrite(ops, options, callback);
};

/**
 * @description Query for the existence and fingerprint matches of a set of content IDs. The result
 * object will have the structure:
 *
 * { contentID: { present: Boolean, matches: Boolean }}
 */
RemoteStorage.prototype.envelopesExist = function (contentIDMap, callback) {
  const query = { contentID: { $in: Object.keys(contentIDMap) } };
  const projection = { contentID: 1, fingerprint: 1 };

  const results = Object.keys(contentIDMap).reduce((object, contentID) => {
    object[contentID] = { present: false, matches: false };
    return object;
  }, {});

  mongoCollection('envelopes').find(query).project(projection).forEach((envelope) => {
    results[envelope.contentID].present = true;
    results[envelope.contentID].matches = envelope.fingerprint === contentIDMap[envelope.contentID];
  }, (err) => callback(err, results));
};

RemoteStorage.prototype.listEnvelopes = function (prefix, eachCallback, endCallback) {
  let filter = {};

  if (prefix) {
    filter = { contentID: { $regex: `^${prefix}` } };
  }

  const iter = (doc) => eachCallback(null, doc);
  const end = (err) => endCallback(err);

  mongoCollection('envelopes').find(filter).forEach(iter, end);
};

RemoteStorage.prototype.createNewIndex = function (indexName, callback) {
  if (!connection.elastic) return callback(null);

  let envelopeMapping = {
    properties: {
      title: { type: 'string', index: 'analyzed' },
      body: { type: 'string', index: 'analyzed' },
      keywords: { type: 'string', index: 'analyzed' },
      categories: { type: 'string', index: 'not_analyzed' }
    }
  };

  connection.elastic.indices.create({ index: indexName }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.putMapping({
      index: indexName,
      type: 'envelope',
      body: {
        envelope: envelopeMapping
      }
    }, callback);
  });
};

RemoteStorage.prototype._indexEnvelope = function (contentID, envelope, indexName, callback) {
  if (!connection.elastic) return callback(null);

  connection.elastic.index({
    index: indexName,
    type: 'envelope',
    id: contentID,
    body: envelope
  }, callback);
};

RemoteStorage.prototype.makeIndexActive = function (indexName, callback) {
  if (!connection.elastic) return callback(null);

  connection.elastic.indices.updateAliases({
    body: {
      actions: [
        { remove: { index: '*', alias: 'envelopes_current' } },
        { add: { index: indexName, alias: 'envelopes_current' } }
      ]
    }
  }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.get({
      index: 'envelopes*',
      ignoreUnavailable: true,
      feature: '_settings'
    }, (err, response, status) => {
      if (err) return callback(err);

      let indexNames = Object.keys(response).filter((n) => n !== indexName);

      async.each(indexNames, (name, cb) => {
        connection.elastic.indices.delete({ index: name }, cb);
      }, callback);
    });
  });
};

RemoteStorage.prototype.queryEnvelopes = function (query, categories, pageNumber, perPage, callback) {
  if (!connection.elastic) {
    return callback(null, {
      hits: {
        hits: [],
        total: 0
      }
    });
  }

  const q = {};

  if (!categories) {
    q.match = { _all: query };
  } else {
    q.filtered = {
      query: { match: { _all: query } },
      filter: { terms: { categories: categories } }
    };
  }

  connection.elastic.search({
    index: 'envelopes_current',
    type: 'envelope',
    from: (pageNumber - 1) * perPage,
    size: perPage,
    ignoreUnavailable: true,
    body: {
      query: q,
      highlight: {
        fields: {
          body: {}
        }
      }
    }
  }, callback);
};

RemoteStorage.prototype.unindexEnvelope = function (contentID, callback) {
  if (!connection.elastic) return callback(null);

  connection.elastic.delete({
    index: 'envelopes_current',
    type: 'envelope',
    id: contentID
  }, (err) => {
    if (err && err.status === '404') {
      // It's already gone. Disregard.
      return callback(null);
    }

    callback(err);
  });
};

RemoteStorage.prototype.bulkUnindexEnvelopes = function (contentIDs, callback) {
  if (!connection.elastic) return callback(null);

  const actions = contentIDs.map((id) => {
    return { delete: { _index: 'envelopes_current', _type: 'envelope', _id: id } };
  });

  connection.elastic.bulk({ body: actions }, callback);
};

RemoteStorage.prototype.storeSHA = function (sha, callback) {
  mongoCollection('sha').updateOne({
    key: 'controlRepository'
  }, {
    $set: {
      key: 'controlRepository',
      sha: sha
    }
  }, {
    upsert: true
  }, callback);
};

RemoteStorage.prototype.getSHA = function (callback) {
  mongoCollection('sha').findOne({key: 'controlRepository'}, (err, doc) => {
    if (err) return callback(err);

    if (doc === null) {
      return callback(null, null);
    }

    callback(null, doc.sha);
  });
};

function mongoCollection (name) {
  return connection.mongo.collection(config.mongodbPrefix() + name);
}
<|endoftext|>


<commit_before>'use strict';

const conf = global.conf;
const pref = global.pref;

const execSync = require('child_process').execSync;
const fs = require('fs-extra');
const glob = require('glob');
const gulp = require('gulp');
const path = require('path');
const yaml = require('js-yaml');

const utils = require('../../../core/lib/utils');

const ROOT_DIR = utils.rootDir();

const cvsDirDefaults = {
  assets: pref.backend.synced_dirs.assets_dir,
  scripts: pref.backend.synced_dirs.scripts_dir,
  styles: pref.backend.synced_dirs.styles_dir,
  templates: pref.backend.synced_dirs.templates_dir
};

const cvsExtDefaults = {
  assets: utils.extCheck(pref.backend.synced_dirs.assets_ext),
  scripts: utils.extCheck(pref.backend.synced_dirs.scripts_ext),
  styles: utils.extCheck(pref.backend.synced_dirs.styles_ext),
  templates: utils.extCheck(pref.backend.synced_dirs.templates_ext)
};

const plnDirDefaults = {
  assets: `${ROOT_DIR}/${conf.src}/_assets`,
  scripts: `${ROOT_DIR}/${conf.src}/_scripts/src`,
  styles: `${ROOT_DIR}/${conf.src}/_styles`,
  templates: `${ROOT_DIR}/${conf.src}/_patterns/03-templates`
};

function cvsProcessExec(cmd, file) {
  var stdout = execSync(`cvs ${cmd} ${file}`, {encoding: conf.enc}).trim();
  if (stdout) {
    utils.log(stdout);
  }
}

function cvsProcess(cmd, argv) {
  var types = ['assets', 'scripts', 'styles', 'templates'];

  // ///////////////////////////////////////////////////////////////////////////
  // First, process Pattern Lab files with corresponding YAML files.
  // ///////////////////////////////////////////////////////////////////////////
  for (let i = 0; i < types.length; i++) {
    let files = glob.sync(plnDirDefaults[types[i]] + '/**/*.yml');

    for (let j = 0; j < files.length; j++) {
      let cvsDir = '';
      let cvsExt = '';
      let cvsFile = '';
      let data = {};
      let nestedDirs = '';
      let stats = null;
      let yml = '';

      try {
        stats = fs.statSync(files[j]);
      }
      catch (err) {
        // Fail gracefully.
        continue;
      }

      // Only process valid files.
      if (!stats || !stats.isFile()) {
        continue;
      }

      try {
        yml = fs.readFileSync(files[j], conf.enc);
        data = yaml.safeLoad(yml);
      }
      catch (err) {
        utils.error(err);
        continue;
      }

      if (typeof data[`${types[i]}_dir`] === 'string') {
        cvsDir = data[`${types[i]}_dir`];

        // Don't want to validate local existence if checking out.
        if (cmd !== 'co') {
          cvsDir = utils.backendDirCheck(cvsDir) ? cvsDir : '';
        }

        cvsDir = cvsDir.trim();
      }
      else {
        cvsDir = cvsDirDefaults.types[i];

        // Don't want to validate local existence if checking out.
        if (cmd !== 'co') {
          cvsDir = utils.backendDirCheck(cvsDir) ? cvsDir : '';
        }

        if (cvsDir) {
          nestedDirs = path.dirname(files[j]).replace(plnDirDefaults[types[i]], '');
          cvsDir += nestedDirs;
        }
      }

      if (typeof data[`${types[i]}_ext`] === 'string') {
        cvsExt = utils.extCheck(data[`${types[i]}_ext`]);
      }
      else {
        cvsExt = cvsExtDefaults[types[i]];
      }

      if (cvsDir && cvsExt) {
        if (cmd === 'co') {
          if (argv && argv.d) {
            cvsDir = `${argv.d}/${cvsDir}`;
          }
        }
        else {
          cvsDir = `backend/${cvsDir}`;
        }

        cvsFile = cvsDir + '/' + path.basename(files[j]).replace(/\.yml$/, `.${cvsExt}`);
        cvsProcessExec(cmd, cvsFile);
      }
    }
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Next, process files listed in cvs-files.yml.
  // ///////////////////////////////////////////////////////////////////////////
  let yml = '';
  let data = {};

  try {
    yml = fs.readFileSync(`${ROOT_DIR}/extend/custom/fp-cvs/cvs.yml`, conf.enc);
    data = yaml.safeLoad(yml);
  }
  catch (err) {
    // Fail gracefully.
    return;
  }

  if (!data instanceof Object || !Array.isArray(data.cvs_files)) {
    return;
  }

  for (let i = 0; i < data.cvs_files.length; i++) {
    let cvsFile = '';

    if (cmd === 'co') {
      if (argv && argv.d) {
        cvsFile = `${argv.d}/${data.cvs_files[i]}`;
      }
      else {
        cvsFile = data.cvs_files[i];
      }
    }
    else {
      cvsFile = `backend/${data.cvs_files[i]}`;
    }

    cvsProcessExec(cmd, cvsFile);
  }
}

// Requires a single argument of -c
gulp.task('cvs', function (cb) {
  let argv = require('yargs')(process.argv).argv;

  if (argv.c && typeof argv.c === 'string') {
    // Fepper's fp bash script replaces single-quotes with double-quotes.
    // We need to strip those double-quotes.
    let cmd = argv.c.replace(/^"(.*)"$/, '$1');
    cvsProcess(cmd);
  }
  else {
    utils.error('Error: need a -c argument!');
  }
  cb();
});

// Requires a single argument of -m
gulp.task('cvs:ci', function (cb) {
  let argv = require('yargs')(process.argv).argv;

  if (argv.m && typeof argv.m === 'string') {
    cvsProcess(`ci -m ${argv.m}`);
  }
  else {
    utils.error('Error: need a -m argument!');
  }
  cb();
});

// Takes an optional argument of -d
gulp.task('cvs:co', function (cb) {
  let argv = require('yargs')(process.argv).argv;

  // Must change working dir in order for CVS checkout to work.
  process.chdir('../');
  cvsProcess('co', argv);
  process.chdir(`${__dirname}/../../../`);
  cb();
});

gulp.task('cvs:st', function (cb) {
  cvsProcess('st');
  cb();
});

gulp.task('cvs:up', function (cb) {
  cvsProcess('up');
  cb();
});
<commit_msg>fixing pattern lab file iterator<commit_after>'use strict';

const conf = global.conf;
const pref = global.pref;

const execSync = require('child_process').execSync;
const fs = require('fs-extra');
const glob = require('glob');
const gulp = require('gulp');
const path = require('path');
const yaml = require('js-yaml');

const utils = require('../../../core/lib/utils');

const ROOT_DIR = utils.rootDir();

const cvsDirDefaults = {
  assets: pref.backend.synced_dirs.assets_dir,
  scripts: pref.backend.synced_dirs.scripts_dir,
  styles: pref.backend.synced_dirs.styles_dir,
  templates: pref.backend.synced_dirs.templates_dir
};

const cvsExtDefaults = {
  assets: utils.extCheck(pref.backend.synced_dirs.assets_ext),
  scripts: utils.extCheck(pref.backend.synced_dirs.scripts_ext),
  styles: utils.extCheck(pref.backend.synced_dirs.styles_ext),
  templates: utils.extCheck(pref.backend.synced_dirs.templates_ext)
};

const plnDirDefaults = {
  assets: `${ROOT_DIR}/${conf.src}/_assets`,
  scripts: `${ROOT_DIR}/${conf.src}/_scripts/src`,
  styles: `${ROOT_DIR}/${conf.src}/_styles`,
  templates: `${ROOT_DIR}/${conf.src}/_patterns/03-templates`
};

function cvsProcessExec(cmd, file) {
  var stdout = execSync(`cvs ${cmd} ${file}`, {encoding: conf.enc}).trim();
  if (stdout) {
    utils.log(stdout);
  }
}

function cvsProcess(cmd, argv) {
  var types = ['assets', 'scripts', 'styles', 'templates'];

  // ///////////////////////////////////////////////////////////////////////////
  // First, process Pattern Lab files with corresponding YAML files.
  // ///////////////////////////////////////////////////////////////////////////
  for (let i = 0; i < types.length; i++) {
    let files = glob.sync(plnDirDefaults[types[i]] + '/**/*.yml');

    for (let j = 0; j < files.length; j++) {
      let cvsDir = '';
      let cvsExt = '';
      let cvsFile = '';
      let data = {};
      let nestedDirs = '';
      let stats = null;
      let yml = '';

      try {
        stats = fs.statSync(files[j]);
      }
      catch (err) {
        // Fail gracefully.
        continue;
      }

      // Only process valid files.
      if (!stats || !stats.isFile()) {
        continue;
      }

      try {
        yml = fs.readFileSync(files[j], conf.enc);
        data = yaml.safeLoad(yml);
      }
      catch (err) {
        utils.error(err);
        continue;
      }

      if (typeof data[`${types[i]}_dir`] === 'string') {
        cvsDir = data[`${types[i]}_dir`].trim();

        // Don't want to validate local existence if checking out.
        if (cmd !== 'co') {
          cvsDir = utils.backendDirCheck(ROOT_DIR, cvsDir) ? cvsDir : '';
        }
      }
      else {
        cvsDir = cvsDirDefaults.types[i];

        // Don't want to validate local existence if checking out.
        if (cmd !== 'co') {
          cvsDir = utils.backendDirCheck(ROOT_DIR, cvsDir) ? cvsDir : '';
        }

        if (cvsDir) {
          nestedDirs = path.dirname(files[j]).replace(plnDirDefaults[types[i]], '');
          cvsDir += nestedDirs;
        }
      }

      if (typeof data[`${types[i]}_ext`] === 'string') {
        cvsExt = utils.extCheck(data[`${types[i]}_ext`]);
      }
      else {
        cvsExt = cvsExtDefaults[types[i]];
      }

      if (cvsDir && cvsExt) {
        if (cmd === 'co') {
          if (argv && argv.d) {
            cvsDir = `${argv.d}/${cvsDir}`;
          }
        }
        else {
          cvsDir = `backend/${cvsDir}`;
        }

        cvsFile = cvsDir + '/' + path.basename(files[j]).replace(/\.yml$/, `.${cvsExt}`);
        cvsProcessExec(cmd, cvsFile);
      }
    }
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Next, process files listed in cvs-files.yml.
  // ///////////////////////////////////////////////////////////////////////////
  let yml = '';
  let data = {};

  try {
    yml = fs.readFileSync(`${ROOT_DIR}/extend/custom/fp-cvs/cvs.yml`, conf.enc);
    data = yaml.safeLoad(yml);
  }
  catch (err) {
    // Fail gracefully.
    return;
  }

  if (!data instanceof Object || !Array.isArray(data.cvs_files)) {
    return;
  }

  for (let i = 0; i < data.cvs_files.length; i++) {
    let cvsFile = '';

    if (cmd === 'co') {
      if (argv && argv.d) {
        cvsFile = `${argv.d}/${data.cvs_files[i]}`;
      }
      else {
        cvsFile = data.cvs_files[i];
      }
    }
    else {
      cvsFile = `backend/${data.cvs_files[i]}`;
    }

    cvsProcessExec(cmd, cvsFile);
  }
}

// Requires a single argument of -c
gulp.task('cvs', function (cb) {
  let argv = require('yargs')(process.argv).argv;

  if (argv.c && typeof argv.c === 'string') {
    // Fepper's fp bash script replaces single-quotes with double-quotes.
    // We need to strip those double-quotes.
    let cmd = argv.c.replace(/^"(.*)"$/, '$1');
    cvsProcess(cmd);
  }
  else {
    utils.error('Error: need a -c argument!');
  }
  cb();
});

// Requires a single argument of -m
gulp.task('cvs:ci', function (cb) {
  let argv = require('yargs')(process.argv).argv;

  if (argv.m && typeof argv.m === 'string') {
    cvsProcess(`ci -m ${argv.m}`);
  }
  else {
    utils.error('Error: need a -m argument!');
  }
  cb();
});

// Takes an optional argument of -d
gulp.task('cvs:co', function (cb) {
  let argv = require('yargs')(process.argv).argv;

  // Must change working dir in order for CVS checkout to work.
  process.chdir('../');
  cvsProcess('co', argv);
  process.chdir(`${__dirname}/../../../`);
  cb();
});

gulp.task('cvs:st', function (cb) {
  cvsProcess('st');
  cb();
});

gulp.task('cvs:up', function (cb) {
  cvsProcess('up');
  cb();
});
<|endoftext|>


<commit_before>// Import React
import React from "react";

// Import Spectacle Core tags
import {
  Deck,
  Heading,
  ListItem,
  List,
  Slide,
  Text,
  Appear,
  Image,
  Link
} from "spectacle";

import CodeSlide from "spectacle-code-slide";

// Import image preloader util
import preloader from "spectacle/lib/utils/preloader";

// Import theme
// import createTheme from "spectacle/lib/themes/default";

import { theme } from "spectacle-theme-solarized-dark";
// Require CSS
require("normalize.css");
require("spectacle/lib/themes/default/index.css");

const images = {
  npmDownloads: require("../assets/TypescriptNPM-Downloads.png")
};

preloader(images);

/*const theme = createTheme({
  primary: "white",
  secondary: "#1F2022",
  tertiary: "#03A9FC",
  quartenary: "#CECECE"
}, {
  primary: "Montserrat",
  secondary: "Helvetica"
});*/

export default class Presentation extends React.Component {
  render() {
    return (
      <Deck transition={[]} transitionDuration={500} theme={theme}>
        <Slide transition={["zoom"]} bgColor="primary">
          <Heading size={1} fit caps lineHeight={1} textColor="secondary">
            Typescript
          </Heading>
          <Text margin="10px 0 0" textColor="tertiary" size={1} fit bold>
            Javascript that scales
          </Text>
        </Slide>
        <Slide transition={["fade"]} bgColor="tertiary">
          <Heading size={6} textColor="primary" caps>Why Typescript</Heading>
          <List textColor="primary">
            <Appear><ListItem>Superset of Javascript</ListItem></Appear>
            <Appear><ListItem>Can Mix JS and TS</ListItem></Appear>
            <Appear><ListItem>Zero runtime cost (ts disappears at runtime)</ListItem></Appear>
            <Appear><ListItem>Great intellisense/autocomplete support</ListItem></Appear>
            <Appear><ListItem>Type inference</ListItem></Appear>
            <Appear><ListItem>Mature ecosystem</ListItem></Appear>
          </List>
        </Slide>
        <Slide transition={["fade"]} bgColor="primary" textColor="tertiary">
          <Heading size={6} textColor="secondary" caps>Typescript - Usage(NPM downloads)</Heading>
          <Image src={images.npmDownloads} />
          <Link href="https://npm-stat.com/charts.html?package=babel&package=typescript&package=flow-bin&from=2016-10-01&to=2017-03-31" target="blank" >Source</Link>
        </Slide>
        <Slide transition={["fade"]} bgColor="tertiary">
            <Heading size={6} textColor="primary" caps>How does this help me?</Heading>
            <Text textAlign="Center" lineHeight={5} textSize={52} bold caps>Demo</Text>
        </Slide>
        <CodeSlide
          transition={[]}
          lang="bash"
          code={require("raw-loader!../code/createTypescript.example")}
          ranges={[
              { loc: [0, 8], title: "Easy Way - Greenfield" },
              { loc: [2, 3], title: "Install CRA" },
              { loc: [5, 7], title: "Start the project" }
          ]}
        />
        <Slide transition={["fade"]} bgColor="tertiary">
          <Heading size={6} textColor="primary" caps>Existing project: Init</Heading>
          <List textColor="primary">
            <Appear><ListItem>Use CRA/Typescript as template</ListItem></Appear>
            <Appear><ListItem>Yarn add --dev ts-loader, tslint, tslint-loader, tslint-react</ListItem></Appear>
          </List>
        </Slide>
        <Slide transition={["fade"]} bgColor="primary" textColor="tertiary">
          <Heading size={6} textColor="secondary" caps>Existing Project: Webpack</Heading>
          <List>
            <Appear><ListItem>Add entry for ts and tsx files passing to ts-loader</ListItem></Appear>
            <Appear><ListItem>Add entry for ts-lint</ListItem></Appear>
            <Appear><ListItem>If using jest add .ts entry re-use transformer from CRA</ListItem></Appear>
          </List>
        </Slide>
        <Slide transition={["fade"]} bgColor="tertiary">
            <Heading size={6} textColor="primary" caps>Existing Project: Webpack</Heading>
            <Text textAlign="Center" lineHeight={5} textSize={52} bold caps>Demo</Text>
        </Slide>
        <Slide transition={["fade"]} bgColor="primary" textColor="tertiary">
          <Heading size={5} textColor="secondary" caps>Existing Project: Hardest part</Heading>
          <Appear><Text textColor="tertiary" lineHeight={5} >Rename js files to ts/tsx</Text></Appear>
        </Slide>
      </Deck>
    );
  }
}
<commit_msg>remove commas between ts commands<commit_after>// Import React
import React from "react";

// Import Spectacle Core tags
import {
  Deck,
  Heading,
  ListItem,
  List,
  Slide,
  Text,
  Appear,
  Image,
  Link
} from "spectacle";

import CodeSlide from "spectacle-code-slide";

// Import image preloader util
import preloader from "spectacle/lib/utils/preloader";

// Import theme
// import createTheme from "spectacle/lib/themes/default";

import { theme } from "spectacle-theme-solarized-dark";
// Require CSS
require("normalize.css");
require("spectacle/lib/themes/default/index.css");

const images = {
  npmDownloads: require("../assets/TypescriptNPM-Downloads.png")
};

preloader(images);

/*const theme = createTheme({
  primary: "white",
  secondary: "#1F2022",
  tertiary: "#03A9FC",
  quartenary: "#CECECE"
}, {
  primary: "Montserrat",
  secondary: "Helvetica"
});*/

export default class Presentation extends React.Component {
  render() {
    return (
      <Deck transition={[]} transitionDuration={500} theme={theme}>
        <Slide transition={["zoom"]} bgColor="primary">
          <Heading size={1} fit caps lineHeight={1} textColor="secondary">
            Typescript
          </Heading>
          <Text margin="10px 0 0" textColor="tertiary" size={1} fit bold>
            Javascript that scales
          </Text>
        </Slide>
        <Slide transition={["fade"]} bgColor="tertiary">
          <Heading size={6} textColor="primary" caps>Why Typescript</Heading>
          <List textColor="primary">
            <Appear><ListItem>Superset of Javascript</ListItem></Appear>
            <Appear><ListItem>Can Mix JS and TS</ListItem></Appear>
            <Appear><ListItem>Zero runtime cost (ts disappears at runtime)</ListItem></Appear>
            <Appear><ListItem>Great intellisense/autocomplete support</ListItem></Appear>
            <Appear><ListItem>Type inference</ListItem></Appear>
            <Appear><ListItem>Mature ecosystem</ListItem></Appear>
          </List>
        </Slide>
        <Slide transition={["fade"]} bgColor="primary" textColor="tertiary">
          <Heading size={6} textColor="secondary" caps>Typescript - Usage(NPM downloads)</Heading>
          <Image src={images.npmDownloads} />
          <Link href="https://npm-stat.com/charts.html?package=babel&package=typescript&package=flow-bin&from=2016-10-01&to=2017-03-31" target="blank" >Source</Link>
        </Slide>
        <Slide transition={["fade"]} bgColor="tertiary">
            <Heading size={6} textColor="primary" caps>How does this help me?</Heading>
            <Text textAlign="Center" lineHeight={5} textSize={52} bold caps>Demo</Text>
        </Slide>
        <CodeSlide
          transition={[]}
          lang="bash"
          code={require("raw-loader!../code/createTypescript.example")}
          ranges={[
              { loc: [0, 8], title: "Easy Way - Greenfield" },
              { loc: [2, 3], title: "Install CRA" },
              { loc: [5, 7], title: "Start the project" }
          ]}
        />
        <Slide transition={["fade"]} bgColor="tertiary">
          <Heading size={6} textColor="primary" caps>Existing project: Init</Heading>
          <List textColor="primary">
            <Appear><ListItem>Use CRA/Typescript as template</ListItem></Appear>
            <Appear><ListItem>Yarn add --dev ts-loader tslint tslint-loader tslint-react</ListItem></Appear>
          </List>
        </Slide>
        <Slide transition={["fade"]} bgColor="primary" textColor="tertiary">
          <Heading size={6} textColor="secondary" caps>Existing Project: Webpack</Heading>
          <List>
            <Appear><ListItem>Add entry for ts and tsx files passing to ts-loader</ListItem></Appear>
            <Appear><ListItem>Add entry for ts-lint</ListItem></Appear>
            <Appear><ListItem>If using jest add .ts entry re-use transformer from CRA</ListItem></Appear>
          </List>
        </Slide>
        <Slide transition={["fade"]} bgColor="tertiary">
            <Heading size={6} textColor="primary" caps>Existing Project: Webpack</Heading>
            <Text textAlign="Center" lineHeight={5} textSize={52} bold caps>Demo</Text>
        </Slide>
        <Slide transition={["fade"]} bgColor="primary" textColor="tertiary">
          <Heading size={5} textColor="secondary" caps>Existing Project: Hardest part</Heading>
          <Appear><Text textColor="tertiary" lineHeight={5} >Rename js files to ts/tsx</Text></Appear>
        </Slide>
      </Deck>
    );
  }
}
<|endoftext|>


<commit_before>Healthygeeks = new Mongo.Collection('healthygeeks');
Foods = new Mongo.Collection('foods');

Foods.insert(
  {
      name: '瘦牛肉', unit: 100, calory: 183, totalFat: 8.46, saturatedFat: 3.422, cholesterol: 83, category: 'naturalfood', resource: 'http://ndb.nal.usda.gov/ndb/foods/show/4005?fg=Beef+Products&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=beef'
  }
);
Foods.insert(
  {
    name: '鸡蛋', unit: 44, calory: 63, totalFat: 4.18, saturatedFat: 1.375, cholesterol: 164, category: 'naturalfood', resource: 'http://ndb.nal.usda.gov/ndb/foods/show/112?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=egg'
  }
);
Foods.insert(
  {
    name: '韭菜包', brand: "味全", unit: 150, calory: 400, totalFat: 24, saturatedFat: 6, cholesterol: 0, category: 'conveniencefood'
  }
);
Foods.insert(
  {
    name: '湖南腊肉',brand: "新东阳食品",unit: 84,calory: 340,totalFat: 25,saturatedFat: 8,cholesterol: 95,category: 'conveniencefood'
  }
);
Foods.insert(
  {
    name: '脱脂牛奶',unit: 100,calory: 35,totalFat: 0.18,saturatedFat: 0.117,cholesterol: 2,category: 'naturalfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/8457?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=milk+fat+free'
  }
);
Foods.insert(
  {
    name: '花生',unit: 100,calory: 567,totalFat: 49.24,saturatedFat: 6.279,cholesterol: 0,category: 'naturalfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/4800?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=peanut'
  }
);
Foods.insert(
  {
    name: '12英寸的奶酪比萨',brand: "必胜客",unit: 100,calory: 280,totalFat: 12.56,saturatedFat: 5.184,cholesterol: 21,category: 'fastfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/6775?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=pizza'
  }
);
Foods.insert(
  {
    name: '金枪鱼三明治-含有生菜和西红柿的白面包',brand: "赛百味",unit: '12inch',calory: 1048,totalFat: 57.07,saturatedFat: 10.39,cholesterol: 133,category: 'fastfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/6729?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=subway'
  }
);

if (Meteor.isClient) {
  Template.addBIMForm.events({
    "submit form": function (event) {
      // Prevent default browser form submit
      event.preventDefault();

      // Get value from form element
      var yourHeight = event.target.yourHeight.value;
      var yourWeight = event.target.yourWeight.value;

      var yourBMIFloatNum = yourWeight / ((yourHeight / 100) * (yourHeight / 100));// Calculate your BMI(kg/m2)
      var yourBMI = yourBMIFloatNum.toFixed(1);     // Calculate your BMI round(1)

      document.getElementById("yourHeightInfo").textContent = "你的身高是 " + yourHeight +".";
      document.getElementById("yourWeightInfo").textContent = "你的体重是 " + yourWeight +".";
      document.getElementById("yourBMIInfo").textContent = "你的BMI是 " + yourBMI +".";

      // output your BMI description according to Hong Kong's BMI recommendation
	if (yourBMI < 18.5) {
    	document.getElementById("yourBMIAssess").textContent = "你的体重过轻";
	}else if (yourBMI >= 18.5 && yourBMI < 23.0) {
	    document.getElementById("yourBMIAssess").textContent = "你的体重正常";
	}else if (yourBMI >= 23.0 && yourBMI < 25.0) {
	    document.getElementById("yourBMIAssess").textContent = "你的体重超重，有健康危险";
	}else if (yourBMI >= 25.0 && yourBMI < 30.0) {
	    document.getElementById("yourBMIAssess").textContent = "你的体重超重，属于中度肥胖";
	}else{
	    document.getElementById("yourBMIAssess").textContent = "你的体重超重，属于严重肥胖";
	};

      // Insert a task into the collection
      Healthygeeks.insert({
        Height: yourHeight,
        Weight: yourWeight,
        BMI: yourBMI
      });

      // Clear form
      event.target.yourHeight.value = "";
      event.target.yourWeight.value = "";
    }
  });

  Template.addFoodForm.events({
    "submit form": function (event) {
      // Prevent default browser form submit
      event.preventDefault();

      // Get value from form element
      var name = event.target.name.value;
      var brand = event.target.brand.value;
      var unit = event.target.unit.value;
      var calory = event.target.calory.value;
      var totalFat = event.target.totalFat.value;
      var saturatedFat = event.target.saturatedFat.value;
      var cholesterol = event.target.cholesterol.value;
      var category = event.target.category.value;
      var resource = event.target.resource.value;

      var caloryPercentDV = (calory * 100 / 2000).toFixed(0);             // get calories's %DV
      var totalFatPercentDV = (totalFat * 100  / 65).toFixed(0);           // get total fat's %DV
      var saturatedFatPercentDV = (saturatedFat * 100  / 20).toFixed(0);   // get saturated fat's %DV
      var cholesterolPercentDV = (cholesterol * 100  / 300).toFixed(0);    // get cholesterol's %DV

      // get the quotient of totalFat's %DV / calories's %DV
      var totalFatPercentDVQuotient = totalFatPercentDV / caloryPercentDV
      // get the quotient of saturatedFat's %DV / calories's %DV
      var saturatedFatPercentDVQuotient = saturatedFatPercentDV / caloryPercentDV
      // get the quotient of cholesterol's %DV / calories's %DV
      var cholesterolPercentDVQuotient = cholesterolPercentDV / caloryPercentDV

      Foods.insert({
        name: name,
        brand: brand,
        unit: unit,
        calory: calory,
        totalFat: totalFat,
        saturatedFat: saturatedFat,
        cholesterol: cholesterol,
        category: category,
        resource: resource,

        caloryPercentDV: caloryPercentDV,
        totalFatPercentDV: totalFatPercentDV,
        saturatedFatPercentDV: saturatedFatPercentDV,
        cholesterolPercentDV: cholesterolPercentDV,

        totalFatPercentDVQuotient: totalFatPercentDVQuotient,
        saturatedFatPercentDVQuotient: saturatedFatPercentDVQuotient,
        cholesterolPercentDVQuotient: cholesterolPercentDVQuotient
      });

      // Clear form
      event.target.name.value = "";
      event.target.brand.value = "";
      event.target.unit.value = "";
      event.target.calory.value = "";
      event.target.totalFat.value = "";
      event.target.saturatedFat.value = "";
      event.target.cholesterol.value = "";
      event.target.category.value = "";
      event.target.resource.value = "";
    }
  });

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}
<commit_msg>add Quotients toFix method<commit_after>Healthygeeks = new Mongo.Collection('healthygeeks');
Foods = new Mongo.Collection('foods');

Foods.insert(
  {
      name: '瘦牛肉', unit: 100, calory: 183, totalFat: 8.46, saturatedFat: 3.422, cholesterol: 83, category: 'naturalfood', resource: 'http://ndb.nal.usda.gov/ndb/foods/show/4005?fg=Beef+Products&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=beef'
  }
);
Foods.insert(
  {
    name: '鸡蛋', unit: 44, calory: 63, totalFat: 4.18, saturatedFat: 1.375, cholesterol: 164, category: 'naturalfood', resource: 'http://ndb.nal.usda.gov/ndb/foods/show/112?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=egg'
  }
);
Foods.insert(
  {
    name: '韭菜包', brand: "味全", unit: 150, calory: 400, totalFat: 24, saturatedFat: 6, cholesterol: 0, category: 'conveniencefood'
  }
);
Foods.insert(
  {
    name: '湖南腊肉',brand: "新东阳食品",unit: 84,calory: 340,totalFat: 25,saturatedFat: 8,cholesterol: 95,category: 'conveniencefood'
  }
);
Foods.insert(
  {
    name: '脱脂牛奶',unit: 100,calory: 35,totalFat: 0.18,saturatedFat: 0.117,cholesterol: 2,category: 'naturalfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/8457?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=milk+fat+free'
  }
);
Foods.insert(
  {
    name: '花生',unit: 100,calory: 567,totalFat: 49.24,saturatedFat: 6.279,cholesterol: 0,category: 'naturalfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/4800?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=peanut'
  }
);
Foods.insert(
  {
    name: '12英寸的奶酪比萨',brand: "必胜客",unit: 100,calory: 280,totalFat: 12.56,saturatedFat: 5.184,cholesterol: 21,category: 'fastfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/6775?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=pizza'
  }
);
Foods.insert(
  {
    name: '金枪鱼三明治-含有生菜和西红柿的白面包',brand: "赛百味",unit: '12inch',calory: 1048,totalFat: 57.07,saturatedFat: 10.39,cholesterol: 133,category: 'fastfood',resource: 'http://ndb.nal.usda.gov/ndb/foods/show/6729?fg=&man=&lfacet=&format=&count=&max=25&offset=&sort=&qlookup=subway'
  }
);

if (Meteor.isClient) {
  Template.addBIMForm.events({
    "submit form": function (event) {
      // Prevent default browser form submit
      event.preventDefault();

      // Get value from form element
      var yourHeight = event.target.yourHeight.value;
      var yourWeight = event.target.yourWeight.value;

      var yourBMIFloatNum = yourWeight / ((yourHeight / 100) * (yourHeight / 100));// Calculate your BMI(kg/m2)
      var yourBMI = yourBMIFloatNum.toFixed(1);     // Calculate your BMI round(1)

      document.getElementById("yourHeightInfo").textContent = "你的身高是 " + yourHeight +".";
      document.getElementById("yourWeightInfo").textContent = "你的体重是 " + yourWeight +".";
      document.getElementById("yourBMIInfo").textContent = "你的BMI是 " + yourBMI +".";

      // output your BMI description according to Hong Kong's BMI recommendation
	if (yourBMI < 18.5) {
    	document.getElementById("yourBMIAssess").textContent = "你的体重过轻";
	}else if (yourBMI >= 18.5 && yourBMI < 23.0) {
	    document.getElementById("yourBMIAssess").textContent = "你的体重正常";
	}else if (yourBMI >= 23.0 && yourBMI < 25.0) {
	    document.getElementById("yourBMIAssess").textContent = "你的体重超重，有健康危险";
	}else if (yourBMI >= 25.0 && yourBMI < 30.0) {
	    document.getElementById("yourBMIAssess").textContent = "你的体重超重，属于中度肥胖";
	}else{
	    document.getElementById("yourBMIAssess").textContent = "你的体重超重，属于严重肥胖";
	};

      // Insert a task into the collection
      Healthygeeks.insert({
        Height: yourHeight,
        Weight: yourWeight,
        BMI: yourBMI
      });

      // Clear form
      event.target.yourHeight.value = "";
      event.target.yourWeight.value = "";
    }
  });

  Template.addFoodForm.events({
    "submit form": function (event) {
      // Prevent default browser form submit
      event.preventDefault();

      // Get value from form element
      var name = event.target.name.value;
      var brand = event.target.brand.value;
      var unit = event.target.unit.value;
      var calory = event.target.calory.value;
      var totalFat = event.target.totalFat.value;
      var saturatedFat = event.target.saturatedFat.value;
      var cholesterol = event.target.cholesterol.value;
      var category = event.target.category.value;
      var resource = event.target.resource.value;

      var caloryPercentDV = (calory * 100 / 2000).toFixed(0);             // get calories's %DV
      var totalFatPercentDV = (totalFat * 100  / 65).toFixed(0);           // get total fat's %DV
      var saturatedFatPercentDV = (saturatedFat * 100  / 20).toFixed(0);   // get saturated fat's %DV
      var cholesterolPercentDV = (cholesterol * 100  / 300).toFixed(0);    // get cholesterol's %DV

      // get the quotient of totalFat's %DV / calories's %DV
      var totalFatPercentDVQuotient = (totalFatPercentDV / caloryPercentDV).toFixed(1);
      // get the quotient of saturatedFat's %DV / calories's %DV
      var saturatedFatPercentDVQuotient = (saturatedFatPercentDV / caloryPercentDV).toFixed(1);
      // get the quotient of cholesterol's %DV / calories's %DV
      var cholesterolPercentDVQuotient = (cholesterolPercentDV / caloryPercentDV).toFixed(1);

      Foods.insert({
        name: name,
        brand: brand,
        unit: unit,
        calory: calory,
        totalFat: totalFat,
        saturatedFat: saturatedFat,
        cholesterol: cholesterol,
        category: category,
        resource: resource,

        caloryPercentDV: caloryPercentDV,
        totalFatPercentDV: totalFatPercentDV,
        saturatedFatPercentDV: saturatedFatPercentDV,
        cholesterolPercentDV: cholesterolPercentDV,

        totalFatPercentDVQuotient: totalFatPercentDVQuotient,
        saturatedFatPercentDVQuotient: saturatedFatPercentDVQuotient,
        cholesterolPercentDVQuotient: cholesterolPercentDVQuotient
      });

      // Clear form
      event.target.name.value = "";
      event.target.brand.value = "";
      event.target.unit.value = "";
      event.target.calory.value = "";
      event.target.totalFat.value = "";
      event.target.saturatedFat.value = "";
      event.target.cholesterol.value = "";
      event.target.category.value = "";
      event.target.resource.value = "";
    }
  });

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}
<|endoftext|>