<commit_before>"use strict";

var Promise   = require('bluebird')
  , url       = require('url')
  , path      = require('path')
  , fs        = require('fs-extra')
  , _         = require('underscore')
  , Git       = require('nodegit')
  , Datastore = require('nedb')
  ;

var db = new Datastore({ filename: '.db/repository.db', autoload: true });

class Repository {
  constructor(id) {
    this.id = id;

    this.remote = id;

    var url_parsed = url.parse(this.id);

    this.path = path.join('.db', url_parsed.hostname, url_parsed.pathname);

    var path_parts = url_parsed.pathname.split(path.sep);

    this.username = path_parts[1];
    this.name = path.basename(path_parts[2], '.git');
  }

  cloneAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  fetchAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/FETCH_HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  updatedAt() {
    return this.fetchAt()
            .catch(() => this.cloneAt() )
            .catch(() => undefined )
            ;
  }

  getMetadata() {
    return Promise.promisify(db.findOne, { context : db })({ remote : this.remote });
  }

  updateMetadata(patch) {
    var query   = { remote : this.remote }
      , data    = _.defaults({ remote : this.remote }, patch)
      , opts    = { upsert: true }
      ;

    return Promise.promisify(db.update, { context : db })(query, data, opts);
  }

  acl(options) {
    return this
            .getMetadata()
            .then((m) => {
              if (!_.isObject(m)) {
                throw new Error('not found');
              }

              var ret = Promise.resolve(m);

              if (m.private && options.token !== m.token) {
                // it is possible that the token was update / try a fetch and accept new token on success
                ret.tap(() => {
                  this.pull(options);
                });
              }

              return ret;
            });
  }

  onLocal() {
    return Promise
              .promisify(fs.access)(path.join(this.path, '.git/HEAD'), fs.R_OK | fs.W_OK)
              .then(() => {
                return true;
              })
              .catch(() => {
                return false;
              });
  }

  clone(options) {
    if (!this.c) {
      this.c = Promise
                .promisify(fs.ensureDir)(path.dirname(this.path))
                .then(() => {
                  var opt = { fetchOpts : { callbacks : { certificateCheck  : () => 1 } } };

                  return Promise
                          .resolve(Git.Clone(this.remote, this.path, opt))
                          .tap(() => {
                            return this.updateMetadata({ private : false });
                          })
                          .catch({ message : 'authentication required but no callback set' }, (err) => {
                            if (options.token) {
                              opt.fetchOpts.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');

                              return Promise
                                      .resolve(Git.Clone(this.remote, this.path, opt))
                                      .tap(() => {
                                        return this.updateMetadata({ private : true, token : options.token });
                                      });
                            } else {
                              throw err;
                            }
                          });
                })
                .catch((err) => {
                  delete Repository.store[this.id];
                  // mask error
                  console.error(this.remote, err);
                  throw new Error('not found');
                })
                .finally(() => {
                  delete this.c;
                });
    }

    return this.c;
  }

  // todo [akamel] problem when clone with token A fails / chained clone with token B will also fail and must retry
  pull(options) {
    var ret = this.p || this.c;

    if (!ret) {
      ret = this
              .onLocal()
              .then((local) => {
                if (!local) {
                  return this.clone({ token : options.token });
                }

                this.p = Git.Repository
                          .open(this.path)
                          .then((repo) => {
                            var opt = { callbacks : { certificateCheck  : () => 1 } };

                            if (options.token) {
                              opt.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');
                            }

                            return Promise
                                    .resolve(repo.fetch('origin', opt))
                                    .then(() => {
                                      return repo.mergeBranches('master', 'origin/master');
                                    })
                                    .tap(() => {
                                      return this.getMetadata((m) => {
                                        // update token in case it changed
                                        if (m && m.token) {
                                          return this.updateMetadata({ token : options.token });
                                        }
                                      });
                                    });
                          })
                          .catch((err) => {
                            throw new Error('not found');
                          })
                          .finally(() => {
                            delete this.p;
                          });

                return this.p;
              });
    }

    return ret;
  }

  getMasterCommit() {
    return Git.Repository
            .open(this.path)
            .then((repo) => {
              return repo.getMasterCommit()
            });
  }

  entry(name) {
    return this
            .getMasterCommit()
            .then((commit) => {
              return commit.getEntry(name)
            });
  }

  blob(name) {
    return Promise
            .resolve(_.isString(name)? this.entry(name) : name)
            .then((entry) => {
              return entry.getBlob();
            });
  }

  stat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return {
                  sha       : blob.id().toString()
                , isBinary  : !!blob.isBinary()
                , rawsize   : blob.rawsize()
              };
            });
  }

  // todo [akamel] this can refetch blob even if we got it from stat
  cat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return blob.toString();
            });
  }

  walk(entry, end) {
    this
      .getMasterCommit()
      .then((commit) => {
        commit
          .getTree()
          .then((tree) => {
            var walker = tree.walk();
            walker.on('entry', entry);
            walker.on('end', end);

            walker.start();
          });
      });
  }

  // todo [akamel] this doesn't measure size
  ls() {
    return Promise
            .fromCallback((cb) => {
              var ret = [];

              this.walk((entry) => {
                if(entry.isFile()) {
                  var filename = entry.path();
                  if (path.extname(filename) === '.js' && !/\/node_modules\//.test(filename)) {
                    ret.push({
                        path  : entry.path()
                      , sha   : entry.sha()
                    });
                  }
                }
              }, () => {
                cb(undefined, {
                  data    : ret
                });
              });
            });
  }

  static get(id) {
    if (!Repository.store[id]) {
      Repository.store[id] = new Repository(id);
    }

    return Repository.store[id];
  }
}

Repository.store = {};

module.exports = {
    get : Repository.get
};<commit_msg>auto update all repositories on restart [in case we missed a repository update]<commit_after>"use strict";

var Promise   = require('bluebird')
  , url       = require('url')
  , path      = require('path')
  , fs        = require('fs-extra')
  , _         = require('underscore')
  , Git       = require('nodegit')
  , Datastore = require('nedb')
  ;

var db = new Datastore({ filename: '.db/repository.db', autoload: true });

class Repository {
  constructor(id) {
    this.id = id;

    this.remote = id;

    var url_parsed = url.parse(this.id);

    this.path = path.join('.db', url_parsed.hostname, url_parsed.pathname);

    var path_parts = url_parsed.pathname.split(path.sep);

    this.username = path_parts[1];
    this.name = path.basename(path_parts[2], '.git');
  }

  cloneAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  fetchAt() {
    return Promise
            .promisify(fs.stat)(path.join(this.path, '.git/FETCH_HEAD'))
            .then((stat) => {
              return stat.mtime.getTime();
            });
  }

  updatedAt() {
    return this.fetchAt()
            .catch(() => this.cloneAt() )
            .catch(() => undefined )
            ;
  }

  getMetadata() {
    return Promise.promisify(db.findOne, { context : db })({ remote : this.remote });
  }

  updateMetadata(patch) {
    var query   = { remote : this.remote }
      , data    = _.defaults({ remote : this.remote }, patch)
      , opts    = { upsert: true }
      ;

    return Promise.promisify(db.update, { context : db })(query, data, opts);
  }

  acl(options) {
    return this
            .getMetadata()
            .then((m) => {
              if (!_.isObject(m)) {
                throw new Error('not found');
              }

              var ret = Promise.resolve(m);

              if (m.private && options.token !== m.token) {
                // it is possible that the token was update / try a fetch and accept new token on success
                ret.tap(() => {
                  this.pull(options);
                });
              }

              return ret;
            });
  }

  onLocal() {
    return Promise
              .promisify(fs.access)(path.join(this.path, '.git/HEAD'), fs.R_OK | fs.W_OK)
              .then(() => {
                return true;
              })
              .catch(() => {
                return false;
              });
  }

  clone(options) {
    if (!this.c) {
      this.c = Promise
                .promisify(fs.ensureDir)(path.dirname(this.path))
                .then(() => {
                  var opt = { fetchOpts : { callbacks : { certificateCheck  : () => 1 } } };

                  return Promise
                          .resolve(Git.Clone(this.remote, this.path, opt))
                          .tap(() => {
                            return this.updateMetadata({ private : false });
                          })
                          .catch({ message : 'authentication required but no callback set' }, (err) => {
                            if (options.token) {
                              opt.fetchOpts.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');

                              return Promise
                                      .resolve(Git.Clone(this.remote, this.path, opt))
                                      .tap(() => {
                                        return this.updateMetadata({ private : true, token : options.token });
                                      });
                            } else {
                              throw err;
                            }
                          });
                })
                .catch((err) => {
                  delete Repository.store[this.id];
                  // mask error
                  console.error(this.remote, err);
                  throw new Error('not found');
                })
                .finally(() => {
                  delete this.c;
                });
    }

    return this.c;
  }

