<commit_before>/*!
 * chain-tiny
 * Copyright(c) 2011 Kazuhito Hokamura
 * MIT Licensed
 */

/**
 * Constructor. Initialize Chain.
 *
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
var Chain = function(fn) {
  if (this instanceof Chain) {
    this.stack = [];
    return this.chain(fn);
  }
  else {
    return new Chain(fn);
  }
};

/**
 * Module version
 */
Chain.version = '0.2.0';

/**
 * Expose
 */
module.exports = Chain;

/**
 * Function push to stack.
 *
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
Chain.prototype.chain = function(fn) {
  var self = this;

  if (typeof fn === 'function') {
    self.stack.push(fn);
  }

  return self;
};

/**
 * Execute all functions, in the pushed order.
 *
 * @param {Function} fn
 * @api public
 */
Chain.prototype.end = function(fn) {
  var self = this;

  (function next() {
    var args = Array.prototype.slice.call(arguments);
    var err = args.shift() || null;
    if (err || !self.stack.length) {
      if (typeof fn === 'function') {
        args.unshift(err);
        fn.apply(null, args);
      }
    }
    else {
      args.push(next);
      self.stack.shift().apply(null, args);
    }
  })();
};

/**
 *
 * Iterator function to each item in an object (plain hash). Array recieved before next function args.
 *
 * @param {Object} obj
 * @return {Object} chain
 * @api public
 */
Chain.prototype.each = function(fn) {
  var self = this;

  return self.chain(function(obj, next) {
    var _chain = Chain();
    var isArray = Array.isArray(obj);
    var results = isArray ? [] : {};
    Object.keys(obj).forEach(function(key) {
      var val = obj[key];
      _chain.chain(function(_next) {
        fn.call(null, key, val, function(err, result) {
          isArray ? results.push(result) : results[key] = result;
          _next(err);
        });
      });
    });
    _chain.end(function(err) {
      next(err, results);
    });
  });
};

/**
 *
 * Iterator function to each item in an object (plain hash) parallel. Array recieved before next function args.
 *
 * @param {Object} obj
 * @return {Object} chain
 * @api public
 */
Chain.prototype.eachParallel = function(fn) {
  var self = this;

  return self.chain(function(obj, next) {
    var _chain = Chain();
    var isArray = Array.isArray(obj);
    var q = isArray ? [] : {};
    Object.keys(obj).forEach(function(key) {
      var val = obj[key];
      var _fn = function(_next) {
        fn.call(null, key, val, function(err, result) {
          _next(err, result);
        });
      };
      isArray ? q.push(_fn) : q[key] = _fn;
    });

    Chain.parallel(q).end(next);
  });
};

/**
 * Parallel exec functions.
 *
 * @param {Object} obj
 * @return {Object} chain
 * @api public
 */
Chain.prototype.parallel = function(obj) {
  var self = this;

  return self.chain(function() {
    var args = Array.prototype.slice.call(arguments);
    var next = args.pop();
    var count = 0;
    var isArray = Array.isArray(obj);
    var results = isArray ? [] : {};
    var keys = Object.keys(obj);
    keys.forEach(function(key) {
      var fn = obj[key];
      process.nextTick(function() {
        fn.apply(null, args.concat(function _next(err, result) {
          if (err) {
            next(err);
          }
          else {
            results[key] = result;
            count++;
            if (count === keys.length) {
              next(null, results);
            }
          }
        }));
      });
    });
  });
};

/**
 * Wait next process during the `time` (ms).
 *
 * @param {time} Int
 * @return {Object} chain
 * @api public
 */
Chain.prototype.wait = function(time) {
  var self = this;

  return self.chain(function() {
    var args = Array.prototype.slice.call(arguments);
    var next = args.pop();
    args.unshift(null);
    setTimeout(function() {
      next.apply(null, args);
    }, time);
  });
};

/**
 * Static method for `.each()`.
 *
 * @param {Object} Object
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
Chain.each = function(obj, fn) {
  return Chain(function(next) {
    next(null, obj);
  }).each(fn);
};

/**
 * Static method for `.eachParallel()`.
 *
 * @param {Object} Object
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
Chain.eachParallel = function(obj, fn) {
  return Chain(function(next) {
    next(null, obj);
  }).eachParallel(fn);
};

/**
 * Static method for `.parallel()`.
 *
 * @param {time} Int
 * @return {Object} chain
 * @api public
 */
Chain.parallel = function(ary) {
  return Chain().parallel(ary);
};

/**
 * Static method for `.wait()`.
 *
 * @param {time} Int
 * @return {Object} chain
 * @api public
 */
Chain.wait = function(time) {
  return Chain(function(next) { next(null); }).wait(time);
};
<commit_msg>Fix `Chain.wait()`<commit_after>/*!
 * chain-tiny
 * Copyright(c) 2011 Kazuhito Hokamura
 * MIT Licensed
 */

/**
 * Constructor. Initialize Chain.
 *
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
var Chain = function(fn) {
  if (this instanceof Chain) {
    this.stack = [];
    return this.chain(fn);
  }
  else {
    return new Chain(fn);
  }
};

/**
 * Module version
 */
Chain.version = '0.2.0';

/**
 * Expose
 */
module.exports = Chain;

/**
 * Function push to stack.
 *
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
Chain.prototype.chain = function(fn) {
  var self = this;

  if (typeof fn === 'function') {
    self.stack.push(fn);
  }

  return self;
};

/**
 * Execute all functions, in the pushed order.
 *
 * @param {Function} fn
 * @api public
 */
Chain.prototype.end = function(fn) {
  var self = this;

  (function next() {
    var args = Array.prototype.slice.call(arguments);
    var err = args.shift() || null;
    if (err || !self.stack.length) {
      if (typeof fn === 'function') {
        args.unshift(err);
        fn.apply(null, args);
      }
    }
    else {
      args.push(next);
      self.stack.shift().apply(null, args);
    }
  })();
};

/**
 *
 * Iterator function to each item in an object (plain hash). Array recieved before next function args.
 *
 * @param {Object} obj
 * @return {Object} chain
 * @api public
 */
Chain.prototype.each = function(fn) {
  var self = this;

  return self.chain(function(obj, next) {
    var _chain = Chain();
    var isArray = Array.isArray(obj);
    var results = isArray ? [] : {};
    Object.keys(obj).forEach(function(key) {
      var val = obj[key];
      _chain.chain(function(_next) {
        fn.call(null, key, val, function(err, result) {
          isArray ? results.push(result) : results[key] = result;
          _next(err);
        });
      });
    });
    _chain.end(function(err) {
      next(err, results);
    });
  });
};

/**
 *
 * Iterator function to each item in an object (plain hash) parallel. Array recieved before next function args.
 *
 * @param {Object} obj
 * @return {Object} chain
 * @api public
 */
Chain.prototype.eachParallel = function(fn) {
  var self = this;

  return self.chain(function(obj, next) {
    var _chain = Chain();
    var isArray = Array.isArray(obj);
    var q = isArray ? [] : {};
    Object.keys(obj).forEach(function(key) {
      var val = obj[key];
      var _fn = function(_next) {
        fn.call(null, key, val, function(err, result) {
          _next(err, result);
        });
      };
      isArray ? q.push(_fn) : q[key] = _fn;
    });

    Chain.parallel(q).end(next);
  });
};

/**
 * Parallel exec functions.
 *
 * @param {Object} obj
 * @return {Object} chain
 * @api public
 */
Chain.prototype.parallel = function(obj) {
  var self = this;

  return self.chain(function() {
    var args = Array.prototype.slice.call(arguments);
    var next = args.pop();
    var count = 0;
    var isArray = Array.isArray(obj);
    var results = isArray ? [] : {};
    var keys = Object.keys(obj);
    keys.forEach(function(key) {
      var fn = obj[key];
      process.nextTick(function() {
        fn.apply(null, args.concat(function _next(err, result) {
          if (err) {
            next(err);
          }
          else {
            results[key] = result;
            count++;
            if (count === keys.length) {
              next(null, results);
            }
          }
        }));
      });
    });
  });
};

/**
 * Wait next process during the `time` (ms).
 *
 * @param {time} Int
 * @return {Object} chain
 * @api public
 */
Chain.prototype.wait = function(time) {
  var self = this;

  return self.chain(function() {
    var args = Array.prototype.slice.call(arguments);
    var next = args.pop();
    args.unshift(null);
    setTimeout(function() {
      next.apply(null, args);
    }, time);
  });
};

/**
 * Static method for `.each()`.
 *
 * @param {Object} Object
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
Chain.each = function(obj, fn) {
  return Chain(function(next) {
    next(null, obj);
  }).each(fn);
};

/**
 * Static method for `.eachParallel()`.
 *
 * @param {Object} Object
 * @param {Function} fn
 * @return {Object} chain
 * @api public
 */
Chain.eachParallel = function(obj, fn) {
  return Chain(function(next) {
    next(null, obj);
  }).eachParallel(fn);
};

/**
 * Static method for `.parallel()`.
 *
 * @param {time} Int
 * @return {Object} chain
 * @api public
 */
Chain.parallel = function(ary) {
  return Chain().parallel(ary);
};

/**
 * Static method for `.wait()`.
 *
 * @param {time} Int
 * @return {Object} chain
 * @api public
 */
Chain.wait = function(time) {
  return Chain().wait(time);
};
<|endoftext|>


<commit_before>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import runServer from './runServer';
import fs from './lib/fs';
import fetch from 'node-fetch';
import { host } from '../src/config';

// Enter your paths here which you want to render as static
const routes = [
  '/',
  '/contact',
  '/login',
  '/register',
  '/about',
  '/privacy',
  '/404', // https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/
];

async function render() {
  let server;
  await new Promise(resolve => (server = runServer(resolve)));

  await routes.reduce((promise, route) => promise.then(async () => {
    const url = `http://${host}${route}`;
    const url = `http://localhost${route}`;

    const dir = `build/public${route.replace(/[^\/]*$/, '')}`;
    const name = route.endsWith('/') ? 'index.html' : `${route.match(/[^/]+$/)[0]}.html`;
    const dist = `${dir}${name}`;
    const res = await fetch(url);
    const text = await res.text();
    await fs.makeDir(dir);
    await fs.writeFile(dist, text);
    console.log(`${dist} => ${res.status} ${res.statusText}`);
  }), Promise.resolve());

  server.kill('SIGTERM');
}

export default render;
<commit_msg>Remove duplicate url const<commit_after>/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

import runServer from './runServer';
import fs from './lib/fs';
import fetch from 'node-fetch';
import { host } from '../src/config';

// Enter your paths here which you want to render as static
const routes = [
  '/',
  '/contact',
  '/login',
  '/register',
  '/about',
  '/privacy',
  '/404', // https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/
];

async function render() {
  let server;
  await new Promise(resolve => (server = runServer(resolve)));

  await routes.reduce((promise, route) => promise.then(async () => {
    const url = `http://${host}${route}`;

    const dir = `build/public${route.replace(/[^\/]*$/, '')}`;
    const name = route.endsWith('/') ? 'index.html' : `${route.match(/[^/]+$/)[0]}.html`;
    const dist = `${dir}${name}`;
    const res = await fetch(url);
    const text = await res.text();
    await fs.makeDir(dir);
    await fs.writeFile(dist, text);
    console.log(`${dist} => ${res.status} ${res.statusText}`);
  }), Promise.resolve());

  server.kill('SIGTERM');
}

export default render;
<|endoftext|>


<commit_before>import {
  action,
  observable,
  runInAction,
  isObservable,
} from 'mobx'
import { app } from '@mindhive/di'
import { meteorTracker } from './tracker'

// Originally based on https://github.com/meteor-space/tracker-mobx-autorun 0.2.0

const observableName = ({ observableArray, observableMap }) => {
  const names = []
  if (observableArray) {
    names.push(observableArray.$mobx.atom.name)
  }
  if (observableMap) {
    names.push(observableMap.name)
  }
  return names.join('+')
}

const groundCollectionName = groundCollection =>
  groundCollection.storage._config.name

export const checkFindOptions = ({
  findOptions,
  observableArray,
}) => {
  const { Meteor } = app()
  if (Meteor.isDevelopment) {
    if (findOptions && findOptions.sort && ! observableArray) {
      console.warn('sort findOptions does not make sense when not observing an array')  // eslint-disable-line no-console
    }
  }
}

const isShallowReferenceEnhancer = enhancer => ! isObservable(enhancer({}))

export const checkObservableModes = ({
  observableArray,
  observableMap,
}) => {
  const { Meteor } = app()
  if (Meteor.isDevelopment) {
    if (observableArray && ! isShallowReferenceEnhancer(observableArray.$mobx.enhancer)) {
      console.warn('observableArray does not appear to be shallow, ' +  // eslint-disable-line no-console
        'declare as: @observable.shallow array = []')
    }
    if (observableMap && ! isShallowReferenceEnhancer(observableMap.enhancer)) {
      console.warn('observableMap does not appear to be shallow, ' +  // eslint-disable-line no-console
        'declare as: @observable.shallow map = new Map()')
    }
  }
}

let uniqueNameCounter = 0

const generateUniqueId = () => {
  uniqueNameCounter += 1
  return uniqueNameCounter
}

class SubscriptionHandle {

  stopped = false

  @observable loading = true

  _ready(disposer) {
    this.disposer = disposer
    this.loading = false
  }

  stop() {
    this.stopped = true
    if (this.disposer) {
      this.disposer.stop()
      this.disposer = null
    }
  }
}

export class MongoMirror {

  // Automatically pump data from a Mongo cursor to a Mobx array or map
  // See subscriptionToObservable for something more high level
  cursorToObservable({
    context = `cursorToObservable@${generateUniqueId()}`,
    mongoCursor,
    observableArray,  // Should be declared as observable.shallow
    observableMap,  // Should be declared as observable.shallow
  }) {
    checkObservableModes({ observableArray, observableMap })
    meteorTracker.nonreactive(() => {
      runInAction(`${context}: initial fetch`, () => {
        const initialDocs = mongoCursor.fetch()
        if (observableArray) {
          observableArray.replace(initialDocs)
        }
        if (observableMap) {
          observableMap.clear()
          initialDocs.forEach((doc) => {
            observableMap.set(doc._id, doc)
          })
        }
      })
    })
    if (observableArray) {
      return mongoCursor.observe({
        _suppress_initial: true,  // suppresses addedAt for documents initially fetched above
        addedAt: action(`${context}: document added`, (doc, index) => {
          observableArray.splice(index, 0, doc)
          if (observableMap) {
            observableMap.set(doc._id, doc)
          }
        }),
        changedAt: action(`${context}: document changed`, (doc, oldDoc, index) => {
          // REVISIT: if existing value isObservable we could be more efficient here and just assign top level fields
          // Probably using extendObservable but also need to handle fields disappearing
          // Or use observeChanges instead so we just get fields
          observableArray[index] = doc
          if (observableMap) {
            observableMap.set(doc._id, doc)
          }
        }),
        removedAt: action(`${context}: document removed`, (doc, index) => {
          observableArray.splice(index, 1)
          if (observableMap) {
            observableMap.delete(doc._id)
          }
        }),
        movedTo: action(`${context}: document moved`, (doc, fromIndex, toIndex) => {
          observableArray.splice(fromIndex, 1)
          observableArray.splice(toIndex, 0, doc)
        }),
      })
    } else if (observableMap) {
      return mongoCursor.observe({
        _suppress_initial: true,  // suppresses added for documents initially fetched above
        added: action(`${context}: document added`, (doc) => {
          observableMap.set(doc._id, doc)
        }),
        removed: action(`${context}: document removed`, (oldDoc) => {
          observableMap.delete(oldDoc._id)
        }),
        changed: action(`${context}: document changed`, (doc) => {
          observableMap.set(doc._id, doc)
        }),
      })
    }
    throw new Error('Neither observableArray, not observableMap')
  }

  subscriptionToLocal({
    publicationName,
    subscriptionArgs,
    context = `subscription:${publicationName}`,
    onReady = null,  // Can optionally return a disposer with a stop function
  }) {
    const subscriptionHandle = new SubscriptionHandle(context)
    Meteor.subscribe(publicationName, subscriptionArgs, () => {
      if (subscriptionHandle.stopped) {
        return
      }
      let disposer = null
      if (onReady) {
        disposer = onReady()
      }
      runInAction(`${context}: ready`, () => {
        subscriptionHandle._ready(disposer)
      })
    })
    return subscriptionHandle
  }

  // What it says on the can, returns handle with stop() (from autorun) and observable property loading
  subscriptionToObservable({
    publicationName,
    subscriptionArgs,
    collection,
    findSelector = {},
    findOptions = {},
    observableArray,
    observableMap,
    context = `subscription:${publicationName}->${observableName({ observableArray, observableMap })}`,
  }) {
    checkFindOptions({ findOptions, observableArray })
    return this.subscriptionToLocal({
      publicationName,
      subscriptionArgs,
      context,
      onReady: () => this.cursorToObservable({
        context,
        observableArray,
        observableMap,
        mongoCursor: collection.find(findSelector, findOptions),
      }),
    })
  }

  subscriptionToOffline({
    publicationName,
    subscriptionArgs,
    collection,
    findSelector = {},
    findOptions = {},
    groundCollection,
    context = `subscription:${publicationName}->${groundCollectionName(groundCollection)}(offline)`,
  }) {
    // REVISIT: this could be more efficient and not go through minimongo
    return this.subscriptionToLocal({
      publicationName,
      subscriptionArgs,
      context,
      onReady: () => {
        const cursor = collection.find(findSelector, findOptions)
        groundCollection.keep(cursor)
        return groundCollection.observeSource(cursor)
      },
    })
  }

  offlineToObservable({
    groundCollection,
    observableArray,
    observableMap,
    context = `offline:${groundCollectionName(groundCollection)}->${
      observableName({ observableArray, observableMap })}`,
  }) {
    return this.cursorToObservable({
      context,
      observableArray,
      observableMap,
      mongoCursor: groundCollection.find(),
    })
  }

  subscriptionToObservableCachedOffline({
    publicationName,
    subscriptionArgs,
    collection,
    findSelector = {},
    findOptions = {},
    groundCollection,
    observableArray,
    observableMap,
  }) {
    // REVISIT: this will have 3 copies in memory (groundCollection._collection, subscription collection, observable(s))
    checkFindOptions({ findOptions, observableArray })
    this.offlineToObservable({
      groundCollection,
      observableArray,
      observableMap,
    })
    return this.subscriptionToOffline({
      publicationName,
      subscriptionArgs,
      collection,
      findSelector,
      findOptions,
      groundCollection,
    })
  }
}
<commit_msg>Speed up mongoMirror when array not ordered<commit_after>import {
  action,
  observable,
  runInAction,
  isObservable,
} from 'mobx'
import { app } from '@mindhive/di'
import { meteorTracker } from './tracker'

// Originally based on https://github.com/meteor-space/tracker-mobx-autorun 0.2.0

const observableName = ({ observableArray, observableMap }) => {
  const names = []
  if (observableArray) {
    names.push(observableArray.$mobx.atom.name)
  }
  if (observableMap) {
    names.push(observableMap.name)
  }
  return names.join('+')
}

const groundCollectionName = groundCollection =>
  groundCollection.storage._config.name

export const checkFindOptions = ({
  findOptions,
  observableArray,
}) => {
  const { Meteor } = app()
  if (Meteor.isDevelopment) {
    if (findOptions && findOptions.sort && ! observableArray) {
      console.warn('sort findOptions does not make sense when not observing an array')  // eslint-disable-line no-console
    }
  }
}

const isShallowReferenceEnhancer = enhancer => ! isObservable(enhancer({}))

export const checkObservableModes = ({
  observableArray,
  observableMap,
}) => {
  const { Meteor } = app()
  if (Meteor.isDevelopment) {
    if (observableArray && ! isShallowReferenceEnhancer(observableArray.$mobx.enhancer)) {
      console.warn('observableArray does not appear to be shallow, ' +  // eslint-disable-line no-console
        'declare as: @observable.shallow array = []')
    }
    if (observableMap && ! isShallowReferenceEnhancer(observableMap.enhancer)) {
      console.warn('observableMap does not appear to be shallow, ' +  // eslint-disable-line no-console
        'declare as: @observable.shallow map = new Map()')
    }
  }
}