  // todo [akamel] problem when clone with token A fails / chained clone with token B will also fail and must retry
  pull(options) {
    var ret = this.p || this.c;

    if (!ret) {
      ret = this
              .onLocal()
              .then((local) => {
                if (!local) {
                  return this.clone({ token : options.token });
                }

                this.p = Git.Repository
                          .open(this.path)
                          .then((repo) => {
                            var opt = { callbacks : { certificateCheck  : () => 1 } };

                            if (options.token) {
                              opt.callbacks.credentials = () => Git.Cred.userpassPlaintextNew(options.token, 'x-oauth-basic');
                            }

                            return Promise
                                    .resolve(repo.fetch('origin', opt))
                                    .then(() => {
                                      return repo.mergeBranches('master', 'origin/master');
                                    })
                                    .tap(() => {
                                      return this.getMetadata((m) => {
                                        // update token in case it changed
                                        if (m && m.token) {
                                          return this.updateMetadata({ token : options.token });
                                        }
                                      });
                                    });
                          })
                          .catch((err) => {
                            throw new Error('not found');
                          })
                          .finally(() => {
                            delete this.p;
                          });

                return this.p;
              });
    }

    return ret;
  }

  getMasterCommit() {
    return Git.Repository
            .open(this.path)
            .then((repo) => {
              return repo.getMasterCommit()
            });
  }

  entry(name) {
    return this
            .getMasterCommit()
            .then((commit) => {
              return commit.getEntry(name)
            });
  }

  blob(name) {
    return Promise
            .resolve(_.isString(name)? this.entry(name) : name)
            .then((entry) => {
              return entry.getBlob();
            });
  }

  stat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return {
                  sha       : blob.id().toString()
                , isBinary  : !!blob.isBinary()
                , rawsize   : blob.rawsize()
              };
            });
  }

  // todo [akamel] this can refetch blob even if we got it from stat
  cat(name) {
    return this
            .blob(name)
            .then((blob) => {
              return blob.toString();
            });
  }

  walk(entry, end) {
    this
      .getMasterCommit()
      .then((commit) => {
        commit
          .getTree()
          .then((tree) => {
            var walker = tree.walk();
            walker.on('entry', entry);
            walker.on('end', end);

            walker.start();
          });
      });
  }

  // todo [akamel] this doesn't measure size
  ls() {
    return Promise
            .fromCallback((cb) => {
              var ret = [];

              this.walk((entry) => {
                if(entry.isFile()) {
                  var filename = entry.path();
                  if (path.extname(filename) === '.js' && !/\/node_modules\//.test(filename)) {
                    ret.push({
                        path  : entry.path()
                      , sha   : entry.sha()
                    });
                  }
                }
              }, () => {
                cb(undefined, {
                  data    : ret
                });
              });
            });
  }

  static get(id) {
    if (!Repository.store[id]) {
      Repository.store[id] = new Repository(id);
    }

    return Repository.store[id];
  }

  static pullAll() {
    db.find({}, function (err, docs) {
      Promise.map(docs, (doc) => {
        return Repository
                .get(doc.remote)
                .pull({ token : doc.token })
                .tap(() => {
                  console.log('pulled', doc.remote);
                });
      }, { concurrency : 20 });
    });
  }
}

Repository.store = {};

// todo [akamel] is this the right place for this?
Repository.pullAll();

module.exports = {
    get : Repository.get
};<|endoftext|>


<commit_before>import { rpc } from 'lib/rpcapi';
import log from 'loglevel';

export function loadContractList() {
    return (dispatch) => {
        dispatch({
            type: 'CONTRACT/LOADING',
        });
        rpc('emerald_contracts', []).then((json) => {
            if (json.error) {
                log.error(`emerald_contracts rpc call: ${JSON.stringify(json)}`);
            }
            dispatch({
                type: 'CONTRACT/SET_LIST',
                contracts: json.result,
            });
            // load Contract details?
        });
    };
}

export function addContract(address, name, abi, version, options, txhash) {
    return (dispatch) =>
        rpc('emerald_addContract', [{
            address,
            name,
            abi,
            version,
            options,
            txhash,
        }]).then((json) => {
            dispatch({
                type: 'CONTRACT/ADD_CONTRACT',
                address,
                name,
                abi,
                version,
                options,
                txhash,
            });
        });
}

export function estimateGas(data) {
    return (dispatch) =>
        rpc('eth_estimateGas', [{ data }]).then((json) => {
            return parseInt(json.result);
        });
}
<commit_msg>store/contractActions: fix NaN in estimateGas<commit_after>import { rpc } from 'lib/rpcapi';
import log from 'loglevel';

export function loadContractList() {
    return (dispatch) => {
        dispatch({
            type: 'CONTRACT/LOADING',
        });
        rpc('emerald_contracts', []).then((json) => {
            if (json.error) {
                log.error(`emerald_contracts rpc call: ${JSON.stringify(json)}`);
            }
            dispatch({
                type: 'CONTRACT/SET_LIST',
                contracts: json.result,
            });
            // load Contract details?
        });
    };
}

export function addContract(address, name, abi, version, options, txhash) {
    return (dispatch) =>
        rpc('emerald_addContract', [{
            address,
            name,
            abi,
            version,
            options,
            txhash,
        }]).then((json) => {
            dispatch({
                type: 'CONTRACT/ADD_CONTRACT',
                address,
                name,
                abi,
                version,
                options,
                txhash,
            });
        });
}

export function estimateGas(data) {
    return (dispatch) =>
        rpc('eth_estimateGas', [{ data }]).then((json) => {
            return isNaN(parseInt(json.result)) ? 0 : parseInt(json.result);
        });
}
<|endoftext|>


<commit_before>import React from 'react';
import {StyleSheet, css} from 'aphrodite/no-important';

import {Link, katex} from '../../../Components';