let uniqueNameCounter = 0

const generateUniqueId = () => {
  uniqueNameCounter += 1
  return uniqueNameCounter
}

class SubscriptionHandle {

  stopped = false

  @observable loading = true

  _ready(disposer) {
    this.disposer = disposer
    this.loading = false
  }

  stop() {
    this.stopped = true
    if (this.disposer) {
      this.disposer.stop()
      this.disposer = null
    }
  }
}

export class MongoMirror {

  // Automatically pump data from a Mongo cursor to a Mobx array or map
  // See subscriptionToObservable for something more high level
  cursorToObservable({
    context = `cursorToObservable@${generateUniqueId()}`,
    mongoCursor,
    observableArray,  // Should be declared as observable.shallow
    observableMap,  // Should be declared as observable.shallow
    arrayIsOrdered = true,
  }) {
    checkObservableModes({ observableArray, observableMap })
    meteorTracker.nonreactive(() => {
      runInAction(`${context}: initial fetch`, () => {
        const initialDocs = mongoCursor.fetch()
        if (observableArray) {
          observableArray.replace(initialDocs)
        }
        if (observableMap) {
          observableMap.clear()
          initialDocs.forEach((doc) => {
            observableMap.set(doc._id, doc)
          })
        }
      })
    })
    if (observableArray && arrayIsOrdered) {
      return mongoCursor.observe({
        _suppress_initial: true,  // suppresses addedAt for documents initially fetched above
        addedAt: action(`${context}: document added`, (doc, index) => {
          observableArray.splice(index, 0, doc)
          if (observableMap) {
            observableMap.set(doc._id, doc)
          }
        }),
        changedAt: action(`${context}: document changed`, (doc, oldDoc, index) => {
          // REVISIT: if existing value isObservable we could be more efficient here and just assign top level fields
          // Probably using extendObservable but also need to handle fields disappearing
          // Or use observeChanges instead so we just get fields
          observableArray[index] = doc
          if (observableMap) {
            observableMap.set(doc._id, doc)
          }
        }),
        removedAt: action(`${context}: document removed`, (doc, index) => {
          observableArray.splice(index, 1)
          if (observableMap) {
            observableMap.delete(doc._id)
          }
        }),
        movedTo: action(`${context}: document moved`, (doc, fromIndex, toIndex) => {
          observableArray.splice(fromIndex, 1)
          observableArray.splice(toIndex, 0, doc)
        }),
      })
    }
    // This style of observe has better performance
    return mongoCursor.observe({
      _suppress_initial: true,  // suppresses added for documents initially fetched above
      added: action(`${context}: document added`, (doc) => {
        if (observableMap) {
          observableMap.set(doc._id, doc)
        }
        if (observableArray) {
          observableArray.push(doc)
        }
      }),
      removed: action(`${context}: document removed`, (oldDoc) => {
        if (observableMap) {
          observableMap.delete(oldDoc._id)
        }
        if (observableArray) {
          // REVISIT: does this first attempt ever find anything?
          let index = observableArray.indexOf(oldDoc)
          if (index === -1) {
            index = observableArray.findIndex(d => d._id === oldDoc._id)
          }
          if (index !== -1) {
            observableArray.splice(index, 1)
          }
        }
      }),
      changed: action(`${context}: document changed`, (doc) => {
        if (observableMap) {
          observableMap.set(doc._id, doc)
        }
        if (observableArray) {
          const index = observableArray.findIndex(d => d._id === doc._id)
          if (index !== -1) {
            observableArray[index] = doc
          }
        }
      }),
    })
  }

  subscriptionToLocal({
    publicationName,
    subscriptionArgs,
    context = `subscription:${publicationName}`,
    onReady = null,  // Can optionally return a disposer with a stop function
  }) {
    const subscriptionHandle = new SubscriptionHandle(context)
    Meteor.subscribe(publicationName, subscriptionArgs, () => {
      if (subscriptionHandle.stopped) {
        return
      }
      let disposer = null
      if (onReady) {
        disposer = onReady()
      }
      runInAction(`${context}: ready`, () => {
        subscriptionHandle._ready(disposer)
      })
    })
    return subscriptionHandle
  }

  // What it says on the can, returns handle with stop() (from autorun) and observable property loading
  subscriptionToObservable({
    publicationName,
    subscriptionArgs,
    collection,
    findSelector = {},
    findOptions = {},
    observableArray,
    observableMap,
    arrayIsOrdered = typeof findOptions.sort === 'object',
    context = `subscription:${publicationName}->${observableName({ observableArray, observableMap })}`,
  }) {
    checkFindOptions({ findOptions, observableArray })
    return this.subscriptionToLocal({
      publicationName,
      subscriptionArgs,
      context,
      onReady: () => this.cursorToObservable({
        context,
        mongoCursor: collection.find(findSelector, findOptions),
        observableArray,
        observableMap,
        arrayIsOrdered,
      }),
    })
  }

  subscriptionToOffline({
    publicationName,
    subscriptionArgs,
    collection,
    findSelector = {},
    findOptions = {},
    groundCollection,
    context = `subscription:${publicationName}->${groundCollectionName(groundCollection)}(offline)`,
  }) {
    // REVISIT: this could be more efficient and not go through minimongo
    return this.subscriptionToLocal({
      publicationName,
      subscriptionArgs,
      context,
      onReady: () => {
        const cursor = collection.find(findSelector, findOptions)
        groundCollection.keep(cursor)
        return groundCollection.observeSource(cursor)
      },
    })
  }

  offlineToObservable({
    groundCollection,
    observableArray,
    observableMap,
    context = `offline:${groundCollectionName(groundCollection)}->${
      observableName({ observableArray, observableMap })}`,
  }) {
    return this.cursorToObservable({
      context,
      observableArray,
      observableMap,
      mongoCursor: groundCollection.find(),
    })
  }

  subscriptionToObservableCachedOffline({
    publicationName,
    subscriptionArgs,
    collection,
    findSelector = {},
    findOptions = {},
    groundCollection,
    observableArray,
    observableMap,
  }) {
    // REVISIT: this will have 3 copies in memory (groundCollection._collection, subscription collection, observable(s))
    checkFindOptions({ findOptions, observableArray })
    this.offlineToObservable({
      groundCollection,
      observableArray,
      observableMap,
    })
    return this.subscriptionToOffline({
      publicationName,
      subscriptionArgs,
      collection,
      findSelector,
      findOptions,
      groundCollection,
    })
  }
}
<|endoftext|>


<commit_before>'use strict';

var persistenceTimerInterval;

function isLively4Script(node) {
    return node.tagName
        && node.tagName.toLocaleLowerCase() == 'script'
        && node.type == 'lively4script'
}

function initialize(){
    resetPersistenceSessionStore();

    var observer = new MutationObserver((mutations, observer) => {
        mutations.forEach(record => {
            if (record.target.id == 'console'
                || record.target.id == 'editor') return;
            var shouldSave = false;
            if (record.type == 'childList') {
                /*var nodes = [...record.addedNodes].concat([...record.removedNodes]);
                shouldSave = nodes.some(node => {
                    return isLively4Script(node);
                });*/
                shouldSave = true;
            }
            else if (record.type == 'characterData') {
                shouldSave = true;
            }
            else if (record.type == 'attributes') {
                shouldSave = true;
            }

            if (shouldSave) {
                sessionStorage["lively.scriptMutationsDetected"] = 'true';
                if (isPersistOnIntervalActive()) {
                    restartPersistenceTimerInterval();
                } else {
                    if (isSaveDOMAllowed()) {
                        saveDOM();
                    } else {
                        console.log("Persist to github not checked. Changes will not be pushed.");
                    }
                }
            }
        })
    }).observe(document, {childList: true, subtree: true, characterData: true, attributes: true});
}

function getURL(){
    var baseurl = $('#baseurl').val() // How to abstract from UI? #TODO #JensLincke
    var filename = $('#filename').val()

    var url = document.URL;
    var r = /https:\/\/([\w-]+)\.github\.io\/([\w-]+)\/(.+)/i;
    var results = url.match(r);

    if (results)
    {
        filename = results[3];
    }
    else
    {
        r = /localhost:8080\/(.+)/i;
        results = url.match(r);
        if (results)
        {
            filename = results[1];
        }
    }

    return new URL(baseurl + filename)
}

export function startPersistenceTimerInterval() {
    persistenceTimerInterval = setInterval(checkForMutationsToSave, 5000);
}

export function stopPersistenceTimerInterval() {
    clearInterval(persistenceTimerInterval);
    persistenceTimerInterval = undefined;
}

function restartPersistenceTimerInterval() {
    stopPersistenceTimerInterval();
    startPersistenceTimerInterval();
}

function resetPersistenceSessionStore() {
    sessionStorage["lively.scriptMutationsDetected"] = 'false';
    sessionStorage["lively.persistenceCurrentlyCloning"] = 'false';
}

function checkForMutationsToSave() {
    if (isSaveDOMAllowed() && sessionStorage["lively.scriptMutationsDetected"] === 'true') {
        console.log("[persistence] timer-based mutations detected, saving DOM...")
        saveDOM();
    }
}

function isSaveDOMAllowed() {
    return isPersistOnIntervalActive();
}

function isPersistOnIntervalActive() {
    var check = $("#persistOnInterval");
    if (!check) return false;

    if (check.size() > 0 && !check[0].checked) {
        return false;
    }

    return true;
}

export function isCurrentlyCloning() {
    return sessionStorage["lively.persistenceCurrentlyCloning"] === 'true';
}

function saveDOM() {
    var world;
    sessionStorage["lively.persistenceCurrentlyCloning"] = 'true';
    try {
        world = $("html").clone();
    }
    finally {
        sessionStorage["lively.persistenceCurrentlyCloning"] = 'false';
    }

    world.find("[data-lively4-donotpersist='children']").empty();
    world.find("[data-lively4-donotpersist='all']").remove();
    world.find("#editor").empty();
    world.find("#console").empty();
    world.find("#ace_editor\\.css").remove();
    world.find("#ace-tm").remove();
    world.find("style").filter((i,e) => /\s*\.error_widget_wrapper+/.test(e.textContent)).remove();

    var content = "<!DOCTYPE html>\n" + world[0].outerHTML;

    resetPersistenceSessionStore();

    writeFile(content);
}

function writeFile(content) {
    var url = getURL()
    console.log("[persistence] save " + url)
    $.ajax({
        url: url,
        type: 'PUT',
        data: content,
        success: text => {
            console.log("[persistence] file " + url + " written.")
        },
        error: (xhr, status, error) => {
            console.log("[persistence] could not write " + url + ": " + error)
        }
    });
}

initialize();<commit_msg>clean-up log messages<commit_after>'use strict';

var persistenceTimerInterval;

function isLively4Script(node) {
    return node.tagName
        && node.tagName.toLocaleLowerCase() == 'script'
        && node.type == 'lively4script'
}

function initialize(){
    resetPersistenceSessionStore();

    var observer = new MutationObserver((mutations, observer) => {
        mutations.forEach(record => {
            if (record.target.id == 'console'
                || record.target.id == 'editor') return;
            var shouldSave = false;
            if (record.type == 'childList') {
                /*var nodes = [...record.addedNodes].concat([...record.removedNodes]);
                shouldSave = nodes.some(node => {
                    return isLively4Script(node);
                });*/
                shouldSave = true;
            }
            else if (record.type == 'characterData') {
                shouldSave = true;
            }
            else if (record.type == 'attributes') {
                shouldSave = true;
            }

            if (shouldSave) {
                sessionStorage["lively.scriptMutationsDetected"] = 'true';
                if (isPersistOnIntervalActive()) {
                    restartPersistenceTimerInterval();
                }
            }
        })
    }).observe(document, {childList: true, subtree: true, characterData: true, attributes: true});
}

function getURL(){
    var baseurl = $('#baseurl').val() // How to abstract from UI? #TODO #JensLincke
    var filename = $('#filename').val()

    var url = document.URL;
    var r = /https:\/\/([\w-]+)\.github\.io\/([\w-]+)\/(.+)/i;
    var results = url.match(r);

    if (results)
    {
        filename = results[3];
    }
    else
    {
        r = /localhost:8080\/(.+)/i;
        results = url.match(r);
        if (results)
        {
            filename = results[1];
        }
    }

    return new URL(baseurl + filename)
}

export function startPersistenceTimerInterval() {
    persistenceTimerInterval = setInterval(checkForMutationsToSave, 5000);
}

export function stopPersistenceTimerInterval() {
    clearInterval(persistenceTimerInterval);
    persistenceTimerInterval = undefined;
}

function restartPersistenceTimerInterval() {
    stopPersistenceTimerInterval();
    startPersistenceTimerInterval();
}

function resetPersistenceSessionStore() {
    sessionStorage["lively.scriptMutationsDetected"] = 'false';
    sessionStorage["lively.persistenceCurrentlyCloning"] = 'false';
}

function checkForMutationsToSave() {
    if (isSaveDOMAllowed() && sessionStorage["lively.scriptMutationsDetected"] === 'true') {
        console.log("[persistence] timer-based mutations detected, saving DOM...")
        saveDOM();
    }
}

function isSaveDOMAllowed() {
    return isPersistOnIntervalActive();
}

function isPersistOnIntervalActive() {
    var check = $("#persistOnInterval");
    if (!check || check.size() == 0) return false;

    return check[0].checked;
}

export function isCurrentlyCloning() {
    return sessionStorage["lively.persistenceCurrentlyCloning"] === 'true';
}

function saveDOM() {
    var world;
    sessionStorage["lively.persistenceCurrentlyCloning"] = 'true';
    try {
        world = $("html").clone();
    }
    finally {
        sessionStorage["lively.persistenceCurrentlyCloning"] = 'false';
    }

    world.find("[data-lively4-donotpersist='children']").empty();
    world.find("[data-lively4-donotpersist='all']").remove();
    world.find("#editor").empty();
    world.find("#console").empty();
    world.find("#ace_editor\\.css").remove();
    world.find("#ace-tm").remove();
    world.find("style").filter((i,e) => /\s*\.error_widget_wrapper+/.test(e.textContent)).remove();

    var content = "<!DOCTYPE html>\n" + world[0].outerHTML;

    resetPersistenceSessionStore();

    writeFile(content);
}

function writeFile(content) {
    var url = getURL()
    console.log("[persistence] save " + url)
    $.ajax({
        url: url,
        type: 'PUT',
        data: content,
        success: text => {
            console.log("[persistence] file " + url + " written.")
        },
        error: (xhr, status, error) => {
            console.log("[persistence] could not write " + url + ": " + error)
        }
    });
}

initialize();<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
 var program = require('commander')
 , os = require('os')
 , fs = require('fs')
 , mkdirp = require('mkdirp');

/**
 * Create Nodeigniter application at `path`.
 *
 * @param {String} path
 * @api private
 */
 exports = module.exports = function create(path) {
  console.log('creating Nodeigniter application at : ' + path);

  (function createApplication(path) {
    emptyDirectory(path, function(empty) {
      if (empty) {
        createApplicationAt(path);
      } else {
        program.confirm('destination is not empty, continue? ', function(ok) {
          if (ok) {
            process.stdin.destroy();
            createApplicationAt(path);
          } else {
            abort('aborting');
          }
        });
      }
    });
  })(path);
}

var eol = 'win32' == os.platform() ? '\r\n' : '\n';

var autoloadConfig = [
'module.exports = new function() {'
, '    this.libraries = [];'
, '    this.helpers = [\'security\',\'array\',\'url\', \'string\', \'html\', \'form\'];'
, '    this.models = [];'
, '}'
].join(eol);

var routesConfig = [
'module.exports = new function() {'
, ''
, '    //add routes here'
, '    this.route = ['
, '    {src: \'/test\', dest: \'/main/test\', method: [\'GET\',\'POST\']}'
, '    ];'
, ''
, '}'
].join(eol);

var mainController = [
'var ni = require(\'nodeigniter\');'
, ''
, 'module.exports = new function() {'
, ''
, '   this.index = function(req, res, next) {'
, '       ni.partial(\'section/header.ejs\').partial(\'main/main.index.ejs\').render();'
, '       ni.partial(\'section/header.ejs\').partial(\'main/main.test.ejs\').render();'
, '   }'
, '};'
].join(eol);

var notfoundError = [
''
, '
Page not Found
'
, ''
].join(eol);

var mainView = [
'    '
, '
'
, '
'
, '
'
, '
'
, '
Nodeigniter
'
, '
Welcome to the fast and flexible ignition framework for node developers
'
, '
Version 1.0
'
, ''
, '
Features:
'
, '
'
, '
Custom routing
'
, '
Asynchronous module loading
'
, '
Built in with different helpers and libraries
'
, '
Localize helper functions for view rendering
'
, '
Use connect middleware
'
, '
Customize it according to your needs
'
, '
'
, '
'
, '
'
, '
'
, '
'
].join(eol);

var testView = [
'
'
, '
'
, '
'
, '
'
, '
TEST
'
, '
This is an example of another view you can pass through your controller. Be sure to route this to your URL in the routes file.
'
, '
'
, '
'
, '
'
, '
'
].join(eol);

var headerElement = [
'{{= doctype(\'html5\') }}'
, ''
, '    '
, '        <title>Nodeigniter - Fast and flexible node framework</title>'
, '        {{= link_tag(\'css/bootstrap.min.css?timestamp=\'+Number(new Date())) }}'
, '        {{= link_tag(\'css/style.css?timestamp=\'+Number(new Date())) }}'
, '        {{= script_tag(\'//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js\') }}'
, '    '
].join(eol);

var appJS = [
'var ni = require(\'nodeigniter\'),'
, 'connect = require('connect');'
, ''
, 'ni.config(\'root\', __dirname + \'/app\');'
, 'ni._initialize(function(){'
, '    connect.createServer('
, '        connect.bodyParser(),'
, '        connect.static(\'public\'),'
, '        connect.query(),'
, '        connect.cookieParser(\'ni_cookie\'),'
, '        connect.session({secret: \'kM43QtvEhmhH2KK9sJac\',cookie: {maxAge: 36000}}),'
, '        ni.router,'
, '        ni.not_found'
, '        ).listen(3000);'
, '    console.log(\'Application Started on Port: \'+3000);'
, '});'
].join(eol);

var componentJSON = [
'{'
, '    "name": "ni-app",'
, '    "version": "1.0.0",'
, '    "dependencies": {'
, '        "jquery": "latest",'
, '        "bootstrap": "latest"'
, '   }'
, '}'
].join(eol);

var packageJSON = [
'{'
, '  "name": "ni-app",'
, '  "version": "1.0.0",'
, '  "private": true,'
, '  "dependencies": {'
, '    "mongodb": "1.x.x",'
, '    "ejs": "0.8.x",'
, '    "connect": "1.x.x"'
, '  }'
, '}'
, ''
].join(eol);

/**
 * Create application at the given directory `path`.
 *
 * @param {String} path
 */
 function createApplicationAt(path) {
  console.log();
  process.on('exit', function(){
    console.log();
    console.log('   install dependencies:');
    console.log('     $ cd %s && npm install', path);
    console.log();
    console.log('   install static files (requires Bower!):');
    console.log('     $ bower install ');
    console.log();
    console.log('   run the app:');
    console.log('     $ node app.js ');
    console.log();
  });

  mkdir(path, function() {
  	mkdir(path + '/app');
    mkdir(path + '/app/config', function(){
      write(path + '/app/config/autoload.js', autoloadConfig);
      write(path + '/app/config/routes.js', routesConfig);
    });
    mkdir(path + '/app/controllers', function(){
      write(path + '/app/controllers/main.js', mainController);
    });
    mkdir(path + '/app/error', function(){
      write(path + '/app/error/404.ejs', notfoundError);
    });
    mkdir(path + '/app/views');
    mkdir(path + '/app/views/main', function(){
      write(path + '/app/views/main/main.index.ejs', mainView);
      write(path + '/app/views/main/main.test.ejs', testView);
    });
    mkdir(path + '/app/views/elements', function(){
      write(path + '/app/views/main/header.ejs', headerElement);
    });
    mkdir(path + '/public');
    mkdir(path + '/public/css');
    mkdir(path + '/public/img');
    mkdir(path + '/public/js');
    write(path + '/app.js', appJS);
    write(path + '/component.json', componentJSON);
    write(path + '/package.json', packageJSON);
  });
}