function render(Title, Section) {
    const urls = {
        geometricMean: "https://en.wikipedia.org/wiki/Geometric_mean",
        jensen: "https://en.wikipedia.org/wiki/Jensen%27s_inequality",
        amgm: "https://en.wikipedia.org/wiki/Inequality_of_arithmetic_and_geometric_means",
    };
    return

        <Title>Why ratios want geometric means</Title>

            You may have heard that when taking the average of a set of ratios, it’s best to use the <Link href={urls.geometricMean}>geometric mean</Link> instead of the normal arithmetic mean.
            For years, I took this as received wisdom, but recently I decided to think about it more carefully.
            It makes sense to me now—here’s my thought process.

Speed test

            Suppose that you have two computer programs, Program A and Program B.
            You want to compare how fast the two programs run on a variety of test cases.
            You time them, and observe the following results:

Trial 1: Program A is 2× faster.

Trial 2: Program A is 4× faster.

Trial 3: Program A is 0.2× as fast.

            Call these ratios {katex()`a_1 = 2.0`}, {katex()`a_2 = 4.0`}, and {katex()`a_3 = 0.2`}.
            Program A is faster than Program B on trial {katex()`i`} if {katex()`a_i > 1`}.
            We can see that Program A is sometimes faster, but Program B is sometimes faster, too.
            Which program is faster “on average”?

            With a simple arithmetic mean, we could compute:

        {katex({display: true})`\bar a = \frac{1}{3} (a_1 + a_2 + a_3) \approx 2.1.`}

            So, “on average”, it looks like Program A is {katex()`2.1`} times faster.

            But something’s not quite right here.
            Suppose that we had looked at the exact same timing data from the perspective of Program B instead.
            We would have observed these results:

Trial 1: Program B is 0.5× as fast.

Trial 2: Program B is 0.25× as fast.

Trial 3: Program B is 5× faster.

            That is, we have {katex()`b_1 = 0.5`}, {katex()`b_2 = 0.25`}, and {katex()`b_3 = 5.0`}, where each {katex()`b_i = 1 / a_i`}.
            Taking an arithmetic mean again, we find:

        {katex({display: true})`\bar b = \frac{1}{3} (b_1 + b_2 + b_3) \approx 1.9.`}

            But then we are forced to conclude that, “on average”, Program A is {katex()`2.1`} times faster than Program B, and Program B is also {katex()`1.9`} times faster than Program A!
            Surely any framework that leads us to such a conclusion must be flawed.
            An arithmetic mean must not be the right tool: let’s return to the drawing board.

Composition

            Consider for a moment what the arithmetic mean means.
            It says, “combine your three quantities by adding them together, then scale them down by a factor of three to counteract the change in magnitude”.
            It we have three quantities of distance, or of mass, or of time, this makes sense.
            If you travel {katex()`3`} meters and then travel {katex()`2`} meters, you’ve traveled {katex()`3 + 2 = 5`} meters.

            But that’s not how ratios compose.
            If you make a program {katex()`1.2`} times faster, and then make it {katex()`1.2`} times faster again, you haven’t made it {katex()`1.2 + 1.2 = 1.4`} times faster—you’ve made it {katex()`1.2 \times 1.2 = 1.44`} times faster.
            Thus, to take an average in the same spirit as the arithmetic mean, we should combine our three ratios by multiplying them together, and then scale them down by a third root to counteract the change in magnitude.
            And this is precisely the geometric mean.

            Returning to our speed comparison example, we can reëvaluate how much faster Program A is “on average”…

        {katex({display: true})`\tilde a = (a_1 \cdot a_2 \cdot a_3)^{1/3} = 1.6,`}

            …and Program B:

        {katex({display: true})`\tilde b = (b_1 \cdot b_2 \cdot b_3)^{1/3} = 0.625.`}

            Now our results are consistent: Program A is faster, and Program B is slower.
            Moreover, note that {katex()`\tilde a = 1 / \tilde b`}, which makes sense.
            This is another property that carries over in spirit from arithmetic means.
            If you travel, on average, {katex()`3`} meters farther than me, then I travel {katex()`-3`} meters farther than you; and just as ratios compose with multiplication rather than addition, they invert with reciprocation rather than negation.

The logarithmic connection

            Look more closely at these analogies that hold “in spirit” between ratios and the more general real numbers:

Ratios live in {katex()`\mathbb{R}^+`}, and reals live in {katex()`\mathbb{R}`}.

Ratios combine with multiplication, and reals with addition.

Ratios scale down with {katex()`n`}th roots, and reals with division.

Ratios invert with reciprocation, and reals with negation.

            The common structure underlying these mappings is the logarithm:

The image of {katex()`\mathbb{R}^+`} under the logarithm is {katex()`\mathbb{R}`}.

For ratios {katex()`a`} and {katex()`b`}, we have {katex()`\log(a \cdot b) = \log(a) + \log(b)`}.

For a ratio {katex()`a`} and positive scalar {katex()`c`}, we have {katex()`\log(a^{1/c}) = \log(a) / c`}.

For a ratio {katex()`a`}, we have {katex()`\log(1 / a) = -\mskip-1mu\log(a)`}.

            Because the logarithm preserves all the structure that we care about, we say that it is a homomorphism (“same shape”) from the ratios to the reals.
            Moreover, since the logarithm is invertible (via the exponential function), and its inverse likewise preserves this structure, it is an isomorphism, which means that these two spaces are really just different characterizations of the same thing.
            Thus, another way to think about the geometric mean is that it uses the logarithm to bring the ratios into “linear space”, takes their arithmetic mean, and then returns them to their natural habitat:

            {katex({display: true})`
                \exp(\overline{\log z_1, \dotsc, \log z_n})
                = \exp\Biggl( \frac{1}{n} \sum_{i=1}^{n} \log z_i \Biggr)
                = \Biggl( \,\prod_{i=1}^{n} z_i \Biggr)^{\mskip-3mu 1/n}
                = \widetilde{z_1, \dotsc, z_n}.
            `}

            {katex({display: true})`
                \begin{aligned}
                    &\mathrel{\hphantom=} \exp(\overline{\log z_1, \dotsc, \log z_n}) \\[1.2ex]
                    &= \exp\Biggl( \frac{1}{n} \sum_{i=1}^{n} \log z_i \Biggr) \\[3ex]
                    &= \Biggl( \,\prod_{i=1}^{n} z_i \Biggr)^{\mskip-3mu 1/n} \\[3ex]
                    &= \widetilde{z_1, \dotsc, z_n}.
                \end{aligned}
            `}

            This also suggests that if we wanted to, say, summarize the distribution of a random variable representing ratios, we should probably not take its expectation.
            We would get a more meaningful result by taking the exponential of the expectation of its logarithm, {katex()`\exp(\mathbb{E}[\log Z])`}.
            And this in turn brings to mind <Link href={urls.jensen}>Jensen’s inequality</Link>, with which we immediately obtain a nice proof of the <Link href={urls.amgm}>AM–GM inequality</Link> by taking {katex()`Z`} to be a uniform random variable over a set {katex()`z_1, \dotsc, z_n`}:

            {katex({display: true})`\widetilde{z_1, \dotsc, z_n} = \exp(\mathbb{E}[\log Z]) \leq \exp(\log(\mathbb{E}[Z])) = \mathbb{E}[Z] = \overline{z_1, \dotsc, z_n},`}

            {katex({display: true})`
                \begin{aligned}
                    &\mathrel{\hphantom=} \widetilde{z_1, \dotsc, z_n} \\
                    &= \exp(\mathbb{E}[\log Z]) \\
                    &\leq \exp(\log(\mathbb{E}[Z])) \\
                    &= \mathbb{E}[Z] \\
                    &= \overline{z_1, \dotsc, z_n},
                \end{aligned}
            `}

            with the second step due to Jensen’s inequality, the concavity of the logarithm, and the monotonicity of the exponential.
            All the pieces fit nicely together… one connection at a time.

;
}

const tallEquationsMediaQuery = '@media(max-width:700px)';

const styles = StyleSheet.create({
    wideEquation: {
        [tallEquationsMediaQuery]: {
            display: 'none',
        },
    },
    tallEquation: {
        display: 'none',
        [tallEquationsMediaQuery]: {
            display: 'unset',
        },
    },
});

export default {
    id: 7,
    title: 'Why ratios want geometric means',
    filename: 'geometricMeanRatios.js',
    date: '2020-08-28',
    render,
};
<commit_msg>Fix arithmetic in “Why ratios want geometric means” (#7)<commit_after>import React from 'react';
import {StyleSheet, css} from 'aphrodite/no-important';

import {Link, katex} from '../../../Components';

function render(Title, Section) {
    const urls = {
        geometricMean: "https://en.wikipedia.org/wiki/Geometric_mean",
        jensen: "https://en.wikipedia.org/wiki/Jensen%27s_inequality",
        amgm: "https://en.wikipedia.org/wiki/Inequality_of_arithmetic_and_geometric_means",
    };
    return

        <Title>Why ratios want geometric means</Title>

            You may have heard that when taking the average of a set of ratios, it’s best to use the <Link href={urls.geometricMean}>geometric mean</Link> instead of the normal arithmetic mean.
            For years, I took this as received wisdom, but recently I decided to think about it more carefully.
            It makes sense to me now—here’s my thought process.

Speed test

            Suppose that you have two computer programs, Program A and Program B.
            You want to compare how fast the two programs run on a variety of test cases.
            You time them, and observe the following results:

Trial 1: Program A is 2× faster.

Trial 2: Program A is 4× faster.

Trial 3: Program A is 0.2× as fast.

            Call these ratios {katex()`a_1 = 2.0`}, {katex()`a_2 = 4.0`}, and {katex()`a_3 = 0.2`}.
            Program A is faster than Program B on trial {katex()`i`} if {katex()`a_i > 1`}.
            We can see that Program A is sometimes faster, but Program B is sometimes faster, too.
            Which program is faster “on average”?

            With a simple arithmetic mean, we could compute:

        {katex({display: true})`\bar a = \frac{1}{3} (a_1 + a_2 + a_3) \approx 2.1.`}

            So, “on average”, it looks like Program A is {katex()`2.1`} times faster.

            But something’s not quite right here.
            Suppose that we had looked at the exact same timing data from the perspective of Program B instead.
            We would have observed these results:

Trial 1: Program B is 0.5× as fast.

Trial 2: Program B is 0.25× as fast.

Trial 3: Program B is 5× faster.

            That is, we have {katex()`b_1 = 0.5`}, {katex()`b_2 = 0.25`}, and {katex()`b_3 = 5.0`}, where each {katex()`b_i = 1 / a_i`}.
            Taking an arithmetic mean again, we find:

        {katex({display: true})`\bar b = \frac{1}{3} (b_1 + b_2 + b_3) \approx 1.9.`}

            But then we are forced to conclude that, “on average”, Program A is {katex()`2.1`} times faster than Program B, and Program B is also {katex()`1.9`} times faster than Program A!
            Surely any framework that leads us to such a conclusion must be flawed.
            An arithmetic mean must not be the right tool: let’s return to the drawing board.

Composition

            Consider for a moment what the arithmetic mean means.
            It says, “combine your three quantities by adding them together, then scale them down by a factor of three to counteract the change in magnitude”.
            It we have three quantities of distance, or of mass, or of time, this makes sense.
            If you travel {katex()`3`} meters and then travel {katex()`2`} meters, you’ve traveled {katex()`3 + 2 = 5`} meters.

            But that’s not how ratios compose.
            If you make a program {katex()`1.2`} times faster, and then make it {katex()`1.2`} times faster again, you haven’t made it {katex()`1.2 + 1.2 = 2.4`} times faster—you’ve made it {katex()`1.2 \times 1.2 = 1.44`} times faster.
            Thus, to take an average in the same spirit as the arithmetic mean, we should combine our three ratios by multiplying them together, and then scale them down by a third root to counteract the change in magnitude.
            And this is precisely the geometric mean.

            Returning to our speed comparison example, we can reëvaluate how much faster Program A is “on average”…

        {katex({display: true})`\tilde a = (a_1 \cdot a_2 \cdot a_3)^{1/3} = 1.6,`}

            …and Program B:

        {katex({display: true})`\tilde b = (b_1 \cdot b_2 \cdot b_3)^{1/3} = 0.625.`}

            Now our results are consistent: Program A is faster, and Program B is slower.
            Moreover, note that {katex()`\tilde a = 1 / \tilde b`}, which makes sense.
            This is another property that carries over in spirit from arithmetic means.
            If you travel, on average, {katex()`3`} meters farther than me, then I travel {katex()`-3`} meters farther than you; and just as ratios compose with multiplication rather than addition, they invert with reciprocation rather than negation.

The logarithmic connection

            Look more closely at these analogies that hold “in spirit” between ratios and the more general real numbers:

Ratios live in {katex()`\mathbb{R}^+`}, and reals live in {katex()`\mathbb{R}`}.

Ratios combine with multiplication, and reals with addition.

Ratios scale down with {katex()`n`}th roots, and reals with division.

Ratios invert with reciprocation, and reals with negation.

            The common structure underlying these mappings is the logarithm:

The image of {katex()`\mathbb{R}^+`} under the logarithm is {katex()`\mathbb{R}`}.

For ratios {katex()`a`} and {katex()`b`}, we have {katex()`\log(a \cdot b) = \log(a) + \log(b)`}.

For a ratio {katex()`a`} and positive scalar {katex()`c`}, we have {katex()`\log(a^{1/c}) = \log(a) / c`}.

For a ratio {katex()`a`}, we have {katex()`\log(1 / a) = -\mskip-1mu\log(a)`}.

            Because the logarithm preserves all the structure that we care about, we say that it is a homomorphism (“same shape”) from the ratios to the reals.
            Moreover, since the logarithm is invertible (via the exponential function), and its inverse likewise preserves this structure, it is an isomorphism, which means that these two spaces are really just different characterizations of the same thing.
            Thus, another way to think about the geometric mean is that it uses the logarithm to bring the ratios into “linear space”, takes their arithmetic mean, and then returns them to their natural habitat:

            {katex({display: true})`
                \exp(\overline{\log z_1, \dotsc, \log z_n})
                = \exp\Biggl( \frac{1}{n} \sum_{i=1}^{n} \log z_i \Biggr)
                = \Biggl( \,\prod_{i=1}^{n} z_i \Biggr)^{\mskip-3mu 1/n}
                = \widetilde{z_1, \dotsc, z_n}.
            `}

            {katex({display: true})`
                \begin{aligned}
                    &\mathrel{\hphantom=} \exp(\overline{\log z_1, \dotsc, \log z_n}) \\[1.2ex]
                    &= \exp\Biggl( \frac{1}{n} \sum_{i=1}^{n} \log z_i \Biggr) \\[3ex]
                    &= \Biggl( \,\prod_{i=1}^{n} z_i \Biggr)^{\mskip-3mu 1/n} \\[3ex]
                    &= \widetilde{z_1, \dotsc, z_n}.
                \end{aligned}
            `}

            This also suggests that if we wanted to, say, summarize the distribution of a random variable representing ratios, we should probably not take its expectation.
            We would get a more meaningful result by taking the exponential of the expectation of its logarithm, {katex()`\exp(\mathbb{E}[\log Z])`}.
            And this in turn brings to mind <Link href={urls.jensen}>Jensen’s inequality</Link>, with which we immediately obtain a nice proof of the <Link href={urls.amgm}>AM–GM inequality</Link> by taking {katex()`Z`} to be a uniform random variable over a set {katex()`z_1, \dotsc, z_n`}:

            {katex({display: true})`\widetilde{z_1, \dotsc, z_n} = \exp(\mathbb{E}[\log Z]) \leq \exp(\log(\mathbb{E}[Z])) = \mathbb{E}[Z] = \overline{z_1, \dotsc, z_n},`}

            {katex({display: true})`
                \begin{aligned}
                    &\mathrel{\hphantom=} \widetilde{z_1, \dotsc, z_n} \\
                    &= \exp(\mathbb{E}[\log Z]) \\
                    &\leq \exp(\log(\mathbb{E}[Z])) \\
                    &= \mathbb{E}[Z] \\
                    &= \overline{z_1, \dotsc, z_n},
                \end{aligned}
            `}

            with the second step due to Jensen’s inequality, the concavity of the logarithm, and the monotonicity of the exponential.
            All the pieces fit nicely together… one connection at a time.

;
}

const tallEquationsMediaQuery = '@media(max-width:700px)';

const styles = StyleSheet.create({
    wideEquation: {
        [tallEquationsMediaQuery]: {
            display: 'none',
        },
    },
    tallEquation: {
        display: 'none',
        [tallEquationsMediaQuery]: {
            display: 'unset',
        },
    },
});

export default {
    id: 7,
    title: 'Why ratios want geometric means',
    filename: 'geometricMeanRatios.js',
    date: '2020-08-28',
    render,
};
<|endoftext|>


<commit_before>/**
 * Copyright 2010 Ajax.org B.V.
 *
 * This product includes software developed by
 * Ajax.org B.V. (http://www.ajax.org/).
 *
 * Author: Ryan Funduk <ryan.funduk@gmail.com>
 */

var sys = require("sys");
var AbstractApi = require("./AbstractApi").AbstractApi;

var PullApi = exports.PullApi = function(api) {
    this.$api = api;
};

sys.inherits(PullApi, AbstractApi);

(function() {
    /**
     * List pulls by username, repo and (optionally) state
     * http://develop.github.com/p/pulls.html
     *
     * @param {String}  username         the username
     * @param {String}  repo             the repo
     * @param {String}  $state           the issue state, optional
     */
    this.getList = function(username, repo, state, callback)
    {
        if (typeof(state) == 'function') {
            callback = state;
            state = '';
        }
        else {
            state = '/' + encodeURI(state);
        }
        this.$api.get(
            'pulls/' + encodeURI(username) + "/" + encodeURI(repo) + state,
            null, null,
            this.$createListener(callback, "pulls")
        );
    };

}).call(PullApi.prototype);
<commit_msg>Implement PullApi.show<commit_after>/**
 * Copyright 2010 Ajax.org B.V.
 *
 * This product includes software developed by
 * Ajax.org B.V. (http://www.ajax.org/).
 *
 * Author: Ryan Funduk <ryan.funduk@gmail.com>
 */

var sys = require("sys");
var AbstractApi = require("./AbstractApi").AbstractApi;

var PullApi = exports.PullApi = function(api) {
    this.$api = api;
};

sys.inherits(PullApi, AbstractApi);

(function() {
    /**
     * List pulls by username, repo and (optionally) state
     * http://develop.github.com/p/pulls.html
     *
     * @param {String}  username         the username
     * @param {String}  repo             the repo
     * @param {String}  $state           the issue state, optional
     */
    this.getList = function(username, repo, state, callback)
    {
        if (typeof(state) == 'function') {
            callback = state;
            state = '';
        }
        else {
            state = '/' + encodeURI(state);
        }
        this.$api.get(
            'pulls/' + encodeURI(username) + "/" + encodeURI(repo) + state,
            null, null,
            this.$createListener(callback, "pulls")
        );
    };

    /**
     * Show a pull by username, repo, and number.
     * http://develop.github.com/p/pulls.html
     *
     * @param {String}  username        the username
     * @param {String}  repo            the repo
     * @param {Int}     num             the pull number
     */
    this.show = function(username, repo, num, callback)
    {
        this.$api.get(
            'pulls/' + encodeURI(username) + '/' + encodeURI(repo) + '/' + num,
            null, null,
            this.$createListener(callback, 'pull')
        );
    };

}).call(PullApi.prototype);
<|endoftext|>


<commit_before>"use strict";
var AppDispatcher = require('../dispatcher/AppDispatcher');
var AppConstants = require('../constants/AppConstants');
var {EventEmitter} = require('events');
var assign = require('object-assign');
var WebAPIUtils = require("../utils/WebAPIUtils");

var {ActionTypes} = AppConstants;
var CHANGE_EVENT = 'change';
var _current = null;
var _candidates = {
  '5': {
    id: 5,
    name: '馮光遠',
    avatar: '/assets/images/candidates/5.jpg', //require("../../client/images/candidates/5.jpg")
    avatar_square: '/assets/images/candidates_avatar/5.jpg',
    status: false
  },
  '6': {
    id: 6,
    name: '連勝文',
    avatar: '/assets/images/candidates/6.jpg', //require("../../client/images/candidates/6.jpg")
    avatar_square: '/assets/images/candidates_avatar/6.jpg',
    status: false
  },
  '7': {
    id: 7,
    name: '柯文哲',
    avatar: '/assets/images/candidates/7.jpg', //require("../../client/images/candidates/7.jpg")
    avatar_square: '/assets/images/candidates_avatar/7.jpg',
    status: false
  }
};

var CandidateStore = assign({}, EventEmitter.prototype, {

  emitChange () {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener (callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener (callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  getStatus () {
    var result = {};
    Object.keys(_candidates).map(function (c) {
      return result[c] = _candidates[c].status;
    });
    return result;
  },

  getCurrentCandidate () {
    return _current;
  },

  get (id) {
    return _candidates[id];
  },

  getAll () {
    return Object.keys(_candidates).map((c) => {
      return _candidates[c];
    });
  }
});

CandidateStore.dispatchToken = AppDispatcher.register((payload) => {
  var {action} = payload;
  switch(action.type) {
    case ActionTypes.CHOOSE_CANDIDATE:
      _current=action.id;
      CandidateStore.emitChange();
      break;
    case ActionTypes.STATUS:
      WebAPIUtils.checkStatus(function (err, res) {
        if (err) {
          return console.log(err);
        }
        Object.keys(_candidates).map(function(c, i){
          var allStatus = res.body.data;
          if(allStatus)
             _candidates[c]['status'] = allStatus[i];
        });
        CandidateStore.emitChange();
      });
      break;
    default:
  }

});

module.exports = CandidateStore;<commit_msg>add workaround comment<commit_after>"use strict";
var AppDispatcher = require('../dispatcher/AppDispatcher');
var AppConstants = require('../constants/AppConstants');
var {EventEmitter} = require('events');
var assign = require('object-assign');
var WebAPIUtils = require("../utils/WebAPIUtils");

var {ActionTypes} = AppConstants;
var CHANGE_EVENT = 'change';
var _current = null;
var _candidates = {
  '5': {
    id: 5,
    name: '馮光遠',
    avatar: '/assets/images/candidates/5.jpg', //require("../../client/images/candidates/5.jpg")
    avatar_square: '/assets/images/candidates_avatar/5.jpg',
    status: false
  },
  '6': {
    id: 6,
    name: '連勝文',
    avatar: '/assets/images/candidates/6.jpg', //require("../../client/images/candidates/6.jpg")
    avatar_square: '/assets/images/candidates_avatar/6.jpg',
    status: false
  },
  '7': {
    id: 7,
    name: '柯文哲',
    avatar: '/assets/images/candidates/7.jpg', //require("../../client/images/candidates/7.jpg")
    avatar_square: '/assets/images/candidates_avatar/7.jpg',
    status: false
  }
};

var CandidateStore = assign({}, EventEmitter.prototype, {

  emitChange () {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener (callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener (callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  getStatus () {
    var result = {};
    Object.keys(_candidates).map(function (c) {
      return result[c] = _candidates[c].status;
    });
    return result;
  },

  getCurrentCandidate () {
    return _current;
  },

  get (id) {
    return _candidates[id];
  },

  getAll () {
    return Object.keys(_candidates).map((c) => {
      return _candidates[c];
    });
  }
});

CandidateStore.dispatchToken = AppDispatcher.register((payload) => {
  var {action} = payload;
  switch(action.type) {
    case ActionTypes.CHOOSE_CANDIDATE:
      _current=action.id;
      CandidateStore.emitChange();
      break;
    case ActionTypes.STATUS:
      WebAPIUtils.checkStatus(function (err, res) {
        if (err) {
          return console.log(err);
        }
        Object.keys(_candidates).map(function(c, i){
          var allStatus = res.body.data;
          if(allStatus)//this is a workaround
             _candidates[c]['status'] = allStatus[i];
        });
        CandidateStore.emitChange();
      });
      break;
    default:
  }

});

module.exports = CandidateStore;<|endoftext|>


<commit_before>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
 * Dont forget to add the view to your DOM or you will not see anything :)
 *
 * @class CanvasRenderer
 * @constructor
 * @param [width=800] {Number} the width of the canvas view
 * @param [height=600] {Number} the height of the canvas view

 * @param [options] {Object} The optional renderer parameters
 * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional
 * @param [options.transparent=false] {Boolean} If the render view is transparent, default false
 * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2
 * @param [options.clearBeforeRender=true] {Boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
 */
PIXI.CanvasRenderer = function(width, height, options)
{
    if(options)
    {
        for (var i in PIXI.defaultRenderOptions)
        {
            if (typeof options[i] === "undefined") options[i] = PIXI.defaultRenderOptions[i];
        }
    }
    else
    {
        options = PIXI.defaultRenderOptions;
    }

    if(!PIXI.defaultRenderer)
    {
        PIXI.sayHello("Canvas");
        PIXI.defaultRenderer = this;
    }

    this.type = PIXI.CANVAS_RENDERER;

    this.resolution = options.resolution;

    /**
     * This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
     * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color.
     * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame.
     * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set.
     *
     * @property clearBeforeRender
     * @type Boolean
     * @default
     */
    this.clearBeforeRender = options.clearBeforeRender;

    /**
     * Whether the render view is transparent
     *
     * @property transparent
     * @type Boolean
     */
    this.transparent = options.transparent;

    /**
     * The width of the canvas view
     *
     * @property width
     * @type Number
     * @default 800
     */
    this.width = width || 800;

    /**
     * The height of the canvas view
     *
     * @property height
     * @type Number
     * @default 600
     */
    this.height = height || 600;

    this.width *= this.resolution;
    this.height *= this.resolution;

    /**
     * The canvas element that everything is drawn to
     *
     * @property view
     * @type HTMLCanvasElement
     */
    this.view = options.view || document.createElement( "canvas" );

    /**
     * The canvas 2d context that everything is drawn with
     * @property context
     * @type CanvasRenderingContext2D 2d Context
     */
    this.context = this.view.getContext( "2d", { alpha: this.transparent } );

    this.refresh = true;
    // hack to enable some hardware acceleration!
    //this.view.style["transform"] = "translatez(0)";

    this.view.width = this.width * this.resolution;
    this.view.height = this.height * this.resolution;
    this.count = 0;

    /**
     * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer
     * @property CanvasMaskManager
     * @type CanvasMaskManager
     */
    this.maskManager = new PIXI.CanvasMaskManager();

    /**
     * The render session is just a bunch of parameter used for rendering
     * @property renderSession
     * @type Object
     */
    this.renderSession = {
        context: this.context,
        maskManager: this.maskManager,
        scaleMode: null,
        smoothProperty: null,
        /**
         * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
         * Handy for crisp pixel art and speed on legacy devices.
         *
         */
        roundPixels: false
    };

    this.resize(width, height);

    if("imageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "imageSmoothingEnabled";
    else if("webkitImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "webkitImageSmoothingEnabled";
    else if("mozImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "mozImageSmoothingEnabled";
    else if("oImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "oImageSmoothingEnabled";
    else if ("msImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "msImageSmoothingEnabled";
};

// constructor
PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;

/**
 * Renders the stage to its canvas view
 *
 * @method render
 * @param stage {Stage} the Stage element to be rendered
 */
PIXI.CanvasRenderer.prototype.render = function(stage)
{
    stage.updateTransform();

    this.context.setTransform(1,0,0,1,0,0);

    this.context.globalAlpha = 1;

    if (navigator.isCocoonJS && this.view.screencanvas) {
        this.context.fillStyle = "black";
        this.context.clear();
    }

    if (!this.transparent && this.clearBeforeRender)
    {
        this.context.fillStyle = stage.backgroundColorString;
        this.context.fillRect(0, 0, this.width , this.height);
    }
    else if (this.transparent && this.clearBeforeRender)
    {
        this.context.clearRect(0, 0, this.width, this.height);
    }

    this.renderDisplayObject(stage);

    // run interaction!
    if(stage.interactive)
    {
        //need to add some events!
        if(!stage._interactiveEventsAdded)
        {
            stage._interactiveEventsAdded = true;
            stage.interactionManager.setTarget(this);
        }
    }
};

/**
 * Resizes the canvas view to the specified width and height
 *
 * @method resize
 * @param width {Number} the new width of the canvas view
 * @param height {Number} the new height of the canvas view
 */
PIXI.CanvasRenderer.prototype.resize = function(width, height)
{
    this.width = width * this.resolution;
    this.height = height * this.resolution;

    this.view.width = this.width;
    this.view.height = this.height;

    this.view.style.width = this.width / this.resolution + "px";
    this.view.style.height = this.height / this.resolution + "px";
};

/**
 * Renders a display object
 *
 * @method renderDisplayObject
 * @param displayObject {DisplayObject} The displayObject to render
 * @param context {CanvasRenderingContext2D} the context 2d method of the canvas
 * @private
 */
PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context)
{
    this.renderSession.context = context || this.context;
    this.renderSession.resolution = this.resolution;
    displayObject._renderCanvas(this.renderSession);
};

PIXI.CanvasRenderer.prototype.mapBlendModes = function()
{
    if(!PIXI.blendModesCanvas)
    {
        PIXI.blendModesCanvas = [];

        if(PIXI.canUseNewCanvasBlendModes())
        {
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "screen";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "overlay";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "darken";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "lighten";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "hue";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "color";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity";
        }
        else
        {
            // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough"
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over";
        }
    }
};
<commit_msg>fixed blendmode mapping in canvas bug<commit_after>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * the CanvasRenderer draws the stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL.
 * Dont forget to add the view to your DOM or you will not see anything :)
 *
 * @class CanvasRenderer
 * @constructor
 * @param [width=800] {Number} the width of the canvas view
 * @param [height=600] {Number} the height of the canvas view

 * @param [options] {Object} The optional renderer parameters
 * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional
 * @param [options.transparent=false] {Boolean} If the render view is transparent, default false
 * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2
 * @param [options.clearBeforeRender=true] {Boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
 */
PIXI.CanvasRenderer = function(width, height, options)
{
    if(options)
    {
        for (var i in PIXI.defaultRenderOptions)
        {
            if (typeof options[i] === "undefined") options[i] = PIXI.defaultRenderOptions[i];
        }
    }
    else
    {
        options = PIXI.defaultRenderOptions;
    }

    if(!PIXI.defaultRenderer)
    {
        PIXI.sayHello("Canvas");
        PIXI.defaultRenderer = this;
    }

    this.type = PIXI.CANVAS_RENDERER;

    this.resolution = options.resolution;

    /**
     * This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
     * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color.
     * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame.
     * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set.
     *
     * @property clearBeforeRender
     * @type Boolean
     * @default
     */
    this.clearBeforeRender = options.clearBeforeRender;

    /**
     * Whether the render view is transparent
     *
     * @property transparent
     * @type Boolean
     */
    this.transparent = options.transparent;

    /**
     * The width of the canvas view
     *
     * @property width
     * @type Number
     * @default 800
     */
    this.width = width || 800;

    /**
     * The height of the canvas view
     *
     * @property height
     * @type Number
     * @default 600
     */
    this.height = height || 600;

    this.width *= this.resolution;
    this.height *= this.resolution;

    /**
     * The canvas element that everything is drawn to
     *
     * @property view
     * @type HTMLCanvasElement
     */
    this.view = options.view || document.createElement( "canvas" );

    /**
     * The canvas 2d context that everything is drawn with
     * @property context
     * @type CanvasRenderingContext2D 2d Context
     */
    this.context = this.view.getContext( "2d", { alpha: this.transparent } );

    this.refresh = true;
    // hack to enable some hardware acceleration!
    //this.view.style["transform"] = "translatez(0)";

    this.view.width = this.width * this.resolution;
    this.view.height = this.height * this.resolution;
    this.count = 0;

    /**
     * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer
     * @property CanvasMaskManager
     * @type CanvasMaskManager
     */
    this.maskManager = new PIXI.CanvasMaskManager();

    /**
     * The render session is just a bunch of parameter used for rendering
     * @property renderSession
     * @type Object
     */
    this.renderSession = {
        context: this.context,
        maskManager: this.maskManager,
        scaleMode: null,
        smoothProperty: null,
        /**
         * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
         * Handy for crisp pixel art and speed on legacy devices.
         *
         */
        roundPixels: false
    };

    this.mapBlendModes();

    this.resize(width, height);

    if("imageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "imageSmoothingEnabled";
    else if("webkitImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "webkitImageSmoothingEnabled";
    else if("mozImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "mozImageSmoothingEnabled";
    else if("oImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "oImageSmoothingEnabled";
    else if ("msImageSmoothingEnabled" in this.context)
        this.renderSession.smoothProperty = "msImageSmoothingEnabled";
};

// constructor
PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer;

/**
 * Renders the stage to its canvas view
 *
 * @method render
 * @param stage {Stage} the Stage element to be rendered
 */
PIXI.CanvasRenderer.prototype.render = function(stage)
{
    stage.updateTransform();

    this.context.setTransform(1,0,0,1,0,0);

    this.context.globalAlpha = 1;

    if (navigator.isCocoonJS && this.view.screencanvas) {
        this.context.fillStyle = "black";
        this.context.clear();
    }

    if (!this.transparent && this.clearBeforeRender)
    {
        this.context.fillStyle = stage.backgroundColorString;
        this.context.fillRect(0, 0, this.width , this.height);
    }
    else if (this.transparent && this.clearBeforeRender)
    {
        this.context.clearRect(0, 0, this.width, this.height);
    }

    this.renderDisplayObject(stage);

    // run interaction!
    if(stage.interactive)
    {
        //need to add some events!
        if(!stage._interactiveEventsAdded)
        {
            stage._interactiveEventsAdded = true;
            stage.interactionManager.setTarget(this);
        }
    }
};

/**
 * Resizes the canvas view to the specified width and height
 *
 * @method resize
 * @param width {Number} the new width of the canvas view
 * @param height {Number} the new height of the canvas view
 */
PIXI.CanvasRenderer.prototype.resize = function(width, height)
{
    this.width = width * this.resolution;
    this.height = height * this.resolution;

    this.view.width = this.width;
    this.view.height = this.height;

    this.view.style.width = this.width / this.resolution + "px";
    this.view.style.height = this.height / this.resolution + "px";
};

/**
 * Renders a display object
 *
 * @method renderDisplayObject
 * @param displayObject {DisplayObject} The displayObject to render
 * @param context {CanvasRenderingContext2D} the context 2d method of the canvas
 * @private
 */
PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context)
{
    this.renderSession.context = context || this.context;
    this.renderSession.resolution = this.resolution;
    displayObject._renderCanvas(this.renderSession);
};

PIXI.CanvasRenderer.prototype.mapBlendModes = function()
{
    if(!PIXI.blendModesCanvas)
    {
        PIXI.blendModesCanvas = [];

        if(PIXI.canUseNewCanvasBlendModes())
        {
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "screen";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "overlay";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "darken";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "lighten";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "hue";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "color";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity";
        }
        else
        {
            // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough"
            PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.ADD]      = "lighter"; //IS THIS OK???
            PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SCREEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DARKEN]   = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN]  = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.HUE]       = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.COLOR]      = "source-over";
            PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over";
        }
    }
};
<|endoftext|>


<commit_before>/*!
 *
 * @author Andrew Teologov <teologov.and@gmail.com>
 * @date 11/23/15
 */

// router middleware which does require() on first activation
// instead of:
//   require('./router').middleware()
// do:
//   require('lib/lazyRouter')('./router')
// purpose: don't require everything on startup

module.exports = function(handlerAbsPath) {
    var handler = null;

    return function handlerMounter(handlerContext) {    // app's context or client
        if (!handler) {
            handler = module.parent.require.call(this, handlerAbsPath); // do not pass context as argument
        }

        if (handler instanceof Function) {
            return handler.apply(handlerContext, [].slice.call(arguments, 1));
        }
        else if(handler instanceof Object) {
            return handler;
        }
    };

};

delete require.cache[__filename];
<commit_msg>minor<commit_after>/*!
 * Handler mounter
 * @author Andrew Teologov <teologov.and@gmail.com>
 * @date 11/23/15
 */

// router middleware which does require() on first activation
// instead of:
//   require('./router').middleware()
// do:
//   require('lib/lazyRouter')('./router')
// purpose: don't require everything on startup

module.exports = function(handlerAbsPath) {
    var handler = null;

    return function handlerMounter(handlerContext) {    // app's context or client
        if (!handler) {
            handler = module.parent.require.call(this, handlerAbsPath); // do not pass context as argument
        }

        if (handler instanceof Function) {
            return handler.apply(handlerContext, [].slice.call(arguments, 1));
        }
        else if (handler instanceof Object) {
            return handler;
        }
    };

};

delete require.cache[__filename];
<|endoftext|>


<commit_before>'use strict';
var mysql = require('../db/mysql');
var config = require('../config');
var store = new mysql(config.store.mysql);
var models = require('../models');

var admin = module.exports = {
  name: 'admin'
};

admin.renderAdmin = function renderAdmin (req, res) {
  var root = req.app.locals.url('', true, req.secure);
  var statik = req.app.locals.urlForStatic('', req.secure);

  res.render('admin/index', {
    host: req.app.get('url host'),
    token: req.session._csrf,
    title: 'Admin',
    layout: 'sub/layout.html',
    httproot: root.replace('https', 'http'),
    root: root,
    'static': statik,
    request: req,
    cacheBust: req.app.get('is_production') ? '?' + req.app.get('version') : ''
  });
};

admin.flagBin = function flagBin (req, res) {
  var url = req.body.bin;
  var rev = req.body.rev;

  // check if the bin exist
  store.getLatestBin({id: url}, function (a, bin) {
    if (bin) {
      // if no rev, get latest
      if (rev === 'latest') {
        rev = bin.revision;
      }
      // update active flag
      models.bin.updateBinData({url: url, revision: rev}, {active: 'y'}, function (err) {
        if (err) {
          console.log(err.stack);
          res.send(400, err);
        }
        res.json(200, { all: 'ok'});
      });
    } else {
      res.json(400, { all: 'Bin not found'});
    }
  });
};

admin.flagUser = function flagUser (req, res) {
  var username = req.body.username;

  // check if the user exist
  store.getUser(username, function(a, user) {
    if (user) {
      // update flagged status
      req.app.store.updateOwnershipData([username], {flagged: true}, function (err) {
        if (err) {
          console.log(err.stack);
          res.send(400, err);
        }
        res.json(200, { all: 'ok'});
      });
    } else {
      res.json(400, { all: 'User not found'});
    }
  });

};
<commit_msg>Cachebust is now a global template var<commit_after>'use strict';
var mysql = require('../db/mysql');
var config = require('../config');
var store = new mysql(config.store.mysql);
var models = require('../models');

var admin = module.exports = {
  name: 'admin'
};

admin.renderAdmin = function renderAdmin (req, res) {
  var root = req.app.locals.url('', true, req.secure);
  var statik = req.app.locals.urlForStatic('', req.secure);

  res.render('admin/index', {
    host: req.app.get('url host'),
    token: req.session._csrf,
    title: 'Admin',
    layout: 'sub/layout.html',
    httproot: root.replace('https', 'http'),
    root: root,
    'static': statik,
    request: req,
  });
};

admin.flagBin = function flagBin (req, res) {
  var url = req.body.bin;
  var rev = req.body.rev;

  // check if the bin exist
  store.getLatestBin({id: url}, function (a, bin) {
    if (bin) {
      // if no rev, get latest
      if (rev === 'latest') {
        rev = bin.revision;
      }
      // update active flag
      models.bin.updateBinData({url: url, revision: rev}, {active: 'y'}, function (err) {
        if (err) {
          console.log(err.stack);
          res.send(400, err);
        }
        res.json(200, { all: 'ok'});
      });
    } else {
      res.json(400, { all: 'Bin not found'});
    }
  });
};

admin.flagUser = function flagUser (req, res) {
  var username = req.body.username;

  // check if the user exist
  store.getUser(username, function(a, user) {
    if (user) {
      // update flagged status
      req.app.store.updateOwnershipData([username], {flagged: true}, function (err) {
        if (err) {
          console.log(err.stack);
          res.send(400, err);
        }
        res.json(200, { all: 'ok'});
      });
    } else {
      res.json(400, { all: 'User not found'});
    }
  });

};
<|endoftext|>


<commit_before>var mime = require('mime'),
    fs = require('fs'),
    gm = require('gm'),
    lockFile    = require('lockfile');

var imageModifier = {
    modifyImage: function (processOptions, options, appConfig){ //callback) {
        //console.log("Modifying image...");
        return new Promise(function(resolve, reject){
            var srcPath = processOptions.filepath;
            var dstPath = processOptions.modifiedFilePath;

            var lockFileOptions = {
                wait: 1000,
                stale: 10000,
                retries: 3,
                retryWait: 250
            };

            var fileType = mime.lookup(srcPath);
            if(appConfig.allowedMimeTypes.indexOf(fileType) > -1){
                try {
                    //console.log("Trying to identify image...");
                    gm(srcPath).identify(function (err, imageData) {
                        if (err || !(imageData && imageData.size && imageData.size.width)) {
                            //console.log("--Couldn't get image information", err);
                            reject(err);
                            return;
                        }

                        var origWidth  = imageData.size.width;
                        var origHeight = imageData.size.height;
                        //console.log("Setting up with these options: ", options);
                        //if we have no width or height defines, just output the full size image.
                        if(!options.width && !options.height){
                            options.width = origWidth;
                            options.height = origHeight;
                        }else if(!options.width){
                            //no width specified, calculate from height
                            options.width = imageModifier.ratioCalculator(options.height, origHeight, origWidth);
                        }else if(!options.height){
                            //no height specified, calculate from width
                            options.height = imageModifier.ratioCalculator(options.width, origWidth, origHeight);
                        }
                        //console.log("options are now: ", options);
                        var Canvas = require('canvas'),
                            SmartCrop = require('smartcrop');

                        var img = new Canvas.Image();
                        var canvasOptions = {}; canvasOptions.canvasFactory = function(w, h) { return new Canvas(w, h); };

                        img.src = fs.readFileSync(srcPath);

                        canvasOptions.width = options.width;
                        canvasOptions.height = options.height;
                        if(options.minScale){
                            canvasOptions.minScale = options.minScale;
                        }
                        canvasOptions.dstPath = dstPath;
                        //console.log("Using smartCrop with ", canvasOptions);
                        SmartCrop.crop(img, canvasOptions, function(result) {
                            //console.log(JSON.stringify(result.topCrop, null, '  '));
                            var rt = result.topCrop;

                            lockFile.lock(srcPath + ".lock", lockFileOptions, function(err) {
                                if (err) {
                                    //console.log(err);
                                    return reject(err);
                                }
                                //console.log("Resizing and writing to" + dstPath + "...");
                                //console.log("Cropping to", rt.width, rt.height, rt.x, rt.y);
                                //console.log("Resizing to", options.width, options.height);
                                //console.log("Source path: "+srcPath);
                                gm(srcPath)
                                    .crop(rt.width, rt.height, rt.x, rt.y)
                                    .resize(options.width, options.height, "!")
                                    .gravity('Center')
                                    .quality(options.quality)
                                    .stream(function(err, stdout, stderr){
                                        //console.log("--unlocking file");
                                        lockFile.unlock(srcPath + ".lock", function (unlockErr) {
                                            if (unlockErr) {
                                                //console.log(unlockErr);
                                                reject(unlockErr);
                                            } else {
                                                if (!err) {
                                                    //console.log("--Writing file stream to "+dstPath);
                                                    var writeStream = fs.createWriteStream(dstPath);

                                                    stdout.pipe(writeStream);
                                                    writeStream.on('close', function () {
                                                        resolve(dstPath);
                                                    });
                                                } else {
                                                    //console.log("gm error", err);
                                                    reject(err);
                                                }
                                            }
                                        });
                                    });
                            });
                        });
                    });
                }catch(err){
                    console.log("gm error", err);
                    reject(err);
                }
            }else{
                var err = {
                    responseCode: 403,
                    message: "Incorrect Mime Type!"
                };
                console.log(err, fileType);
                reject(err);
            }
        });
    },

    modifyImageProcess: function(processOptions, options, appConfig){
        return new Promise(function(resolve, reject){
            var spawn = require('child_process').spawn;
            var resolved = false;
            var env = {
                processOptions: JSON.stringify(processOptions),
                options: JSON.stringify(options),
                appConfig: JSON.stringify(appConfig)
            };

            var child = spawn('node',
                [
                    __dirname+'/image-modifier.js',
                    'processOptions', env.processOptions,
                    'options', env.options,
                    'appConfig', env.appConfig
                ]
            );

            // Listen for stdout data
            child.stdout.on('data', function (data) {
                console.log("-------------"+data);
                var out = (""+data).replace(/\n$/, '');
                try {
                    out = JSON.parse(out);
                }catch(err){}

                if(out.dstPath){
                    resolved = true;
                    resolve(out.dstPath);
                }else{
                    console.log(out);
                    resolved = true;
                    reject(out);
                }
            });

            // Listen for an exit event:
            child.on('exit', function (exitCode) {
                if(!resolved){
                    console.log("--Not resolved, aborting child");
                    reject(exitCode);
                }
                console.log("Child exited with code: " + exitCode);
            });
        });
    },

    /**
     * Detect targetHeight for a proportional resizing, when only width is indicated.
     *
     * @param targetWidth
     * @param origWidth
     * @param origHeight
     */
    ratioCalculator: function (targetKnown, origTargetKnownEquiv, origTargetUnknownEquiv) {
        return origTargetUnknownEquiv * targetKnown / origTargetKnownEquiv;
    }
};

//Check to see if we've been executed as a process
var runOptions = {};
for(var i = 0; i < process.argv.length; i++){
    if(['processOptions', 'options', 'appConfig'].indexOf(process.argv[i]) > -1){
        runOptions[process.argv[i]] = process.argv[i+1];
    }
}
if(runOptions.processOptions){
    processOptions = JSON.parse(runOptions.processOptions);
    options = JSON.parse(runOptions.options);
    appConfig = JSON.parse(runOptions.appConfig);

    imageModifier.modifyImage(processOptions, options, appConfig).then(function(dstPath){
        console.log(JSON.stringify({ dstPath: dstPath }));

    }).catch(function(err){
        console.log(JSON.stringify({
            responseCode: 404,
            message: err
        }));
    });
}

module.exports = imageModifier;

<commit_msg>Adjusted child spawn code because of deprecation.<commit_after>var mime = require('mime'),
    fs = require('fs'),
    gm = require('gm'),
    lockFile    = require('lockfile');

var imageModifier = {
    modifyImage: function (processOptions, options, appConfig){ //callback) {
        //console.log("Modifying image...");
        return new Promise(function(resolve, reject){
            var srcPath = processOptions.filepath;
            var dstPath = processOptions.modifiedFilePath;

            var lockFileOptions = {
                wait: 1000,
                stale: 10000,
                retries: 3,
                retryWait: 250
            };

            var fileType = mime.lookup(srcPath);
            if(appConfig.allowedMimeTypes.indexOf(fileType) > -1){
                try {
                    //console.log("Trying to identify image...");
                    gm(srcPath).identify(function (err, imageData) {
                        if (err || !(imageData && imageData.size && imageData.size.width)) {
                            //console.log("--Couldn't get image information", err);
                            reject(err);
                            return;
                        }

                        var origWidth  = imageData.size.width;
                        var origHeight = imageData.size.height;
                        //console.log("Setting up with these options: ", options);
                        //if we have no width or height defines, just output the full size image.
                        if(!options.width && !options.height){
                            options.width = origWidth;
                            options.height = origHeight;
                        }else if(!options.width){
                            //no width specified, calculate from height
                            options.width = imageModifier.ratioCalculator(options.height, origHeight, origWidth);
                        }else if(!options.height){
                            //no height specified, calculate from width
                            options.height = imageModifier.ratioCalculator(options.width, origWidth, origHeight);
                        }
                        //console.log("options are now: ", options);
                        var Canvas = require('canvas'),
                            SmartCrop = require('smartcrop');

                        var img = new Canvas.Image();
                        var canvasOptions = {}; canvasOptions.canvasFactory = function(w, h) { return new Canvas(w, h); };

                        img.src = fs.readFileSync(srcPath);

                        canvasOptions.width = options.width;
                        canvasOptions.height = options.height;
                        if(options.minScale){
                            canvasOptions.minScale = options.minScale;
                        }
                        canvasOptions.dstPath = dstPath;
                        //console.log("Using smartCrop with ", canvasOptions);
                        SmartCrop.crop(img, canvasOptions, function(result) {
                            //console.log(JSON.stringify(result.topCrop, null, '  '));
                            var rt = result.topCrop;

                            lockFile.lock(srcPath + ".lock", lockFileOptions, function(err) {
                                if (err) {
                                    //console.log(err);
                                    return reject(err);
                                }
                                //console.log("Resizing and writing to" + dstPath + "...");
                                //console.log("Cropping to", rt.width, rt.height, rt.x, rt.y);
                                //console.log("Resizing to", options.width, options.height);
                                //console.log("Source path: "+srcPath);
                                gm(srcPath)
                                    .crop(rt.width, rt.height, rt.x, rt.y)
                                    .resize(options.width, options.height, "!")
                                    .gravity('Center')
                                    .quality(options.quality)
                                    .stream(function(err, stdout, stderr){
                                        //console.log("--unlocking file");
                                        lockFile.unlock(srcPath + ".lock", function (unlockErr) {
                                            if (unlockErr) {
                                                //console.log(unlockErr);
                                                reject(unlockErr);
                                            } else {
                                                if (!err) {
                                                    //console.log("--Writing file stream to "+dstPath);
                                                    var writeStream = fs.createWriteStream(dstPath);

                                                    stdout.pipe(writeStream);
                                                    writeStream.on('close', function () {
                                                        resolve(dstPath);
                                                    });
                                                } else {
                                                    //console.log("gm error", err);
                                                    reject(err);
                                                }
                                            }
                                        });
                                    });
                            });
                        });
                    });
                }catch(err){
                    console.log("gm error", err);
                    reject(err);
                }
            }else{
                var err = {
                    responseCode: 403,
                    message: "Incorrect Mime Type!"
                };
                console.log(err, fileType);
                reject(err);
            }
        });
    },

    modifyImageProcess: function(processOptions, options, appConfig){
        return new Promise(function(resolve, reject){
            var spawn = require('child_process').spawn;
            var resolved = false;
            var env = {
                processOptions: JSON.stringify(processOptions),
                options: JSON.stringify(options),
                appConfig: JSON.stringify(appConfig)
            };

            var child = spawn('node',
                [
                    __dirname+'/image-modifier.js',
                    'processOptions', env.processOptions,
                    'options', env.options,
                    'appConfig', env.appConfig
                ],
                {
                    stdio: 'pipe'
                }
            );

            // Listen for stdout data
            child.stdout.on('data', function (data) {
                console.log("-------------"+data);
                var out = (""+data).replace(/\n$/, '');
                try {
                    out = JSON.parse(out);
                }catch(err){}

                if(out.dstPath){
                    resolved = true;
                    resolve(out.dstPath);
                }else{
                    console.log(out);
                    resolved = true;
                    reject(out);
                }
            });

            // Listen for an exit event:
            child.on('error', function (exitCode) {
                if(!resolved){
                    console.log("--Not resolved, aborting child");
                    reject(exitCode);
                }
                console.log("Child exited with code: " + exitCode);
            });

            // Listen for an exit event:
            child.on('exit', function (exitCode) {
                if(!resolved){
                    console.log("--Not resolved, aborting child");
                    reject(exitCode);
                }
                console.log("Child exited with code: " + exitCode);
            });
        });
    },

    /**
     * Detect targetHeight for a proportional resizing, when only width is indicated.
     *
     * @param targetWidth
     * @param origWidth
     * @param origHeight
     */
    ratioCalculator: function (targetKnown, origTargetKnownEquiv, origTargetUnknownEquiv) {
        return origTargetUnknownEquiv * targetKnown / origTargetKnownEquiv;
    }
};

//Check to see if we've been executed as a process
var runOptions = {};
for(var i = 0; i < process.argv.length; i++){
    if(['processOptions', 'options', 'appConfig'].indexOf(process.argv[i]) > -1){
        runOptions[process.argv[i]] = process.argv[i+1];
    }
}
if(runOptions.processOptions){
    processOptions = JSON.parse(runOptions.processOptions);
    options = JSON.parse(runOptions.options);
    appConfig = JSON.parse(runOptions.appConfig);

    imageModifier.modifyImage(processOptions, options, appConfig).then(function(dstPath){
        console.log(JSON.stringify({ dstPath: dstPath }));

    }).catch(function(err){
        console.log(JSON.stringify({
            responseCode: 404,
            message: err
        }));
    });
}

module.exports = imageModifier;

<|endoftext|>


<commit_before>//Render a simple vertical check box group, where the buttons all have the same sizes
//TODO: not ready for use in simulations, it will need further development & discussion first.
//TODO: Abstract out common functionality between this and VerticalCheckBoxGroup
define( function( require ) {
  "use strict";

  var Path = require( 'SCENERY/nodes/Path' );
  var RadioButton = require( 'SUN/RadioButton' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var Shape = require( 'KITE/Shape' );
  var inherit = require( 'PHET_CORE/inherit' );

  /**
   * Main constructor.
   *
   * @param items  an array of {content, property}
   * @param options
   * @constructor
   */
  function VerticalCheckBoxGroup( items, options ) {
    options = _.extend( { spacing: 3 }, options );
    var padding = options.padding ? options.padding : 8;

    var width = 0;
    for ( var i = 0; i < items.length; i++ ) {
      width = Math.max( width, items[i].node.width );
      console.log( items[i].node.width );
    }

    var children = [];
    for ( i = 0; i < items.length; i++ ) {

      //Add an invisible strut to each content to make the widths match
      var content = new Path( {shape: Shape.rect( 0, 0, width + padding, 0 ), children: [items[i].node], renderer: 'svg'} );
      children.push( new RadioButton( items[i].property, items[i].value, content, {radius: 12} ) );//Made the radius smaller here so the whole panel won't take up too much vertical space
    }

    options.children = children;
    options.renderer = 'svg';
    VBox.call( this, options );
  }

  inherit( VBox, VerticalCheckBoxGroup );

  return VerticalCheckBoxGroup;
} );<commit_msg>removed console.log<commit_after>//Render a simple vertical check box group, where the buttons all have the same sizes
//TODO: not ready for use in simulations, it will need further development & discussion first.
//TODO: Abstract out common functionality between this and VerticalCheckBoxGroup
define( function( require ) {
  "use strict";

  var Path = require( 'SCENERY/nodes/Path' );
  var RadioButton = require( 'SUN/RadioButton' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var Shape = require( 'KITE/Shape' );
  var inherit = require( 'PHET_CORE/inherit' );

  /**
   * Main constructor.
   *
   * @param items  an array of {content, property}
   * @param options
   * @constructor
   */
  function VerticalCheckBoxGroup( items, options ) {
    options = _.extend( { spacing: 3 }, options );
    var padding = options.padding ? options.padding : 8;

    var width = 0;
    for ( var i = 0; i < items.length; i++ ) {
      width = Math.max( width, items[i].node.width );
    }

    var children = [];
    for ( i = 0; i < items.length; i++ ) {

      //Add an invisible strut to each content to make the widths match
      var content = new Path( {shape: Shape.rect( 0, 0, width + padding, 0 ), children: [items[i].node], renderer: 'svg'} );
      children.push( new RadioButton( items[i].property, items[i].value, content, {radius: 12} ) );//Made the radius smaller here so the whole panel won't take up too much vertical space
    }

    options.children = children;
    options.renderer = 'svg';
    VBox.call( this, options );
  }

  inherit( VBox, VerticalCheckBoxGroup );

  return VerticalCheckBoxGroup;
} );<|endoftext|>