/**
 * Check if the given directory `path` is empty.
 *
 * @param {String} path
 * @param {Function} fn
 */
 function emptyDirectory(path, fn) {
  fs.readdir(path, function(err, files){
    if (err && 'ENOENT' != err.code) throw err;
    fn(!files || !files.length);
  });
}

/**
 * Mkdir -p.
 *
 * @param {String} path
 * @param {Function} fn
 */
 function mkdir(path, fn) {
  mkdirp(path, 0755, function(err){
    if (err) throw err;
    console.log('   \033[36mcreate\033[0m : ' + path);
    fn && fn();
  });
}

/**
 * echo str > path.
 *
 * @param {String} path
 * @param {String} str
 */
 function write(path, str) {
  fs.writeFile(path, str);
  console.log('   \x1b[36mcreate\x1b[0m : ' + path);
}

/**
 * Exit with the given `str`.
 *
 * @param {String} str
 */
 function abort(str) {
  console.error(str);
  process.exit(1);
}<commit_msg>still escaping quotes<commit_after>/**
 * Module dependencies.
 */
 var program = require('commander')
 , os = require('os')
 , fs = require('fs')
 , mkdirp = require('mkdirp');

/**
 * Create Nodeigniter application at `path`.
 *
 * @param {String} path
 * @api private
 */
 exports = module.exports = function create(path) {
  console.log('creating Nodeigniter application at : ' + path);

  (function createApplication(path) {
    emptyDirectory(path, function(empty) {
      if (empty) {
        createApplicationAt(path);
      } else {
        program.confirm('destination is not empty, continue? ', function(ok) {
          if (ok) {
            process.stdin.destroy();
            createApplicationAt(path);
          } else {
            abort('aborting');
          }
        });
      }
    });
  })(path);
}

var eol = 'win32' == os.platform() ? '\r\n' : '\n';

var autoloadConfig = [
'module.exports = new function() {'
, '    this.libraries = [];'
, '    this.helpers = [\'security\',\'array\',\'url\', \'string\', \'html\', \'form\'];'
, '    this.models = [];'
, '}'
].join(eol);

var routesConfig = [
'module.exports = new function() {'
, ''
, '    //add routes here'
, '    this.route = ['
, '    {src: \'/test\', dest: \'/main/test\', method: [\'GET\',\'POST\']}'
, '    ];'
, ''
, '}'
].join(eol);

var mainController = [
'var ni = require(\'nodeigniter\');'
, ''
, 'module.exports = new function() {'
, ''
, '   this.index = function(req, res, next) {'
, '       ni.partial(\'section/header.ejs\').partial(\'main/main.index.ejs\').render();'
, '       ni.partial(\'section/header.ejs\').partial(\'main/main.test.ejs\').render();'
, '   }'
, '};'
].join(eol);

var notfoundError = [
''
, '
Page not Found
'
, ''
].join(eol);

var mainView = [
'    '
, '
'
, '
'
, '
'
, '
'
, '
Nodeigniter
'
, '
Welcome to the fast and flexible ignition framework for node developers
'
, '
Version 1.0
'
, ''
, '
Features:
'
, '
'
, '
Custom routing
'
, '
Asynchronous module loading
'
, '
Built in with different helpers and libraries
'
, '
Localize helper functions for view rendering
'
, '
Use connect middleware
'
, '
Customize it according to your needs
'
, '
'
, '
'
, '
'
, '
'
, '
'
].join(eol);

var testView = [
'
'
, '
'
, '
'
, '
'
, '
TEST
'
, '
This is an example of another view you can pass through your controller. Be sure to route this to your URL in the routes file.
'
, '
'
, '
'
, '
'
, '
'
].join(eol);

var headerElement = [
'{{= doctype(\'html5\') }}'
, ''
, '    '
, '        <title>Nodeigniter - Fast and flexible node framework</title>'
, '        {{= link_tag(\'css/bootstrap.min.css?timestamp=\'+Number(new Date())) }}'
, '        {{= link_tag(\'css/style.css?timestamp=\'+Number(new Date())) }}'
, '        {{= script_tag(\'//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js\') }}'
, '    '
].join(eol);

var appJS = [
'var ni = require(\'nodeigniter\'),'
, 'connect = require(\'connect\');'
, ''
, 'ni.config(\'root\', __dirname + \'/app\');'
, 'ni._initialize(function(){'
, '    connect.createServer('
, '        connect.bodyParser(),'
, '        connect.static(\'public\'),'
, '        connect.query(),'
, '        connect.cookieParser(\'ni_cookie\'),'
, '        connect.session({secret: \'kM43QtvEhmhH2KK9sJac\',cookie: {maxAge: 36000}}),'
, '        ni.router,'
, '        ni.not_found'
, '        ).listen(3000);'
, '    console.log(\'Application Started on Port: \'+3000);'
, '});'
].join(eol);

var componentJSON = [
'{'
, '    "name": "ni-app",'
, '    "version": "1.0.0",'
, '    "dependencies": {'
, '        "jquery": "latest",'
, '        "bootstrap": "latest"'
, '   }'
, '}'
].join(eol);

var packageJSON = [
'{'
, '  "name": "ni-app",'
, '  "version": "1.0.0",'
, '  "private": true,'
, '  "dependencies": {'
, '    "mongodb": "1.x.x",'
, '    "ejs": "0.8.x",'
, '    "connect": "1.x.x"'
, '  }'
, '}'
, ''
].join(eol);

/**
 * Create application at the given directory `path`.
 *
 * @param {String} path
 */
 function createApplicationAt(path) {
  console.log();
  process.on('exit', function(){
    console.log();
    console.log('   install dependencies:');
    console.log('     $ cd %s && npm install', path);
    console.log();
    console.log('   install static files (requires Bower!):');
    console.log('     $ bower install ');
    console.log();
    console.log('   run the app:');
    console.log('     $ node app.js ');
    console.log();
  });

  mkdir(path, function() {
  	mkdir(path + '/app');
    mkdir(path + '/app/config', function(){
      write(path + '/app/config/autoload.js', autoloadConfig);
      write(path + '/app/config/routes.js', routesConfig);
    });
    mkdir(path + '/app/controllers', function(){
      write(path + '/app/controllers/main.js', mainController);
    });
    mkdir(path + '/app/error', function(){
      write(path + '/app/error/404.ejs', notfoundError);
    });
    mkdir(path + '/app/views');
    mkdir(path + '/app/views/main', function(){
      write(path + '/app/views/main/main.index.ejs', mainView);
      write(path + '/app/views/main/main.test.ejs', testView);
    });
    mkdir(path + '/app/views/elements', function(){
      write(path + '/app/views/main/header.ejs', headerElement);
    });
    mkdir(path + '/public');
    mkdir(path + '/public/css');
    mkdir(path + '/public/img');
    mkdir(path + '/public/js');
    write(path + '/app.js', appJS);
    write(path + '/component.json', componentJSON);
    write(path + '/package.json', packageJSON);
  });
}

/**
 * Check if the given directory `path` is empty.
 *
 * @param {String} path
 * @param {Function} fn
 */
 function emptyDirectory(path, fn) {
  fs.readdir(path, function(err, files){
    if (err && 'ENOENT' != err.code) throw err;
    fn(!files || !files.length);
  });
}

/**
 * Mkdir -p.
 *
 * @param {String} path
 * @param {Function} fn
 */
 function mkdir(path, fn) {
  mkdirp(path, 0755, function(err){
    if (err) throw err;
    console.log('   \033[36mcreate\033[0m : ' + path);
    fn && fn();
  });
}

/**
 * echo str > path.
 *
 * @param {String} path
 * @param {String} str
 */
 function write(path, str) {
  fs.writeFile(path, str);
  console.log('   \x1b[36mcreate\x1b[0m : ' + path);
}

/**
 * Exit with the given `str`.
 *
 * @param {String} str
 */
 function abort(str) {
  console.error(str);
  process.exit(1);
}<|endoftext|>


<commit_before>//TODO: encapsulate back to private function when card design is done

//typical sizes:
/*
  portrait:
    iPhone 4,5    375px
    iPhone 6      320px
    iPhone 6+     414px
    Galaxy S3     360px
  landscape:
    iPhone 4      480px
    iPhone 5      568px
    iPhone 6      667px (574px container)
    iPhone 6+     736px (574px container)
    Galaxy S3     640px (574px container)
*/

const ContainerWidth = {
  XS: 375,
  SM: 574,
  MD: 728,
  LG: 938,
  XL: 1148,
  XXL: 1384
}

const COLUMNS = 14
const GUTTER_PX = 16

export default {
  ContainerWidth : ContainerWidth,

  getFluidContainerWidth(browserWidth) {
    return browserWidth - GUTTER_PX
  },

  getContainerWidth(browserWidth) {
    //should match variables from bootstrap

    const Breakpoints = {
      XS: 0,    // Extra small screen / phone
      SM: 544,  // Small screen / phone
      MD: 768,  // Medium screen / tablet
      LG: 992,  // Large screen / desktop
      XL: 1200,  // Extra large screen / wide desktop
      XXL: 1440,  // Extra large screen / wide desktop
    }

    if (browserWidth <= ContainerWidth.SM) {
      return browserWidth //container becomes fluid for small size
    } else if (browserWidth > ContainerWidth.SM && browserWidth < Breakpoints.MD) {
      return ContainerWidth.SM
    } else if (browserWidth >= Breakpoints.MD && browserWidth < Breakpoints.LG) {
      return ContainerWidth.MD
    } else if (browserWidth >= Breakpoints.LG && browserWidth < Breakpoints.XL) {
      return ContainerWidth.LG
    } else if (browserWidth >= Breakpoints.XL && browserWidth < Breakpoints.XXL) {
      return ContainerWidth.XL
    } else if (browserWidth >= Breakpoints.XXL) {
      return ContainerWidth.XXL
    }
  },
  init(containerWidth) {
    return {
      //returns width in px of Container's content area (width without paddings)
      getColumnContentWidth({numberOfCols}) {
        const oneColPercent = (100 / COLUMNS) / 100
        return containerWidth * (oneColPercent * numberOfCols) - GUTTER_PX
      }
    }
  },

}
<commit_msg>improving grid calculation to account for mobile breakpoints<commit_after>//TODO: encapsulate back to private function when card design is done

//typical sizes:
/*
  portrait:
    iPhone 4,5    375px
    iPhone 6      320px
    iPhone 6+     414px
    Galaxy S3     360px
  landscape:
    iPhone 4      480px
    iPhone 5      568px
    iPhone 6      667px (574px container)
    iPhone 6+     736px (574px container)
    Galaxy S3     640px (574px container)
*/

const ContainerWidth = {
  XS: 375,
  SM: 574,
  MD: 728,
  LG: 938,
  XL: 1148,
  XXL: 1384
}

const Breakpoints = {
  XS: 0,    // Extra small screen / phone
  SM: 544,  // Small screen / phone
  MD: 768,  // Medium screen / tablet
  LG: 992,  // Large screen / desktop
  XL: 1200,  // Extra large screen / wide desktop
  XXL: 1440,  // Extra large screen / wide desktop
}

const COLUMNS = 14

//since media query defines smaller base font size in typography.scss we need to calculated gutters properly
const getGutter = containerOrBrowserWidth => (containerOrBrowserWidth > Breakpoints.SM ? 16 : 14)

export default {
  ContainerWidth : ContainerWidth,

  getFluidContainerWidth(browserWidth) {
    return browserWidth - getGutter(browserWidth)
  },

  getContainerWidth(browserWidth) {
    //should match variables from bootstrap

    if (browserWidth <= ContainerWidth.SM) {
      return browserWidth //container becomes fluid for small size
    } else if (browserWidth > ContainerWidth.SM && browserWidth < Breakpoints.MD) {
      return ContainerWidth.SM
    } else if (browserWidth >= Breakpoints.MD && browserWidth < Breakpoints.LG) {
      return ContainerWidth.MD
    } else if (browserWidth >= Breakpoints.LG && browserWidth < Breakpoints.XL) {
      return ContainerWidth.LG
    } else if (browserWidth >= Breakpoints.XL && browserWidth < Breakpoints.XXL) {
      return ContainerWidth.XL
    } else if (browserWidth >= Breakpoints.XXL) {
      return ContainerWidth.XXL
    }
  },
  init(containerWidth) {
    return {
      //returns width in px of Container's content area (width without paddings)
      getColumnContentWidth({numberOfCols}) {
        const oneColPercent = (100 / COLUMNS) / 100
        const containerGutter = containerWidth >= Breakpoints.SM ? getGutter(containerWidth) : 0
        return containerWidth * (oneColPercent * numberOfCols) - containerGutter
      }
    }
  },

}
<|endoftext|>


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

'use strict';

const imageWrite = require('etcher-image-write');
const imageStream = require('etcher-image-stream');
const Bluebird = require('bluebird');
const umount = Bluebird.promisifyAll(require('umount'));
const os = require('os');
const isWindows = os.platform() === 'win32';

if (isWindows) {
  var removedrive = Bluebird.promisifyAll(require('removedrive'));
}

/**
 * @summary Write an image to a disk drive
 * @function
 * @public
 *
 * @description
 * See https://github.com/resin-io-modules/etcher-image-write for information
 * about the `state` object passed to `onProgress` callback.
 *
 * @param {String} imagePath - path to image
 * @param {Object} drive - drive
 * @param {Object} options - options
 * @param {Boolean} [options.unmountOnSuccess=false] - unmount on success
 * @param {Boolean} [options.validateWriteOnSuccess=false] - validate write on success
 * @param {Function} onProgress - on progress callback (state)
 *
 * @fulfil {Boolean} - whether the operation was successful
 * @returns {Promise}
 *
 * @example
 * writer.writeImage('path/to/image.img', {
 *   device: '/dev/disk2'
 * }, {
 *   unmountOnSuccess: true,
 *   validateWriteOnSuccess: true
 * }, function(state) {
 *   console.log(state.percentage);
 * }).then(function() {
 *   console.log('Done!');
 * });
 */
exports.writeImage = function(imagePath, drive, options, onProgress) {
  return umount.umountAsync(drive.device).then(function() {
    return imageStream.getFromFilePath(imagePath);
  }).then(function(image) {
    return imageWrite.write(drive.device, image.stream, {
      check: options.validateWriteOnSuccess,
      size: image.size,
      transform: image.transform
    });
  }).then(function(writer) {
    return new Bluebird(function(resolve, reject) {
      writer.on('progress', onProgress);
      writer.on('error', reject);
      writer.on('done', resolve);
    });
  }).tap(function() {
    if (!options.unmountOnSuccess) {
      return;
    }

    if (isWindows && drive.mountpoint) {
      return removedrive.ejectAsync(drive.mountpoint);
    }

    return umount.umountAsync(drive.device);
  });
};
<commit_msg>Annotation for EncloseJS to ignore missing dependency (#454)<commit_after>/*
 * Copyright 2016 Resin.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const imageWrite = require('etcher-image-write');
const imageStream = require('etcher-image-stream');
const Bluebird = require('bluebird');
const umount = Bluebird.promisifyAll(require('umount'));
const os = require('os');
const isWindows = os.platform() === 'win32';

if (isWindows) {
  var removedrive = Bluebird.promisifyAll(require('removedrive', 'can-ignore'));
}

/**
 * @summary Write an image to a disk drive
 * @function
 * @public
 *
 * @description
 * See https://github.com/resin-io-modules/etcher-image-write for information
 * about the `state` object passed to `onProgress` callback.
 *
 * @param {String} imagePath - path to image
 * @param {Object} drive - drive
 * @param {Object} options - options
 * @param {Boolean} [options.unmountOnSuccess=false] - unmount on success
 * @param {Boolean} [options.validateWriteOnSuccess=false] - validate write on success
 * @param {Function} onProgress - on progress callback (state)
 *
 * @fulfil {Boolean} - whether the operation was successful
 * @returns {Promise}
 *
 * @example
 * writer.writeImage('path/to/image.img', {
 *   device: '/dev/disk2'
 * }, {
 *   unmountOnSuccess: true,
 *   validateWriteOnSuccess: true
 * }, function(state) {
 *   console.log(state.percentage);
 * }).then(function() {
 *   console.log('Done!');
 * });
 */
exports.writeImage = function(imagePath, drive, options, onProgress) {
  return umount.umountAsync(drive.device).then(function() {
    return imageStream.getFromFilePath(imagePath);
  }).then(function(image) {
    return imageWrite.write(drive.device, image.stream, {
      check: options.validateWriteOnSuccess,
      size: image.size,
      transform: image.transform
    });
  }).then(function(writer) {
    return new Bluebird(function(resolve, reject) {
      writer.on('progress', onProgress);
      writer.on('error', reject);
      writer.on('done', resolve);
    });
  }).tap(function() {
    if (!options.unmountOnSuccess) {
      return;
    }

    if (isWindows && drive.mountpoint) {
      return removedrive.ejectAsync(drive.mountpoint);
    }

    return umount.umountAsync(drive.device);
  });
};
<|endoftext|>


<commit_before>var Stream = require('./stream');
var utils = require('./utils');

// export event emitter object (clone before use!)
module.exports = {

    /**
     * Call handlers of a specific event or pipe if handler is a stream.
     *
     * @public
     * @param {string} The event name.
     * @param {object} The data for the event handlers.
     */
    emit: function (eventName, stream) {
        var events = this._events;

        // create eso and emit to flow hanlder
        stream = stream || Stream.local(this);

        // save origin scope on stream
        stream._ = this;

        // compare event or test regex
        if (events[eventName]) {

            // index for events that must be removed
            var obsoleteEvents = [];

            // call handlers
            for (var i = 0, l = events[eventName].length; i < l; ++i) {
                handler = events[eventName][i];

                if (handler) {

                    // pass stream to flow to setup handlers
                    handler.fn.call(this, stream, handler.data);

                    // remove from event buffer, if once is true
                    if (handler._1) {
                        handler = undefined;
                        obsoleteEvents.push([eventName, i]);
                    }
                }
            }

            // remove obsolete events
            this._rm(obsoleteEvents);
        }

        // return transmitter to send and receive data
        return stream;
    },

    /**
     * Listen on an event.
     *
     * @public
     * @param {string} The even name regular expression pattern.
     * @param {function} The event handler.
     * @param {boolean} Event handler is removed after calling.
     * @param {boolean} If an event is emitted from the route method, the event
     *                  loop stops, when an event is found. This argument tells the
     *                  event loop to continue.
     */
    on: function (eventName, method, data, once) {

        var events = this._events;
        var handler = {
            fn: method,
            data: data || {},
            _1: once
        };

        if (!events[eventName]) {
            events[eventName] = [];
        }

        events[eventName].push(handler);
    },

    /**
     * Remove an event or a single event hanlder from the event loop.
     *
     * @public
     * @param {string} The even name regular expression pattern.
     * @param {function} The event handler.
     */
    off: function (event, handler) {

        var events = this._events;

        if (events[event]) {

            if (handler) {
                var rm = [];

                for (var i = 0; i < events[event].length; ++i) {
                    if (events[event][i] === handler) {
                        events[event][i] = undefined;
                        rm.push([event, i]);
                    }
                }

                this._rm(rm);

            } else {
                delete events[event];
            }
        }
    },

    /**
     * Removes the collected events from an observable.
     *
     * @private
     * @param {object} The events of an observable.
     * @param {array} The infos for removing events.
     */
    _rm: function (rmObject) {

        if (rmObject.length) {
            var events = this._events;
            for (var i = 0, l = rmObject.length; i < l; ++i) {

                // remove handler
                events[rmObject[i][0]].splice(rmObject[i][1], 1);

                // remove event
                if (events[rmObject[i][0]].length === 0) {
                    delete events[rmObject[i][0]];
                }
            }
        }
    }
};
<commit_msg>require specific stream type<commit_after>var EventStream = require('./stream_local');
var utils = require('./utils');

// export event emitter object (clone before use!)
module.exports = {

    /**
     * Call handlers of a specific event or pipe if handler is a stream.
     *
     * @public
     * @param {string} The event name.
     * @param {object} The data for the event handlers.
     */
    emit: function (eventName, stream) {
        var events = this._events;

        // create eso and emit to flow hanlder
        stream = stream || EventStream(this);

        // save origin scope on stream
        stream._ = this;

        // compare event or test regex
        if (events[eventName]) {

            // index for events that must be removed
            var obsoleteEvents = [];

            // call handlers
            for (var i = 0, l = events[eventName].length; i < l; ++i) {
                handler = events[eventName][i];

                if (handler) {

                    // pass stream to flow to setup handlers
                    handler.fn.call(this, stream, handler.data);

                    // remove from event buffer, if once is true
                    if (handler._1) {
                        handler = undefined;
                        obsoleteEvents.push([eventName, i]);
                    }
                }
            }

            // remove obsolete events
            this._rm(obsoleteEvents);
        }

        // return transmitter to send and receive data
        return stream;
    },

    /**
     * Listen on an event.
     *
     * @public
     * @param {string} The even name regular expression pattern.
     * @param {function} The event handler.
     * @param {boolean} Event handler is removed after calling.
     * @param {boolean} If an event is emitted from the route method, the event
     *                  loop stops, when an event is found. This argument tells the
     *                  event loop to continue.
     */
    on: function (eventName, method, data, once) {

        var events = this._events;
        var handler = {
            fn: method,
            data: data || {},
            _1: once
        };

        if (!events[eventName]) {
            events[eventName] = [];
        }

        events[eventName].push(handler);
    },

    /**
     * Remove an event or a single event hanlder from the event loop.
     *
     * @public
     * @param {string} The even name regular expression pattern.
     * @param {function} The event handler.
     */
    off: function (event, handler) {

        var events = this._events;

        if (events[event]) {

            if (handler) {
                var rm = [];

                for (var i = 0; i < events[event].length; ++i) {
                    if (events[event][i] === handler) {
                        events[event][i] = undefined;
                        rm.push([event, i]);
                    }
                }

                this._rm(rm);

            } else {
                delete events[event];
            }
        }
    },

    /**
     * Removes the collected events from an observable.
     *
     * @private
     * @param {object} The events of an observable.
     * @param {array} The infos for removing events.
     */
    _rm: function (rmObject) {

        if (rmObject.length) {
            var events = this._events;
            for (var i = 0, l = rmObject.length; i < l; ++i) {

                // remove handler
                events[rmObject[i][0]].splice(rmObject[i][1], 1);

                // remove event
                if (events[rmObject[i][0]].length === 0) {
                    delete events[rmObject[i][0]];
                }
            }
        }
    }
};
<|endoftext|>


<commit_before>'use strict';
exports.info = {
  name: 'stats',
  desc: 'Posts statistics about this process/system/client',
  usage: 'stats',
};

const moment = require('moment');
const os = require('os');
const pusage = require('pidusage');

exports.run = (d, m) => {
  const nodeUp = moment.duration(process.uptime() * 1000);
  const totalMem = (os.totalmem() / 1024 / 1024).toFixed(0);
  const freeMem = (os.freemem() / 1024 / 1024).toFixed(0);
  const load = os.loadavg().map(x => (x * 100).toFixed(2));
  const osUp = moment.duration(os.uptime() * 1000);
  const cpuData = os.cpus();
  const usedRam = (totalMem - freeMem).toFixed(0);
  let members = 0;
  d.client.guilds.forEach(g => {
    members += g.members.size;
  });

  pusage.stat(process.pid, (err, stat) => {
    let cpu;
    if (err) {
      cpu = '--';
    } else {
      cpu = stat.cpu.toFixed(3);

      let str = '--Process Stats--\n';
      str += `Name: '${d.client.user.username} #${d.client.user.discriminator}' (ID: ${d.client.user.id})\n`;
      str += `Uptime: ${formatUptime(nodeUp)}\n`;
      str += `RAM: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)}MB\n`;
      str += `CPU: ${cpu}%\n`;
      str += `Guilds: ${d.client.guilds.size}\n`;
      str += `Channels: ${d.client.channels.filter(c => c.type === 'text').size} text`;
      str += ` / ${d.client.channels.filter(c => c.type === 'voice').size} voice\n`;
      str += `Members: ${members}\n`;

      str += '\n--System Stats--\n';
      str += `OS: '${os.type()} - ${os.release()}'\n`;
      str += `Core${cpuData.length > 1 ? 's' : ''}: '${cpuData[0].model}' (${cpuData.length}x)\n`;
      str += `Uptime: ${formatUptime(osUp)}\n`;
      str += `RAM: ${usedRam}MB/${totalMem}MB (${(usedRam / totalMem * 100).toFixed(2)}%)\n`;
      str += `CPU: ${load[0]}%\n`;

      str += '\n--Session Stats--\n';
      str += `Messages: ${d.client.count.messages}\n`;
      str += `Commands: ${d.client.count.commands}\n`;
      str += `Tweets: ${d.client.count.tweets}\n`;

      m.channel.sendCode('qml', str);
    }
  });
};

function formatUptime(time) {
  let str = '';
  const days = time.asDays().toFixed(0);
  if (days > 0) {
    str += `${days} day${time.days() === 1 ? '' : 's'} `;
  }
  str += `${time.hours()} hour${time.hours() === 1 ? '' : 's'} `;
  str += `${time.minutes()} minute${time.minutes() === 1 ? '' : 's'} `;
  str += `${time.seconds()} second${time.seconds() === 1 ? '' : 's'} `;
  return str.trim();
}
<commit_msg>Header Change<commit_after>'use strict';
exports.info = {
  name: 'stats',
  desc: 'Posts statistics about this process/system/client',
  usage: 'stats',
};

const moment = require('moment');
const os = require('os');
const pusage = require('pidusage');

exports.run = (d, m) => {
  const nodeUp = moment.duration(process.uptime() * 1000);
  const totalMem = (os.totalmem() / 1024 / 1024).toFixed(0);
  const freeMem = (os.freemem() / 1024 / 1024).toFixed(0);
  const load = os.loadavg().map(x => (x * 100).toFixed(2));
  const osUp = moment.duration(os.uptime() * 1000);
  const cpuData = os.cpus();
  const usedRam = (totalMem - freeMem).toFixed(0);
  let members = 0;
  d.client.guilds.forEach(g => {
    members += g.members.size;
  });

  pusage.stat(process.pid, (err, stat) => {
    let cpu;
    if (err) {
      cpu = '--';
    } else {
      cpu = stat.cpu.toFixed(3);

      let str = '--Bot Stats--\n';
      str += `Name: '${d.client.user.username} #${d.client.user.discriminator}' (ID: ${d.client.user.id})\n`;
      str += `Uptime: ${formatUptime(nodeUp)}\n`;
      str += `RAM: ${(process.memoryUsage().rss / 1024 / 1024).toFixed(2)}MB\n`;
      str += `CPU: ${cpu}%\n`;
      str += `Guilds: ${d.client.guilds.size}\n`;
      str += `Channels: ${d.client.channels.filter(c => c.type === 'text').size} text`;
      str += ` / ${d.client.channels.filter(c => c.type === 'voice').size} voice\n`;
      str += `Members: ${members}\n`;

      str += '\n--System Stats--\n';
      str += `OS: '${os.type()} - ${os.release()}'\n`;
      str += `Core${cpuData.length > 1 ? 's' : ''}: '${cpuData[0].model}' (${cpuData.length}x)\n`;
      str += `Uptime: ${formatUptime(osUp)}\n`;
      str += `RAM: ${usedRam}MB/${totalMem}MB (${(usedRam / totalMem * 100).toFixed(2)}%)\n`;
      str += `CPU: ${load[0]}%\n`;

      str += '\n--Session Stats--\n';
      str += `Messages: ${d.client.count.messages}\n`;
      str += `Commands: ${d.client.count.commands}\n`;
      str += `Tweets: ${d.client.count.tweets}\n`;

      m.channel.sendCode('qml', str);
    }
  });
};

function formatUptime(time) {
  let str = '';
  const days = time.asDays().toFixed(0);
  if (days > 0) {
    str += `${days} day${time.days() === 1 ? '' : 's'} `;
  }
  str += `${time.hours()} hour${time.hours() === 1 ? '' : 's'} `;
  str += `${time.minutes()} minute${time.minutes() === 1 ? '' : 's'} `;
  str += `${time.seconds()} second${time.seconds() === 1 ? '' : 's'} `;
  return str.trim();
}
<|endoftext|>


<commit_before>const map = require('lodash/fp/map')
const assign = require('lodash/fp/assign')
const assignAll = require('lodash/fp/assignAll')
const mapValues = require('lodash/fp/mapValues')
const mapValuesWithKey = mapValues.convert({ cap: false })
const filter = require('lodash/fp/filter')
const flow = require('lodash/fp/flow')
const reduce = require('lodash/fp/reduce')
const sortBy = require('lodash/fp/sortBy')
const reverse = require('lodash/fp/reverse')
const keyBy = require('lodash/fp/keyBy')
const BigMath = require('bigmath')

module.exports = {
  needs: {
    'ordering.get.rawOrderingItems': 'first',
    'auth.get.whoami': 'first',
    'orders.get.orders': 'first',
    'supplierCommitments.get.supplierCommitments': 'first'
  },
  create: (api) => [
    api.ordering.get.rawOrderingItems,
    api.orders.get.orders,
    api.auth.get.whoami,
    (orderingItems, orders, whoami) => {
      const getItemsByOrders = mapValues(getItemsByOrder)
      const filterToSupplierCommitment = id => filter(o => o.supplierCommitmentId === id)

      return getItemsByOrders(orders)

      function getItemsByOrder (order) {
        const { supplierCommitments } = order
        const getItems = flow(
          map(supplierCommitment => {
            return getItem({ order, supplierCommitment })
          }),
          sortBy('totalMinValue'),
          reverse,
          keyBy('id')
        )

        return getItems(supplierCommitments)
      }

      function getItem ({ order, supplierCommitment }) {
        const { id: orderId } = order
        const orderingItemId = OrderingItemId({ order, supplierCommitment })
        const rawOrderingItem = orderingItems[orderingItemId]

        const allConsumerIntents = filterToSupplierCommitment(supplierCommitment.id)(order.allConsumerIntents)
        const myConsumerIntent = filterToSupplierCommitment(supplierCommitment.id)(order.myConsumerIntents)[0] || {
          agentId: whoami,
          supplierCommitmentId: supplierCommitment.id,
          orderId,
          minValue: '0',
          maxValue: '0'
        }
        const { currency } = order
        const { name, pluralName, batchSize } = supplierCommitment

        // TODO implement for real
        const expectedValue = Math.floor(myConsumerIntent.minValue + ((myConsumerIntent.maxValue - myConsumerIntent.minValue) / 2))
        const expectedCost = supplierCommitment.costFunction({ value: expectedValue, currency }) || 0

        const totalMinValue = sumMinValue(allConsumerIntents)
        const totalMaxValue = sumMaxValue(allConsumerIntents)
        const totalMinBatches = BigMath.floor(BigMath.div(totalMinValue, batchSize.value))
        const totalMinRemainder = BigMath.mod(totalMinValue, batchSize.value)
        const totalExtraValue = BigMath.sub(totalMaxValue, totalMinValue)
        const didFillExtra = BigMath.greaterThanOrEqualTo(BigMath.add(totalMinRemainder, totalExtraValue), batchSize.value)
        const totalBatches = BigMath.add(totalMinBatches, (didFillExtra ? '1' : '0'))

        const nextMin = didFillExtra ? '0' : totalMinRemainder
        const nextExtra = didFillExtra ? '0' : totalExtraValue
        const nextLeft = BigMath.sub(BigMath.sub(batchSize.value, nextExtra), nextMin)

        return assignAll([
          {
            isExpanded: false
          },
          rawOrderingItem,
          {
            id: orderingItemId,
            supplierCommitment,
            name,
            pluralName,
            batchSize,
            allConsumerIntents,
            myConsumerIntent,
            expectedValue,
            expectedCost,
            currency,
            totalMinValue,
            totalMaxValue,
            totalBatches,
            didFillExtra,
            nextMin,
            nextExtra,
            nextLeft
          }
        ])
      }
    }
  ]
}

const sumMinValue = sumBy('minValue')
const sumMaxValue = sumBy('maxValue')

function OrderingItemId ({ order, supplierCommitment }) {
  return `${order.id}_${supplierCommitment.id}`
}

function sumBy (name) {
  return flow(
    map(name),
    reduce(BigMath.add, '0')
  )
}
<commit_msg>big math<commit_after>const map = require('lodash/fp/map')
const assign = require('lodash/fp/assign')
const assignAll = require('lodash/fp/assignAll')
const mapValues = require('lodash/fp/mapValues')
const mapValuesWithKey = mapValues.convert({ cap: false })
const filter = require('lodash/fp/filter')
const flow = require('lodash/fp/flow')
const reduce = require('lodash/fp/reduce')
const sortBy = require('lodash/fp/sortBy')
const reverse = require('lodash/fp/reverse')
const keyBy = require('lodash/fp/keyBy')
const BigMath = require('bigmath')

module.exports = {
  needs: {
    'ordering.get.rawOrderingItems': 'first',
    'auth.get.whoami': 'first',
    'orders.get.orders': 'first',
    'supplierCommitments.get.supplierCommitments': 'first'
  },
  create: (api) => [
    api.ordering.get.rawOrderingItems,
    api.orders.get.orders,
    api.auth.get.whoami,
    (orderingItems, orders, whoami) => {
      const getItemsByOrders = mapValues(getItemsByOrder)
      const filterToSupplierCommitment = id => filter(o => o.supplierCommitmentId === id)

      return getItemsByOrders(orders)

      function getItemsByOrder (order) {
        const { supplierCommitments } = order
        const getItems = flow(
          map(supplierCommitment => {
            return getItem({ order, supplierCommitment })
          }),
          sortBy('totalMinValue'),
          reverse,
          keyBy('id')
        )

        return getItems(supplierCommitments)
      }

      function getItem ({ order, supplierCommitment }) {
        const { id: orderId } = order
        const orderingItemId = OrderingItemId({ order, supplierCommitment })
        const rawOrderingItem = orderingItems[orderingItemId]

        const allConsumerIntents = filterToSupplierCommitment(supplierCommitment.id)(order.allConsumerIntents)
        const myConsumerIntent = filterToSupplierCommitment(supplierCommitment.id)(order.myConsumerIntents)[0] || {
          agentId: whoami,
          supplierCommitmentId: supplierCommitment.id,
          orderId,
          minValue: '0',
          maxValue: '0'
        }
        const { currency } = order
        const { name, pluralName, batchSize } = supplierCommitment

        // TODO implement for real
        const expectedValue = BigMath.floor(BigMath.add(myConsumerIntent.minValue, BigMath.div(BigMath.sub(myConsumerIntent.maxValue, myConsumerIntent.minValue), 2)))
        const expectedCost = supplierCommitment.costFunction({ value: expectedValue, currency }) || 0

        const totalMinValue = sumMinValue(allConsumerIntents)
        const totalMaxValue = sumMaxValue(allConsumerIntents)
        const totalMinBatches = BigMath.floor(BigMath.div(totalMinValue, batchSize.value))
        const totalMinRemainder = BigMath.mod(totalMinValue, batchSize.value)
        const totalExtraValue = BigMath.sub(totalMaxValue, totalMinValue)
        const didFillExtra = BigMath.greaterThanOrEqualTo(BigMath.add(totalMinRemainder, totalExtraValue), batchSize.value)
        const totalBatches = BigMath.add(totalMinBatches, (didFillExtra ? '1' : '0'))

        const nextMin = didFillExtra ? '0' : totalMinRemainder
        const nextExtra = didFillExtra ? '0' : totalExtraValue
        const nextLeft = BigMath.sub(BigMath.sub(batchSize.value, nextExtra), nextMin)

        return assignAll([
          {
            isExpanded: false
          },
          rawOrderingItem,
          {
            id: orderingItemId,
            supplierCommitment,
            name,
            pluralName,
            batchSize,
            allConsumerIntents,
            myConsumerIntent,
            expectedValue,
            expectedCost,
            currency,
            totalMinValue,
            totalMaxValue,
            totalBatches,
            didFillExtra,
            nextMin,
            nextExtra,
            nextLeft
          }
        ])
      }
    }
  ]
}

const sumMinValue = sumBy('minValue')
const sumMaxValue = sumBy('maxValue')

function OrderingItemId ({ order, supplierCommitment }) {
  return `${order.id}_${supplierCommitment.id}`
}

function sumBy (name) {
  return flow(
    map(name),
    reduce(BigMath.add, '0')
  )
}
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import fyndiqColors from 'fyndiq-styles-colors'

import styles from '../message.css'

const colors = {
  info: fyndiqColors.blue,
  confirm: fyndiqColors.green,
  warn: fyndiqColors.orange,
  error: fyndiqColors.red,
}

class Message extends React.Component {
  constructor(props) {
    super(props)

    this.remaining = this.props.timeout

    this.onMouseEnter = this.onMouseEnter.bind(this)
    this.onMouseLeave = this.onMouseLeave.bind(this)
  }

  componentWillMount() {
    this.resume()
  }

  componentWillUnmount() {
    clearTimeout(this.timeout)
  }

  onMouseEnter() {
    // Pause the timer
    this.pause()
  }

  onMouseLeave() {
    // Resume the timer
    this.resume()
  }

  pause() {
    clearTimeout(this.timeout)
    this.remaining -= new Date() - this.start

    // Timer should be at least 1s
    this.remaining = Math.max(this.remaining, 1000)
  }

  resume() {
    this.start = new Date()
    clearTimeout(this.timeout)
    this.timeout = setTimeout(this.props.onClose, this.remaining)
  }

  render() {
    const { type, children, icon } = this.props

    return (
      <div
        className={`
          ${styles.message}
          ${styles[`message--type_${type}`]}
        `}
        onMouseEnter={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
      >
        {icon && React.cloneElement(icon, { color: colors[type] })}
        {children}
      </div>
    )
  }
}

Message.propTypes = {
  type: PropTypes.oneOf(['info', 'confirm', 'warn', 'error']),
  children: PropTypes.node,
  icon: PropTypes.element,
  onClose: PropTypes.func,
  timeout: PropTypes.number,
}

Message.defaultProps = {
  type: 'info',
  children: null,
  icon: null,
  onClose: () => {},
  timeout: 5000,
}

export default Message
<commit_msg>:wrench: Use Date.now instead of new Date()<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import fyndiqColors from 'fyndiq-styles-colors'

import styles from '../message.css'

const colors = {
  info: fyndiqColors.blue,
  confirm: fyndiqColors.green,
  warn: fyndiqColors.orange,
  error: fyndiqColors.red,
}

class Message extends React.Component {
  constructor(props) {
    super(props)

    this.remaining = this.props.timeout

    this.onMouseEnter = this.onMouseEnter.bind(this)
    this.onMouseLeave = this.onMouseLeave.bind(this)
  }

  componentWillMount() {
    this.resume()
  }

  componentWillUnmount() {
    clearTimeout(this.timeout)
  }

  onMouseEnter() {
    // Pause the timer
    this.pause()
  }

  onMouseLeave() {
    // Resume the timer
    this.resume()
  }

  pause() {
    clearTimeout(this.timeout)
    this.remaining -= Date.now() - this.start

    // Timer should be at least 1s
    this.remaining = Math.max(this.remaining, 1000)
  }

  resume() {
    this.start = Date.now()
    clearTimeout(this.timeout)
    this.timeout = setTimeout(this.props.onClose, this.remaining)
  }

  render() {
    const { type, children, icon } = this.props

    return (
      <div
        className={`
          ${styles.message}
          ${styles[`message--type_${type}`]}
        `}
        onMouseEnter={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
      >
        {icon && React.cloneElement(icon, { color: colors[type] })}
        {children}
      </div>
    )
  }
}

Message.propTypes = {
  type: PropTypes.oneOf(['info', 'confirm', 'warn', 'error']),
  children: PropTypes.node,
  icon: PropTypes.element,
  onClose: PropTypes.func,
  timeout: PropTypes.number,
}

Message.defaultProps = {
  type: 'info',
  children: null,
  icon: null,
  onClose: () => {},
  timeout: 5000,
}

export default Message
<|endoftext|>