<commit_before>import { useEffect, useState } from 'haunted';
import { memooize } from './memoize';

const
	columnSymbol = Symbol('column'),
	verifyColumnSetup = columns => {
		let ok = true;
		const columnNames = columns.map(c => c.name);
		// Check if column names are set
		columns.forEach(column => {
			if (column.name != null) {
				return;
			}
			ok = false;
			// eslint-disable-next-line no-console
			console.error('The name attribute needs to be set on all columns! Missing on column', column);
		});

		columns.forEach(column => {
			if (columnNames.indexOf(column.name) === columnNames.lastIndexOf(column.name)) {
				return;
			}
			ok = false;
			// eslint-disable-next-line no-console
			console.error('The name attribute needs to be unique among all columns! Not unique on column', column);
		});
		return ok;
	},

	// eslint-disable-next-line max-lines-per-function
	domColumnsToConfig = (host, { enabledColumns }) => {
		const domColumns = host.shadowRoot.querySelector('#columnsSlot')
			.assignedElements({ flatten: true })
			.filter(child => child.isOmnitableColumn && !child.hidden);

		if (!verifyColumnSetup(domColumns)) {
			return [];
		}

		const columns = Array.isArray(enabledColumns)
			? domColumns.filter(column => enabledColumns.includes(column.name))
			: domColumns.filter(column => !column.disabled);

		// eslint-disable-next-line max-lines-per-function
		return columns.map(column => ({
			name: column.name,
			title: column.title,

			valuePath: column.valuePath ?? column.name,
			groupOn: column.groupOn ?? column.valuePath,
			sortOn: column.sortOn ?? column.valuePath,

			minWidth: column.minWidth,
			width: column.width,
			flex: column.flex,
			priority: column.priority,

			getString: column.getString,
			getComparableValue: column.getComparableValue,
			serializeFilter: column.serializeFilter,
			deserializeFilter: column.deserializeFilter,
			toXlsxValue: column.toXlsxValue,

			renderHeader: column.renderHeader,
			renderCell: column.renderCell,
			renderEditCell: column.renderEditCell,
			renderGroup: column.renderGroup,
			cellTitleFn: column.cellTitleFn,
			getFilterFn: column.getFilterFn,
			headerCellClass: column.headerCellClass,
			cellClass: column.cellClass,

			editable: column.editable,

			values: column.values,
			source: memooize(column.computeSource),

			// @deprecated
			loading: column.loading,
			externalValues: column.externalValues,
			computeSource: column.computeSource,

			// boolean columns
			trueLabel: column.trueLabel,
			falseLabel: column.falseLabel,

			// list columns
			valueProperty: column.valueProperty,
			textProperty: column.textProperty,
			emptyLabel: column.emptyLabel,
			emptyValue: column.emptyValue,

			// range columns
			min: column.min,
			max: column.max,
			locale: column.locale,
			autoupdate: column.autoupdate,

			// number columns
			maximumFractionDigits: column.maximumFractionDigits,
			minimumFractionDigits: column.minimumFractionDigits,

			// amount columns
			currency: column.currency,
			rates: column.rates,
			autodetect: column.autodetect,

			// treenode columns
			ownerTree: column.ownerTree,
			keyProperty: column.keyProperty,

			[columnSymbol]: column
		}));
	},

	useDOMColumns = (host, { enabledColumns }) => {
		const
			[columns, setColumns] = useState(() => domColumnsToConfig(host, { enabledColumns }));

		useEffect(() => {
			const
				slot = host.shadowRoot.querySelector('#columnsSlot'),
				handler = () => {
					host.suppressNextScrollReset();
					setColumns(domColumnsToConfig(host, { enabledColumns }));
				};

			slot.addEventListener('slotchange', handler);
			host.addEventListener('cosmoz-column-prop-changed', handler);
			return () => {
				slot.removeEventListener('slotchange', handler);
				host.removeEventListener('cosmoz-column-prop-changed', handler);
			};
		}, []);

		useEffect(() => setColumns(domColumnsToConfig(host, { enabledColumns })), [enabledColumns]);

		return columns;
	};

export {
	columnSymbol,
	useDOMColumns
};
<commit_msg>fix(use-dom-columns): changes to enabled columns are ignored<commit_after>import { useEffect, useState } from 'haunted';
import { memooize } from './memoize';

const
	columnSymbol = Symbol('column'),
	verifyColumnSetup = columns => {
		let ok = true;
		const columnNames = columns.map(c => c.name);
		// Check if column names are set
		columns.forEach(column => {
			if (column.name != null) {
				return;
			}
			ok = false;
			// eslint-disable-next-line no-console
			console.error('The name attribute needs to be set on all columns! Missing on column', column);
		});

		columns.forEach(column => {
			if (columnNames.indexOf(column.name) === columnNames.lastIndexOf(column.name)) {
				return;
			}
			ok = false;
			// eslint-disable-next-line no-console
			console.error('The name attribute needs to be unique among all columns! Not unique on column', column);
		});
		return ok;
	},

	// eslint-disable-next-line max-lines-per-function
	domColumnsToConfig = (host, { enabledColumns }) => {
		const domColumns = host.shadowRoot.querySelector('#columnsSlot')
			.assignedElements({ flatten: true })
			.filter(child => child.isOmnitableColumn && !child.hidden);

		if (!verifyColumnSetup(domColumns)) {
			return [];
		}

		const columns = Array.isArray(enabledColumns)
			? domColumns.filter(column => enabledColumns.includes(column.name))
			: domColumns.filter(column => !column.disabled);

		// eslint-disable-next-line max-lines-per-function
		return columns.map(column => ({
			name: column.name,
			title: column.title,

			valuePath: column.valuePath ?? column.name,
			groupOn: column.groupOn ?? column.valuePath,
			sortOn: column.sortOn ?? column.valuePath,

			minWidth: column.minWidth,
			width: column.width,
			flex: column.flex,
			priority: column.priority,

			getString: column.getString,
			getComparableValue: column.getComparableValue,
			serializeFilter: column.serializeFilter,
			deserializeFilter: column.deserializeFilter,
			toXlsxValue: column.toXlsxValue,

			renderHeader: column.renderHeader,
			renderCell: column.renderCell,
			renderEditCell: column.renderEditCell,
			renderGroup: column.renderGroup,
			cellTitleFn: column.cellTitleFn,
			getFilterFn: column.getFilterFn,
			headerCellClass: column.headerCellClass,
			cellClass: column.cellClass,

			editable: column.editable,

			values: column.values,
			source: memooize(column.computeSource),

			// @deprecated
			loading: column.loading,
			externalValues: column.externalValues,
			computeSource: column.computeSource,

			// boolean columns
			trueLabel: column.trueLabel,
			falseLabel: column.falseLabel,

			// list columns
			valueProperty: column.valueProperty,
			textProperty: column.textProperty,
			emptyLabel: column.emptyLabel,
			emptyValue: column.emptyValue,

			// range columns
			min: column.min,
			max: column.max,
			locale: column.locale,
			autoupdate: column.autoupdate,

			// number columns
			maximumFractionDigits: column.maximumFractionDigits,
			minimumFractionDigits: column.minimumFractionDigits,

			// amount columns
			currency: column.currency,
			rates: column.rates,
			autodetect: column.autodetect,

			// treenode columns
			ownerTree: column.ownerTree,
			keyProperty: column.keyProperty,

			[columnSymbol]: column
		}));
	},

	useDOMColumns = (host, { enabledColumns }) => {
		const
			[columns, setColumns] = useState(() => domColumnsToConfig(host, { enabledColumns }));

		useEffect(() => {
			const
				slot = host.shadowRoot.querySelector('#columnsSlot'),
				handler = () => {
					host.suppressNextScrollReset();
					setColumns(domColumnsToConfig(host, { enabledColumns }));
				};

			handler();
			slot.addEventListener('slotchange', handler);
			host.addEventListener('cosmoz-column-prop-changed', handler);
			return () => {
				slot.removeEventListener('slotchange', handler);
				host.removeEventListener('cosmoz-column-prop-changed', handler);
			};
		}, [enabledColumns]);

		return columns;
	};

export {
	columnSymbol,
	useDOMColumns
};
<|endoftext|>


<commit_before>var genericPool = require('generic-pool'),
	nconf = require('nconf'),
	redis = require('redis');

//noinspection JSValidateTypes
exports.redis = genericPool.Pool({
	name: 'redis',
	create: function (callback) {
		var client = redis.createClient(nconf.get('redis:port'), nconf.get('redis:hostname')),
			db = nconf.get('redis:db'),
			auth = nconf.get('redis:auth');

		if (auth) {
			client.auth(auth);
		}

		client.on('connect', function () {
			if (db) {
				client.select(db);
			}

			console.log('CONNECTED TO REDIS');

			callback(null, client);
		});

		client.on('disconnect', function () {
			console.log('DISCONNECTED TO REDIS');
		});
	},
	destroy: function (client) {
		client.quit();
	},
	max: 50,
	idleTimeoutMillis: 5000,
	log: false
});<commit_msg>Tweaked pool logs<commit_after>var genericPool = require('generic-pool'),
	nconf = require('nconf'),
	redis = require('redis');

//noinspection JSValidateTypes
exports.redis = genericPool.Pool({
	name: 'redis',
	create: function (callback) {
		var client = redis.createClient(nconf.get('redis:port'), nconf.get('redis:hostname')),
			db = nconf.get('redis:db'),
			auth = nconf.get('redis:auth');

		if (auth) {
			client.auth(auth);
		}

		client.on('connect', function () {
			if (db) {
				client.select(db);
			}

			console.log('CONNECTED TO REDIS');

			callback(null, client);
		});

		client.on('end', function () {
			console.log('DISCONNECTED TO REDIS');
		});
	},
	destroy: function (client) {
		client.quit();
	},
	max: 50,
	idleTimeoutMillis: 5000,
	log: false
});<|endoftext|>


<commit_before>var require = (function() {
    var appVersion = '3.1.1';
    return {
        config: {
            'config/config': {
                'appVersion' : appVersion
            }
        },
        baseUrl: 'js',
        paths: {
            'vie' : '../lib/VIE/vie',
            'backbone' : '../lib/backbone/backbone-min',
            'underscore' : '../lib/underscore/underscore-min',
            'jquery' : '../lib/jquery/js/jquery-1.9.1',
            'jquery-ui' : '../lib/jquery/js/jquery-ui-1.10.3.custom.min',
            'jquery-cookie' : '../lib/jquery-cookie/jquery.cookie',
            'logger' : '../lib/logger',
            'chap-timeline' : '../lib/chap-links-library/timeline',
            'svg' : '../lib/svgjs/svg',
            'svg.draggable' : '../lib/svgjs/svg.draggable',
            'svg.foreignobject' : '../lib/svgjs/svg.foreignobject',
            'organize' : '../lib/organize/organize',
            'text' : '../lib/text',
            'bootstrap' : '../lib/bootstrap/js/bootstrap',
            'spin' : '../lib/spinjs/spin.min'
        },
        shim: {
            'logger' : {
                'exports' : 'Logger'
            },
            'vie' : {
                'deps' : ['backbone', 'jquery', 'underscore'],
                'exports' : 'VIE'
            },
            'backbone' : {
                'deps': ['underscore', 'jquery'],
                'exports' : 'Backbone'
            },
            'underscore' : {
                'exports' : '_'
            },
            'organize' : {
                'deps' : ['svg', 'svg.draggable', 'svg.foreignobject', 'jquery'],
                'exports' : 'Organize'
            },
            'chap-timeline' : {
                'exports': 'links.Timeline'
            },
            'svg.draggable' : ['svg'],
            'svg.foreignobject' : ['svg'],
            'jquery-cookie' : {
                'deps' : ['jquery']
            },
            'jquery-ui' : {
                'deps' : ['jquery']
            },
            'bootstrap' : {
                'deps' : ['jquery']
            },
            'spin' : {
                'exports' : 'Spinner'
            }
        },
        urlArgs: "bust=" + (new Date()).getTime()
    };
}());

<commit_msg>Changed version to reflect what it should be after releasing.<commit_after>var require = (function() {
    var appVersion = '3.2.0-pre';
    return {
        config: {
            'config/config': {
                'appVersion' : appVersion
            }
        },
        baseUrl: 'js',
        paths: {
            'vie' : '../lib/VIE/vie',
            'backbone' : '../lib/backbone/backbone-min',
            'underscore' : '../lib/underscore/underscore-min',
            'jquery' : '../lib/jquery/js/jquery-1.9.1',
            'jquery-ui' : '../lib/jquery/js/jquery-ui-1.10.3.custom.min',
            'jquery-cookie' : '../lib/jquery-cookie/jquery.cookie',
            'logger' : '../lib/logger',
            'chap-timeline' : '../lib/chap-links-library/timeline',
            'svg' : '../lib/svgjs/svg',
            'svg.draggable' : '../lib/svgjs/svg.draggable',
            'svg.foreignobject' : '../lib/svgjs/svg.foreignobject',
            'organize' : '../lib/organize/organize',
            'text' : '../lib/text',
            'bootstrap' : '../lib/bootstrap/js/bootstrap',
            'spin' : '../lib/spinjs/spin.min'
        },
        shim: {
            'logger' : {
                'exports' : 'Logger'
            },
            'vie' : {
                'deps' : ['backbone', 'jquery', 'underscore'],
                'exports' : 'VIE'
            },
            'backbone' : {
                'deps': ['underscore', 'jquery'],
                'exports' : 'Backbone'
            },
            'underscore' : {
                'exports' : '_'
            },
            'organize' : {
                'deps' : ['svg', 'svg.draggable', 'svg.foreignobject', 'jquery'],
                'exports' : 'Organize'
            },
            'chap-timeline' : {
                'exports': 'links.Timeline'
            },
            'svg.draggable' : ['svg'],
            'svg.foreignobject' : ['svg'],
            'jquery-cookie' : {
                'deps' : ['jquery']
            },
            'jquery-ui' : {
                'deps' : ['jquery']
            },
            'bootstrap' : {
                'deps' : ['jquery']
            },
            'spin' : {
                'exports' : 'Spinner'
            }
        },
        urlArgs: "bust=" + (new Date()).getTime()
    };
}());

<|endoftext|>


<commit_before>'use strict';
const fs = require('fs');
const path = require('path');
const async = require('async');
const detectConflict = require('detect-conflict');
const _ = require('lodash');
const typedError = require('error/typed');
const binaryDiff = require('./binary-diff');

const AbortedError = typedError({
  type: 'AbortedError',
  message: 'Process aborted by user'
});

const ConflictError = typedError({
  type: 'ConflicterConflict',
  message: 'Process aborted by conflict'
});

/**
 * The Conflicter is a module that can be used to detect conflict between files. Each
 * Generator file system helpers pass files through this module to make sure they don't
 * break a user file.
 *
 * When a potential conflict is detected, we prompt the user and ask them for
 * confirmation before proceeding with the actual write.
 *
 * @constructor
 * @property {Boolean} force - same as the constructor argument
 *
 * @param  {TerminalAdapter} adapter - The generator adapter
 * @param  {Boolean} force - When set to true, we won't check for conflict. (the
 *                           conflicter become a passthrough)
 * @param  {Boolean} bail - When set to true, we will abort on first conflict. (used for
 *                           testing reproducibility)
 */
class Conflicter {
  constructor(adapter, force, bail = false) {
    this.force = force === true;
    this.bail = bail === true;
    this.adapter = adapter;
    this.conflicts = [];
  }

  /**
   * Add a file to conflicter queue
   *
   * @param {String} filepath - File destination path
   * @param {String} contents - File new contents
   * @param {Function} callback - callback to be called once we know if the user want to
   *                              proceed or not.
   */
  checkForCollision(filepath, contents, callback) {
    this.conflicts.push({
      file: {
        path: path.resolve(filepath),
        contents
      },
      callback
    });
  }

  /**
   * Process the _potential conflict_ queue and ask the user to resolve conflict when they
   * occur
   *
   * The user is presented with the following options:
   *
   *   - `Y` Yes, overwrite
   *   - `n` No, do not overwrite
   *   - `a` All, overwrite this and all others
   *   - `x` Exit, abort
   *   - `d` Diff, show the differences between the old and the new
   *   - `h` Help, show this help
   *
   * @param  {Function} cb Callback once every conflict are resolved. (note that each
   *                       file can specify it's own callback. See `#checkForCollision()`)
   */
  resolve(cb) {
    cb = cb || (() => {});

    const resolveConflicts = conflict => {
      return next => {
        if (!conflict) {
          next();
          return;
        }

        this.collision(conflict.file, status => {
          // Remove the resolved conflict from the queue
          _.pull(this.conflicts, conflict);
          conflict.callback(null, status);
          next();
        });
      };
    };

    async.series(this.conflicts.map(resolveConflicts), cb.bind(this));
  }

  /**
   * Check if a file conflict with the current version on the user disk
   *
   * A basic check is done to see if the file exists, if it does:
   *
   *   1. Read its content from  `fs`
   *   2. Compare it with the provided content
   *   3. If identical, mark it as is and skip the check
   *   4. If diverged, prepare and show up the file collision menu
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   * @param  {Function} cb Callback receiving a status string ('identical', 'create',
   *                       'skip', 'force')
   */
  collision(file, cb) {
    const rfilepath = path.relative(process.cwd(), file.path);

    if (!fs.existsSync(file.path)) {
      this.adapter.log.create(rfilepath);
      cb('create');
      return;
    }

    if (this.force) {
      this.adapter.log.force(rfilepath);
      cb('force');
      return;
    }

    if (detectConflict(file.path, file.contents)) {
      this.adapter.log.conflict(rfilepath);
      if (this.bail) {
        this.adapter.log.writeln('Aborting ...');
        throw new ConflictError();
      }

      this._ask(file, cb);
    } else {
      this.adapter.log.identical(rfilepath);
      cb('identical');
    }
  }

  /**
   * Actual prompting logic
   * @private
   * @param {Object} file vinyl file object
   * @param {Function} cb callback receiving the next action
   */
  _ask(file, cb) {
    const rfilepath = path.relative(process.cwd(), file.path);
    const prompt = {
      name: 'action',
      type: 'expand',
      message: `Overwrite ${rfilepath}?`,
      choices: [
        {
          key: 'y',
          name: 'overwrite',
          value: 'write'
        },
        {
          key: 'n',
          name: 'do not overwrite',
          value: 'skip'
        },
        {
          key: 'a',
          name: 'overwrite this and all others',
          value: 'force'
        },
        {
          key: 'x',
          name: 'abort',
          value: 'abort'
        }
      ]
    };

    // Only offer diff option for files
    if (fs.statSync(file.path).isFile()) {
      prompt.choices.push({
        key: 'd',
        name: 'show the differences between the old and the new',
        value: 'diff'
      });
    }

    this.adapter.prompt([prompt], result => {
      if (result.action === 'abort') {
        this.adapter.log.writeln('Aborting ...');
        throw new AbortedError();
      }

      if (result.action === 'diff') {
        if (binaryDiff.isBinary(file.path, file.contents)) {
          this.adapter.log.writeln(binaryDiff.diff(file.path, file.contents));
        } else {
          const existing = fs.readFileSync(file.path);
          this.adapter.diff(existing.toString(), (file.contents || '').toString());
        }

        return this._ask(file, cb);
      }

      if (result.action === 'force') {
        this.force = true;
      }

      if (result.action === 'write') {
        result.action = 'force';
      }

      this.adapter.log[result.action || 'force'](rfilepath);
      return cb(result.action);
    });
  }
}

module.exports = Conflicter;
<commit_msg>Print diff on bailling. (#1126)<commit_after>'use strict';
const fs = require('fs');
const path = require('path');
const async = require('async');
const detectConflict = require('detect-conflict');
const _ = require('lodash');
const typedError = require('error/typed');
const binaryDiff = require('./binary-diff');

const AbortedError = typedError({
  type: 'AbortedError',
  message: 'Process aborted by user'
});

const ConflictError = typedError({
  type: 'ConflicterConflict',
  message: 'Process aborted by conflict'
});

/**
 * The Conflicter is a module that can be used to detect conflict between files. Each
 * Generator file system helpers pass files through this module to make sure they don't
 * break a user file.
 *
 * When a potential conflict is detected, we prompt the user and ask them for
 * confirmation before proceeding with the actual write.
 *
 * @constructor
 * @property {Boolean} force - same as the constructor argument
 *
 * @param  {TerminalAdapter} adapter - The generator adapter
 * @param  {Boolean} force - When set to true, we won't check for conflict. (the
 *                           conflicter become a passthrough)
 * @param  {Boolean} bail - When set to true, we will abort on first conflict. (used for
 *                           testing reproducibility)
 */
class Conflicter {
  constructor(adapter, force, bail = false) {
    this.force = force === true;
    this.bail = bail === true;
    this.adapter = adapter;
    this.conflicts = [];
  }

  /**
   * Add a file to conflicter queue
   *
   * @param {String} filepath - File destination path
   * @param {String} contents - File new contents
   * @param {Function} callback - callback to be called once we know if the user want to
   *                              proceed or not.
   */
  checkForCollision(filepath, contents, callback) {
    this.conflicts.push({
      file: {
        path: path.resolve(filepath),
        contents
      },
      callback
    });
  }

  /**
   * Process the _potential conflict_ queue and ask the user to resolve conflict when they
   * occur
   *
   * The user is presented with the following options:
   *
   *   - `Y` Yes, overwrite
   *   - `n` No, do not overwrite
   *   - `a` All, overwrite this and all others
   *   - `x` Exit, abort
   *   - `d` Diff, show the differences between the old and the new
   *   - `h` Help, show this help
   *
   * @param  {Function} cb Callback once every conflict are resolved. (note that each
   *                       file can specify it's own callback. See `#checkForCollision()`)
   */
  resolve(cb) {
    cb = cb || (() => {});

    const resolveConflicts = conflict => {
      return next => {
        if (!conflict) {
          next();
          return;
        }

        this.collision(conflict.file, status => {
          // Remove the resolved conflict from the queue
          _.pull(this.conflicts, conflict);
          conflict.callback(null, status);
          next();
        });
      };
    };

    async.series(this.conflicts.map(resolveConflicts), cb.bind(this));
  }

  /**
   * Print the file differences to console
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   */
  _printDiff(file) {
    if (binaryDiff.isBinary(file.path, file.contents)) {
      this.adapter.log.writeln(binaryDiff.diff(file.path, file.contents));
    } else {
      const existing = fs.readFileSync(file.path);
      this.adapter.diff(existing.toString(), (file.contents || '').toString());
    }
  }

  /**
   * Check if a file conflict with the current version on the user disk
   *
   * A basic check is done to see if the file exists, if it does:
   *
   *   1. Read its content from  `fs`
   *   2. Compare it with the provided content
   *   3. If identical, mark it as is and skip the check
   *   4. If diverged, prepare and show up the file collision menu
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   * @param  {Function} cb Callback receiving a status string ('identical', 'create',
   *                       'skip', 'force')
   */
  collision(file, cb) {
    const rfilepath = path.relative(process.cwd(), file.path);

    if (!fs.existsSync(file.path)) {
      this.adapter.log.create(rfilepath);
      cb('create');
      return;
    }

    if (this.force) {
      this.adapter.log.force(rfilepath);
      cb('force');
      return;
    }

    if (detectConflict(file.path, file.contents)) {
      this.adapter.log.conflict(rfilepath);
      if (this.bail) {
        this._printDiff(file);
        this.adapter.log.writeln('Aborting ...');
        throw new ConflictError();
      }

      this._ask(file, cb);
    } else {
      this.adapter.log.identical(rfilepath);
      cb('identical');
    }
  }

  /**
   * Actual prompting logic
   * @private
   * @param {Object} file vinyl file object
   * @param {Function} cb callback receiving the next action
   */
  _ask(file, cb) {
    const rfilepath = path.relative(process.cwd(), file.path);
    const prompt = {
      name: 'action',
      type: 'expand',
      message: `Overwrite ${rfilepath}?`,
      choices: [
        {
          key: 'y',
          name: 'overwrite',
          value: 'write'
        },
        {
          key: 'n',
          name: 'do not overwrite',
          value: 'skip'
        },
        {
          key: 'a',
          name: 'overwrite this and all others',
          value: 'force'
        },
        {
          key: 'x',
          name: 'abort',
          value: 'abort'
        }
      ]
    };

    // Only offer diff option for files
    if (fs.statSync(file.path).isFile()) {
      prompt.choices.push({
        key: 'd',
        name: 'show the differences between the old and the new',
        value: 'diff'
      });
    }

    this.adapter.prompt([prompt], result => {
      if (result.action === 'abort') {
        this.adapter.log.writeln('Aborting ...');
        throw new AbortedError();
      }

      if (result.action === 'diff') {
        this._printDiff(file);

        return this._ask(file, cb);
      }

      if (result.action === 'force') {
        this.force = true;
      }

      if (result.action === 'write') {
        result.action = 'force';
      }

      this.adapter.log[result.action || 'force'](rfilepath);
      return cb(result.action);
    });
  }
}

module.exports = Conflicter;
<|endoftext|>


<commit_before>import React from 'react';
import { interpolate } from 'd3-interpolate';
import * as Easing from 'd3-ease';

class Motion extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.move = this.move.bind(this);
        this.state = {items: []};
    }
    componentWillReceiveProps() {
        if (!this.moveId)
            this.moveId = requestAnimationFrame(this.move);
    }
    componentDidMount() {
        this.moveId = requestAnimationFrame(this.move)
    }
    componentWillUnmount() {
        cancelAnimationFrame(this.moveId);
    }
    move() {
        this.setState(({items: prevItems}) => {
            var {data, enter, leave, update, getKey, duration, easing, onRest} = this.props;
            var tick = performance.now();
            var dataByKey = data.reduce((acc, item) => ({...acc, [getKey(item)]: item}), {});
            var itemsByKey = prevItems.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var items = prevItems
                .map(item => {
                    var nextItem = {key: item.key, data: item.data, tick};
                    nextItem.end = !dataByKey[item.key] ? leave(item.data) : update(dataByKey[item.key]);
                    var unchanged = this.areEqual(item.end, nextItem.end);
                    if (unchanged) {
                        nextItem.rest = item.progress === 1;
                        nextItem.start = item.start;
                        var progressDelta = (nextItem.tick - item.tick) / duration(item.data);
                        nextItem.progress = Math.min(item.progress + progressDelta, 1);
                        nextItem.interpolators = item.interpolators;
                    } else {
                        nextItem.rest = false;
                        var reverse = !unchanged && this.areEqual(item.start, nextItem.end);
                        nextItem.start = reverse ? item.end : item.style;
                        nextItem.progress = reverse ? 1 - item.progress : 0;
                        nextItem.interpolators = this.getInterpolators(nextItem);
                    }
                    nextItem.style = this.interpolateStyle(nextItem, easing(item.data));
                    if (onRest && nextItem.rest && !item.rest) {
                        onRest(item.data);
                    }
                    return nextItem;
                })
                .filter(item => !item.rest || dataByKey[item.key])
                .concat(data
                    .filter(item => !itemsByKey[getKey(item)])
                    .map(item => {
                        var newItem = {key: getKey(item), data: item, progress: 0, tick, rest: false};
                        newItem.start = newItem.style = enter(item);
                        newItem.end = update(item);
                        newItem.interpolators = this.getInterpolators(newItem);
                        return newItem;
                    })
                );
            this.moveId = null;
            if (items.filter(({rest}) => !rest).length !== 0)
                this.moveId = requestAnimationFrame(this.move);
            return {items};
        })
    }
    areEqual(from, to) {
        if (Object.keys(from).length !== Object.keys(to).length)
            return false;
        for(var key in from) {
            if (from[key] !== to[key])
                return false;
        }
        return true;
    }
    getInterpolators({start, end}) {
        var interpolators = {};
        for(var key in start)
            interpolators[key] = interpolate(start[key], end[key]);
        return interpolators;
    }
    interpolateStyle({interpolators, end, progress}, easing) {
        var style = {};
        for(var key in end)
            style[key] = interpolators[key](Easing[easing](progress))
        return style;
    }
    render() {
        return this.props.children(this.state.items);
    }
}

export default Motion;
<commit_msg>Tweaked format<commit_after>import React from 'react';
import { interpolate } from 'd3-interpolate';
import * as Easing from 'd3-ease';

class Motion extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.move = this.move.bind(this);
        this.state = {items: []};
    }
    componentWillReceiveProps() {
        if (!this.moveId)
            this.moveId = requestAnimationFrame(this.move);
    }
    componentDidMount() {
        this.moveId = requestAnimationFrame(this.move)
    }
    componentWillUnmount() {
        cancelAnimationFrame(this.moveId);
    }
    move() {
        this.setState(({items: prevItems}) => {
            var {data, enter, leave, update, getKey, duration, easing, onRest} = this.props;
            var tick = performance.now();
            var dataByKey = data.reduce((acc, item) => ({...acc, [getKey(item)]: item}), {});
            var itemsByKey = prevItems.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var items = prevItems
                .map(item => {
                    var nextItem = {key: item.key, data: item.data, tick};
                    nextItem.end = !dataByKey[item.key] ? leave(item.data) : update(dataByKey[item.key]);
                    var unchanged = this.areEqual(item.end, nextItem.end);
                    if (unchanged) {
                        nextItem.start = item.start;
                        nextItem.rest = item.progress === 1;
                        var progressDelta = (nextItem.tick - item.tick) / duration(item.data);
                        nextItem.progress = Math.min(item.progress + progressDelta, 1);
                        nextItem.interpolators = item.interpolators;
                    } else {
                        nextItem.rest = false;
                        var reverse = !unchanged && this.areEqual(item.start, nextItem.end);
                        nextItem.start = reverse ? item.end : item.style;
                        nextItem.progress = reverse ? 1 - item.progress : 0;
                        nextItem.interpolators = this.getInterpolators(nextItem);
                    }
                    nextItem.style = this.interpolateStyle(nextItem, easing(item.data));
                    if (onRest && nextItem.rest && !item.rest) {
                        onRest(item.data);
                    }
                    return nextItem;
                })
                .filter(item => !item.rest || dataByKey[item.key])
                .concat(data
                    .filter(item => !itemsByKey[getKey(item)])
                    .map(item => {
                        var newItem = {key: getKey(item), data: item, progress: 0, tick, rest: false};
                        newItem.start = newItem.style = enter(item);
                        newItem.end = update(item);
                        newItem.interpolators = this.getInterpolators(newItem);
                        return newItem;
                    })
                );
            this.moveId = null;
            if (items.filter(({rest}) => !rest).length !== 0)
                this.moveId = requestAnimationFrame(this.move);
            return {items};
        })
    }
    areEqual(from, to) {
        if (Object.keys(from).length !== Object.keys(to).length)
            return false;
        for(var key in from) {
            if (from[key] !== to[key])
                return false;
        }
        return true;
    }
    getInterpolators({start, end}) {
        var interpolators = {};
        for(var key in start)
            interpolators[key] = interpolate(start[key], end[key]);
        return interpolators;
    }
    interpolateStyle({interpolators, end, progress}, easing) {
        var style = {};
        for(var key in end)
            style[key] = interpolators[key](Easing[easing](progress))
        return style;
    }
    render() {
        return this.props.children(this.state.items);
    }
}

export default Motion;
<|endoftext|>


<commit_before>/* @flow */

import Promise from 'bluebird'
import chokidar from 'chokidar'
import find from 'lodash.find'
import fs from 'fs'
import mime from 'mime'
import path from 'path'

import * as checksumer from './checksumer'
import * as chokidarEvent from './chokidar_event'
import LocalEventBuffer from './event_buffer'
import logger from '../logger'
import * as metadata from '../metadata'
import Pouch from '../pouch'
import Prep from '../prep'
import { PendingMap } from '../utils/pending'
import { maxDate } from '../timestamp'

import type { Checksumer } from './checksumer'
import type { ChokidarFSEvent } from './chokidar_event'
import type { Callback } from '../utils/func'
import type { Pending } from '../utils/pending' // eslint-disable-line

const log = logger({
  component: 'LocalWatcher'
})
log.chokidar = log.child({
  component: 'Chokidar'
})

const EXECUTABLE_MASK = 1 << 6

const SIDE = 'local'

// This file contains the filesystem watcher that will trigger operations when
// a file or a folder is added/removed/changed locally.
// Operations will be added to the a common operation queue along with the
// remote operations triggered by the remoteEventWatcher.
class LocalWatcher {
  syncPath: string
  prep: Prep
  pouch: Pouch
  initialScan: ?{ids: string[]}
  pendingDeletions: PendingMap
  checksums: number
  checksumer: Checksumer
  watcher: any // chokidar
  buffer: LocalEventBuffer<ChokidarFSEvent>

  constructor (syncPath: string, prep: Prep, pouch: Pouch) {
    this.syncPath = syncPath
    this.prep = prep
    this.pouch = pouch
    const timeoutInMs = 1000 // TODO: Read from config
    this.buffer = new LocalEventBuffer(timeoutInMs, this.handleEvents)
    this.checksumer = checksumer.init()
  }

  // Start chokidar, the filesystem watcher
  // https://github.com/paulmillr/chokidar
  start () {
    log.debug('Starting...')

    // To detect which files&folders have been removed since the last run of
    // cozy-desktop, we keep all the paths seen by chokidar during its
    // initial scan in @paths to compare them with pouchdb database.
    this.initialScan = {ids: []}

    // A map of pending operations. It's used for detecting move operations,
    // as chokidar only reports adds and deletion. The key is the path (as
    // seen on the filesystem, not normalized as an _id), and the value is
    // an object, with at least a done method and a timeout value. The done
    // method can be used to finalized the pending operation (we are sure we
    // want to save the operation as it in pouchdb), and the timeout can be
    // cleared to cancel the operation (for example, a deletion is finally
    // seen as a part of a move operation).
    this.pendingDeletions = new PendingMap()

    // A counter of how many files are been read to compute a checksum right
    // now. It's useful because we can't do some operations when a checksum
    // is running, like deleting a file, because the checksum operation is
    // slow but needed to detect move operations.
    this.checksums = 0

    this.watcher = chokidar.watch('.', {
      // Let paths in events be relative to this base path
      cwd: this.syncPath,
      // Ignore our own .system-tmp-cozy-drive directory
      ignored: /(^|[\/\\])\.system-tmp-cozy-drive/, // eslint-disable-line no-useless-escape
      // Don't follow symlinks
      followSymlinks: false,
      // The stats object is used in methods below
      alwaysStat: true,
      // Watching on Windows seems to lock dirs with subdirs, preventing them
      // from being renamed/moved/deleted.
      usePolling: (process.platform === 'win32'),
      // Filter out artifacts from editors with atomic writes
      atomic: true,
      // Poll newly created files to detect when the write is finished
      awaitWriteFinish: {
        pollInterval: 200,
        stabilityThreshold: 1000
      },
      // With node 0.10 on linux, only polling is available
      interval: 1000,
      binaryInterval: 2000
    })

    return new Promise((resolve) => {
      for (let eventType of ['add', 'addDir', 'change', 'unlink', 'unlinkDir']) {
        this.watcher.on(eventType, (path?: string, stats?: fs.Stats) => {
          log.chokidar.debug({path}, eventType)
          log.chokidar.trace({stats})
          const newEvent = chokidarEvent.build(eventType, path, stats)
          this.buffer.push(newEvent)
        })
      }

      this.watcher
        .on('ready', this.onReady(resolve))
        .on('error', (err) => {
          if (err.message === 'watch ENOSPC') {
            log.error('Sorry, the kernel is out of inotify watches! ' +
              'See doc/usage/inotify.md for how to solve this issue.')
          } else {
            log.error({err})
          }
        })

      log.info(`Now watching ${this.syncPath}`)
    })
  }

  handleEvents (events: ChokidarFSEvent[]) {
    log.debug(`Flushed ${events.length} events`)
    for (let e of events) {
      switch (e.type) {
        case 'add':
          this.onAddFile(e.path, e.stats)
          break
        case 'addDir':
          this.onAddDir(e.path, e.stats)
          break
        case 'change':
          this.onChange(e.path, e.stats)
          break
        case 'unlink':
          this.onUnlinkFile(e.path)
          break
        case 'unlinkDir':
          this.onUnlinkDir(e.path)
          break
        default:
          log.error(`Unknown event type: ${JSON.stringify(e.type)}`)
      }
    }
  }

  stop () {
    if (this.watcher) {
      this.watcher.close()
      this.watcher = null
    }
    this.buffer.switchMode('idle')
    this.pendingDeletions.executeAll()
    // Give some time for awaitWriteFinish events to be fired
    return new Promise((resolve) => {
      setTimeout(resolve, 3000)
    })
  }

  // Show watched paths
  debug () {
    if (this.watcher) {
      log.info('This is the list of the paths watched by chokidar:')
      const object = this.watcher.getWatched()
      for (let dir in object) {
        var file
        const files = object[dir]
        if (dir === '..') {
          for (file of Array.from(files)) {
            log.info(`- ${dir}/${file}`)
          }
        } else {
          if (dir !== '.') { log.info(`- ${dir}`) }
          for (file of Array.from(files)) {
            log.info(`  * ${file}`)
          }
        }
      }
      log.info('--------------------------------------------------')
    } else {
      log.warn('The file system is not currrently watched')
    }
  }

  /* Helpers */

  // An helper to create a document for a file
  // with checksum and mime informations
  createDoc (filePath: string, stats: fs.Stats, md5sum: string) {
    const mimeType = mime.lookup(filePath)
    const {mtime, ctime} = stats
    let doc: Object = {
      path: filePath,
      docType: 'file',
      md5sum,
      updated_at: maxDate(mtime, ctime),
      mime: mimeType,
      class: mimeType.split('/')[0],
      size: stats.size
    }
    if ((stats.mode & EXECUTABLE_MASK) !== 0) { doc.executable = true }
    return doc
  }

  async checksum (filePath: string): Promise<string> {
    const absPath = path.join(this.syncPath, filePath)
    return this.checksumer.push(absPath)
  }

  /* Actions */

  // New file detected
  onAddFile (filePath: string, stats: fs.Stats) {
    const logError = (err) => log.error({err, path: filePath})
    if (this.initialScan) { this.initialScan.ids.push(metadata.id(filePath)) }
    this.pendingDeletions.executeIfAny(filePath)
    this.checksums++
    this.checksum(filePath).asCallback((err, md5sum) => {
      if (err) {
        this.checksums--
        logError(err)
      } else {
        const doc = this.createDoc(filePath, stats, md5sum)
        if (this.pendingDeletions.isEmpty()) {
          this.checksums--
          log.info({path: filePath}, 'file added')
          this.prep.addFileAsync(SIDE, doc).catch(logError)
        } else {
          // Let's see if one of the pending deleted files has the
          // same checksum that the added file. If so, we mark them as
          // a move.
          this.pouch.byChecksum(doc.md5sum, (err, docs) => {
            this.checksums--
            if (err) {
              log.info({path: filePath}, 'file added')
              this.prep.addFileAsync(SIDE, doc).catch(logError)
            } else {
              const same = find(docs, this.initialScan
                  ? d => !fs.existsSync(d.path)
                  : d => this.pendingDeletions.hasPath(d.path))
              if (same) {
                log.info({path: filePath}, `was moved from ${same.path}`)
                this.pendingDeletions.clear(same.path)
                this.prep.moveFileAsync(SIDE, doc, same).catch(logError)
              } else {
                log.info({path: filePath}, 'file added')
                this.prep.addFileAsync(SIDE, doc).catch(logError)
              }
            }
          })
        }
      }
    })
  }

  // New directory detected
  onAddDir (folderPath: string, stats: fs.Stats) {
    if (folderPath === '') return

    if (this.initialScan) { this.initialScan.ids.push(metadata.id(folderPath)) }
    this.pendingDeletions.executeIfAny(folderPath)
    const doc = {
      path: folderPath,
      docType: 'folder',
      updated_at: stats.mtime
    }
    log.info({path: folderPath}, 'folder added')
    this.prep.putFolderAsync(SIDE, doc).catch(err => log.error({err, path: folderPath}))
  }

  // File deletion detected
  //
  // It can be a file moved out. So, we wait a bit to see if a file with the
  // same checksum is added and, if not, we declare this file as deleted.
  onUnlinkFile (filePath: string) {
    // TODO: Extract delayed execution logic to utils/pending
    let timeout
    const stopChecking = () => {
      clearTimeout(timeout)
    }
    const execute = () => {
      log.info({path: filePath}, 'File deleted')
      this.prep.trashFileAsync(SIDE, {path: filePath}).catch(err => log.error({err, path: filePath}))
    }
    const check = () => {
      if (this.checksums === 0) {
        this.pendingDeletions.executeIfAny(filePath)
      } else {
        timeout = setTimeout(check, 100)
      }
    }
    this.pendingDeletions.add(filePath, {stopChecking, execute})
    timeout = setTimeout(check, 1250)
  }

  // Folder deletion detected
  //
  // We don't want to delete a folder before files inside it. So we wait a bit
  // after chokidar event to declare the folder as deleted.
  onUnlinkDir (folderPath: string) {
    // TODO: Extract repeated check logic to utils/pending
    let interval
    const stopChecking = () => {
      clearInterval(interval)
    }
    const execute = () => {
      log.info({path: folderPath}, 'Folder deleted')
      this.prep.trashFolderAsync(SIDE, {path: folderPath}).catch(err => log.error({err, path: folderPath}))
    }
    const check = () => {
      if (!this.pendingDeletions.hasPendingChild(folderPath)) {
        this.pendingDeletions.executeIfAny(folderPath)
      }
    }
    this.pendingDeletions.add(folderPath, {stopChecking, execute})
    interval = setInterval(check, 350)
  }

  // File update detected
  onChange (filePath: string, stats: fs.Stats) {
    log.info({path: filePath}, 'File changed')
    this.checksum(filePath).asCallback((err, md5sum) => {
      if (err) {
        log.info({path: filePath, err})
      } else {
        const doc = this.createDoc(filePath, stats, md5sum)
        return this.prep.updateFileAsync(SIDE, doc)
      }
    })
  }

  // Called after chokidar has finished its initial scan
  onReady (callback: Callback) {
    return () => {
      this.buffer.switchMode('timeout')

      // Try to detect removed files & folders
      this.pouch.byRecursivePath('', async function (err, docs) {
        if (err) { return callback(err) }
        try {
          for (const doc of docs.reverse()) {
            // $FlowFixMe: initialScan cannot be null
            if (this.initialScan.ids.indexOf(metadata.id(doc.path)) !== -1 || doc.trashed) {
              continue
            } else if (doc.docType === 'file') {
              this.onUnlinkFile(doc.path)
            } else {
              this.onUnlinkDir(doc.path)
            }
          }
          delete this.initialScan
          setTimeout(callback, 3000)
        } catch (err) {
          callback(err)
        }
      }.bind(this))
    }
  }
}

export default LocalWatcher
<commit_msg>Make LocalWatcher#handleEvents() async<commit_after>/* @flow */

import Promise from 'bluebird'
import chokidar from 'chokidar'
import find from 'lodash.find'
import fs from 'fs'
import mime from 'mime'
import path from 'path'

import * as checksumer from './checksumer'
import * as chokidarEvent from './chokidar_event'
import LocalEventBuffer from './event_buffer'
import logger from '../logger'
import * as metadata from '../metadata'
import Pouch from '../pouch'
import Prep from '../prep'
import { PendingMap } from '../utils/pending'
import { maxDate } from '../timestamp'

import type { Checksumer } from './checksumer'
import type { ChokidarFSEvent } from './chokidar_event'
import type { Callback } from '../utils/func'
import type { Pending } from '../utils/pending' // eslint-disable-line

const log = logger({
  component: 'LocalWatcher'
})
log.chokidar = log.child({
  component: 'Chokidar'
})

const EXECUTABLE_MASK = 1 << 6

const SIDE = 'local'

// This file contains the filesystem watcher that will trigger operations when
// a file or a folder is added/removed/changed locally.
// Operations will be added to the a common operation queue along with the
// remote operations triggered by the remoteEventWatcher.
class LocalWatcher {
  syncPath: string
  prep: Prep
  pouch: Pouch
  initialScan: ?{ids: string[]}
  pendingDeletions: PendingMap
  checksums: number
  checksumer: Checksumer
  watcher: any // chokidar
  buffer: LocalEventBuffer<ChokidarFSEvent>

  constructor (syncPath: string, prep: Prep, pouch: Pouch) {
    this.syncPath = syncPath
    this.prep = prep
    this.pouch = pouch
    const timeoutInMs = 1000 // TODO: Read from config
    this.buffer = new LocalEventBuffer(timeoutInMs, this.handleEvents)
    this.checksumer = checksumer.init()
  }

  // Start chokidar, the filesystem watcher
  // https://github.com/paulmillr/chokidar
  start () {
    log.debug('Starting...')

    // To detect which files&folders have been removed since the last run of
    // cozy-desktop, we keep all the paths seen by chokidar during its
    // initial scan in @paths to compare them with pouchdb database.
    this.initialScan = {ids: []}

    // A map of pending operations. It's used for detecting move operations,
    // as chokidar only reports adds and deletion. The key is the path (as
    // seen on the filesystem, not normalized as an _id), and the value is
    // an object, with at least a done method and a timeout value. The done
    // method can be used to finalized the pending operation (we are sure we
    // want to save the operation as it in pouchdb), and the timeout can be
    // cleared to cancel the operation (for example, a deletion is finally
    // seen as a part of a move operation).
    this.pendingDeletions = new PendingMap()

    // A counter of how many files are been read to compute a checksum right
    // now. It's useful because we can't do some operations when a checksum
    // is running, like deleting a file, because the checksum operation is
    // slow but needed to detect move operations.
    this.checksums = 0

    this.watcher = chokidar.watch('.', {
      // Let paths in events be relative to this base path
      cwd: this.syncPath,
      // Ignore our own .system-tmp-cozy-drive directory
      ignored: /(^|[\/\\])\.system-tmp-cozy-drive/, // eslint-disable-line no-useless-escape
      // Don't follow symlinks
      followSymlinks: false,
      // The stats object is used in methods below
      alwaysStat: true,
      // Watching on Windows seems to lock dirs with subdirs, preventing them
      // from being renamed/moved/deleted.
      usePolling: (process.platform === 'win32'),
      // Filter out artifacts from editors with atomic writes
      atomic: true,
      // Poll newly created files to detect when the write is finished
      awaitWriteFinish: {
        pollInterval: 200,
        stabilityThreshold: 1000
      },
      // With node 0.10 on linux, only polling is available
      interval: 1000,
      binaryInterval: 2000
    })

    return new Promise((resolve) => {
      for (let eventType of ['add', 'addDir', 'change', 'unlink', 'unlinkDir']) {
        this.watcher.on(eventType, (path?: string, stats?: fs.Stats) => {
          log.chokidar.debug({path}, eventType)
          log.chokidar.trace({stats})
          const newEvent = chokidarEvent.build(eventType, path, stats)
          this.buffer.push(newEvent)
        })
      }

      this.watcher
        .on('ready', this.onReady(resolve))
        .on('error', (err) => {
          if (err.message === 'watch ENOSPC') {
            log.error('Sorry, the kernel is out of inotify watches! ' +
              'See doc/usage/inotify.md for how to solve this issue.')
          } else {
            log.error({err})
          }
        })

      log.info(`Now watching ${this.syncPath}`)
    })
  }

  async handleEvents (events: ChokidarFSEvent[]) {
    log.debug(`Flushed ${events.length} events`)
    for (let e of events) {
      try {
        switch (e.type) {
          case 'add':
            await this.onAddFile(e.path, e.stats)
            break
          case 'addDir':
            await this.onAddDir(e.path, e.stats)
            break
          case 'change':
            await this.onChange(e.path, e.stats)
            break
          case 'unlink':
            await this.onUnlinkFile(e.path)
            break
          case 'unlinkDir':
            await this.onUnlinkDir(e.path)
            break
          default:
            throw new TypeError(`Unknown event type: ${e.type}`)
        }
      } catch (err) {
        log.error({err, path: e.path})
        throw err
      }
    }
  }

  stop () {
    if (this.watcher) {
      this.watcher.close()
      this.watcher = null
    }
    this.buffer.switchMode('idle')
    this.pendingDeletions.executeAll()
    // Give some time for awaitWriteFinish events to be fired
    return new Promise((resolve) => {
      setTimeout(resolve, 3000)
    })
  }

  // Show watched paths
  debug () {
    if (this.watcher) {
      log.info('This is the list of the paths watched by chokidar:')
      const object = this.watcher.getWatched()
      for (let dir in object) {
        var file
        const files = object[dir]
        if (dir === '..') {
          for (file of Array.from(files)) {
            log.info(`- ${dir}/${file}`)
          }
        } else {
          if (dir !== '.') { log.info(`- ${dir}`) }
          for (file of Array.from(files)) {
            log.info(`  * ${file}`)
          }
        }
      }
      log.info('--------------------------------------------------')
    } else {
      log.warn('The file system is not currrently watched')
    }
  }

  /* Helpers */

  // An helper to create a document for a file
  // with checksum and mime informations
  createDoc (filePath: string, stats: fs.Stats, md5sum: string) {
    const mimeType = mime.lookup(filePath)
    const {mtime, ctime} = stats
    let doc: Object = {
      path: filePath,
      docType: 'file',
      md5sum,
      updated_at: maxDate(mtime, ctime),
      mime: mimeType,
      class: mimeType.split('/')[0],
      size: stats.size
    }
    if ((stats.mode & EXECUTABLE_MASK) !== 0) { doc.executable = true }
    return doc
  }

  async checksum (filePath: string): Promise<string> {
    const absPath = path.join(this.syncPath, filePath)
    return this.checksumer.push(absPath)
  }

  /* Actions */

  // New file detected
  onAddFile (filePath: string, stats: fs.Stats) {
    const logError = (err) => log.error({err, path: filePath})
    if (this.initialScan) { this.initialScan.ids.push(metadata.id(filePath)) }
    this.pendingDeletions.executeIfAny(filePath)
    this.checksums++
    this.checksum(filePath).asCallback((err, md5sum) => {
      if (err) {
        this.checksums--
        logError(err)
      } else {
        const doc = this.createDoc(filePath, stats, md5sum)
        if (this.pendingDeletions.isEmpty()) {
          this.checksums--
          log.info({path: filePath}, 'file added')
          this.prep.addFileAsync(SIDE, doc).catch(logError)
        } else {
          // Let's see if one of the pending deleted files has the
          // same checksum that the added file. If so, we mark them as
          // a move.
          this.pouch.byChecksum(doc.md5sum, (err, docs) => {
            this.checksums--
            if (err) {
              log.info({path: filePath}, 'file added')
              this.prep.addFileAsync(SIDE, doc).catch(logError)
            } else {
              const same = find(docs, this.initialScan
                  ? d => !fs.existsSync(d.path)
                  : d => this.pendingDeletions.hasPath(d.path))
              if (same) {
                log.info({path: filePath}, `was moved from ${same.path}`)
                this.pendingDeletions.clear(same.path)
                this.prep.moveFileAsync(SIDE, doc, same).catch(logError)
              } else {
                log.info({path: filePath}, 'file added')
                this.prep.addFileAsync(SIDE, doc).catch(logError)
              }
            }
          })
        }
      }
    })
  }

  // New directory detected
  onAddDir (folderPath: string, stats: fs.Stats) {
    if (folderPath === '') return

    if (this.initialScan) { this.initialScan.ids.push(metadata.id(folderPath)) }
    this.pendingDeletions.executeIfAny(folderPath)
    const doc = {
      path: folderPath,
      docType: 'folder',
      updated_at: stats.mtime
    }
    log.info({path: folderPath}, 'folder added')
    this.prep.putFolderAsync(SIDE, doc).catch(err => log.error({err, path: folderPath}))
  }

  // File deletion detected
  //
  // It can be a file moved out. So, we wait a bit to see if a file with the
  // same checksum is added and, if not, we declare this file as deleted.
  onUnlinkFile (filePath: string) {
    // TODO: Extract delayed execution logic to utils/pending
    let timeout
    const stopChecking = () => {
      clearTimeout(timeout)
    }
    const execute = () => {
      log.info({path: filePath}, 'File deleted')
      this.prep.trashFileAsync(SIDE, {path: filePath}).catch(err => log.error({err, path: filePath}))
    }
    const check = () => {
      if (this.checksums === 0) {
        this.pendingDeletions.executeIfAny(filePath)
      } else {
        timeout = setTimeout(check, 100)
      }
    }
    this.pendingDeletions.add(filePath, {stopChecking, execute})
    timeout = setTimeout(check, 1250)
  }

  // Folder deletion detected
  //
  // We don't want to delete a folder before files inside it. So we wait a bit
  // after chokidar event to declare the folder as deleted.
  onUnlinkDir (folderPath: string) {
    // TODO: Extract repeated check logic to utils/pending
    let interval
    const stopChecking = () => {
      clearInterval(interval)
    }
    const execute = () => {
      log.info({path: folderPath}, 'Folder deleted')
      this.prep.trashFolderAsync(SIDE, {path: folderPath}).catch(err => log.error({err, path: folderPath}))
    }
    const check = () => {
      if (!this.pendingDeletions.hasPendingChild(folderPath)) {
        this.pendingDeletions.executeIfAny(folderPath)
      }
    }
    this.pendingDeletions.add(folderPath, {stopChecking, execute})
    interval = setInterval(check, 350)
  }

  // File update detected
  onChange (filePath: string, stats: fs.Stats) {
    log.info({path: filePath}, 'File changed')
    this.checksum(filePath).asCallback((err, md5sum) => {
      if (err) {
        log.info({path: filePath, err})
      } else {
        const doc = this.createDoc(filePath, stats, md5sum)
        return this.prep.updateFileAsync(SIDE, doc)
      }
    })
  }

  // Called after chokidar has finished its initial scan
  onReady (callback: Callback) {
    return () => {
      this.buffer.switchMode('timeout')

      // Try to detect removed files & folders
      this.pouch.byRecursivePath('', async function (err, docs) {
        if (err) { return callback(err) }
        try {
          for (const doc of docs.reverse()) {
            // $FlowFixMe: initialScan cannot be null
            if (this.initialScan.ids.indexOf(metadata.id(doc.path)) !== -1 || doc.trashed) {
              continue
            } else if (doc.docType === 'file') {
              this.onUnlinkFile(doc.path)
            } else {
              this.onUnlinkDir(doc.path)
            }
          }
          delete this.initialScan
          setTimeout(callback, 3000)
        } catch (err) {
          callback(err)
        }
      }.bind(this))
    }
  }
}

export default LocalWatcher
<|endoftext|>


<commit_before>/* @flow */
import * as Pixi from 'pixi.js';
import Gob from './gob';
import Matter from 'matter-js';

import {Time} from './constants/private';
import {Utils} from './util';

import Loader from './loader'

type PoolOpts = Object;

export default class Pool {
  _id: number;
  stage: Pixi.Container;
  gobs: Array<Gob>;
  engine: Matter.Engine;
  renderer: Pixi.WebGLRenderer;
  tileSize: number;
  grid: Pixi.Graphics;

  fps: number;
  lastTime: number;
  fpsText: Pixi.Text;

  loader: Loader;
  resources: Object;
  audioContext: AudioContext;

  constructor(opts: PoolOpts): void {
    this._id = Utils.uuid();
    this.renderer =  new Pixi.WebGLRenderer(
      opts.width,
      opts.height,
      {view: opts.canvas},
    );
    this.renderer.backgroundColor = 0xFFFFFF;
    // TODO: tilesize hardcoded
    this.tileSize = 30;

    // array of gob objects
    this.gobs = [];

    this.stage = new Pixi.Container()

    this.engine = Matter.Engine.create();
    this.engine.world.gravity.x = 0;
    this.engine.world.gravity.y = 0;
    this._drawFPS();
    this._drawGrid(opts.showGrid);

    this.audioContext = new AudioContext();
    this.loader = new Loader(this.loaded, this.audioContext);
  }

  _drawFPS(): void {
    this.fps = 0;
    // TODO: move off Pixi
    this.fpsText = new Pixi.Text(Math.floor(this.fps),
      {fontFamily: 'Arial', fontSize: 12, fill: 0xff1010, align: 'center'});
    this.fpsText.position.set(0, 0);
    this.stage.addChild(this.fpsText);
  }

  _drawGrid(showGrid: bool): void {
    // draws a grid of given tilesize
    this.grid = new Pixi.Graphics();
    this.grid.moveTo(0, 0);
    this.grid.lineStyle(1, 0x336699, 0.3);
    for (let i = 0; i <= this.renderer.view.width; i += this.tileSize) {
      this.grid.moveTo(i, 0);
      this.grid.lineTo(i, this.renderer.view.height);
    }
    for (let i = 0; i < this.renderer.view.height; i += this.tileSize) {
      this.grid.moveTo(0, i);
      this.grid.lineTo(this.renderer.view.width, i);
    }
    if (!showGrid) {
      this.grid.visible = false;
    }
    this.stage.addChild(this.grid);
  }

  showGrid(): void {
    this.grid.visible = true;
  }

  hideGrid(): void {
    this.grid.visible = false;
  }

  // TODO: test this method
  // creates a gob of type GobClass
  // TODO: in the future allow gob-specific events? might be interesting for
  //       clicks. maybe see what phaser offers
  // TODO: OPTIMIZATION: allow batch create with a custom position, etc.
  //       function!
  createGob(opts: Object, GobClass: Class<Gob> = Gob): void {
    const gob: Gob = new GobClass();
    gob.__init(this, opts);

    // if it contains a collider, we need to put it into the collision engine,
    // regardless whether or not it is a rigid body
    if (gob.collider != null) {
      Matter.World.add(this.engine.world, gob.collider.body)
    }

    this.gobs.push(gob);
  }

  // TODO: test this method
  // for destroy() and cleanup
  removeGob(gob: Gob): void {
    // TODO: remove keyboard even handlers
    // this._keyboard.removeGobEventHandlers(gob._id, gob.events);
  }

  // TODO: EXPERIMENTAL: How do we handle allowing the definition of updates
  //                     from gobs themselves, as well as an overarching update?
  //                     Perhaps, have an internal method updateCanvas that
  //                     delegates and calls all the updates
  //
  // TODO: in the future, if game states are implemented, the Game object
  //       should shift between the states to determine which update methods
  //       are called
  updateCanvas(): void {
    // if the resources haven't been loaded yet, do not do anything!
    // TODO: support multiple states in the game
    if (this.resources == null) {
      return;
    }

    const currentTime: number = Date.now();

    this.fps = 1000 / (currentTime - this.lastTime);
    this.lastTime = currentTime;
    this.fpsText.text = Math.floor(this.fps);

    this.update();

    // because the spatial hash is now being regenerated on each update, we just
    // pass all the gobs to the ceng

    // TODO: Refactor this + above
    for (let i = 0; i < this.gobs.length; i++) {
      if (this.gobs[i].collider) {
        this.gobs[i].prePhysicsUpdate();
        this.gobs[i].__prePhysicsUpdate();
      }
    }

    // TODO: add ONLY THE ONES THAT CHANGED back to the spatial hash
    Matter.Engine.update(this.engine, Time.dtms);

    // TODO: Refactor this + above
    for (let i = 0; i < this.gobs.length; i++) {
      if (this.gobs[i].collider) {
        this.gobs[i].__postPhysicsUpdate();
        this.gobs[i].postPhysicsUpdate();
      }
    }

    this.gobs.map((gob) => {
      gob.__update();
      gob.update()
    })

    // sort by the new z depth
    this.stage.children.sort(
      (sprite1, sprite2) => {
        if (sprite1.zDepth < sprite2.zDepth) {
          return -1;
        }
        else if (sprite1.zDepth >= sprite2.zDepth) {
          return 1;
        }
        // null or undefined
        return 0;
      },
    );

    //
    // TODO: physics responses
    // TODO: need to update spatial hash post physics response
    this.renderer.render(this.stage);
  }

  // TODO: in the future, if game states are implemented, this should be found
  // in the game states. The Game object should shift between the states to
  // determine which update methods are called
  //
  // run through pool of objects and update positions based on the velocities
  // Should be defined/overwritten when extended
  update(): void {}

  preload(...gobs: Array<typeof Gob>) {
    const spritePromise = this.loader.loadSprites(...gobs);
    const audioPromise = this.loader.loadAudio(...gobs);

    spritePromise.then((resources) => {
      this.resources = resources;
    });
    Promise.all([spritePromise, audioPromise]).then(this.loaded).catch(
      (err) => console.log('Error loading audio sources: ', err)
    );
  }

  loaded = ([resources: Object, audioResources: Array<mixed>]: Array<mixed>) => {
    this.gobs.map((gob: Gob) => {
      gob.__onGameLoaded()
      // add the sprite to the stage
      this.stage.addChild(gob.sprite.pixi);
    })
  }
}
<commit_msg>fix asset preloading<commit_after>/* @flow */
import * as Pixi from 'pixi.js';
import Gob from './gob';
import Matter from 'matter-js';

import {Time} from './constants/private';
import {Utils} from './util';

import Loader from './loader'

type PoolOpts = Object;

export default class Pool {
  _id: number;
  stage: Pixi.Container;
  gobs: Array<Gob>;
  engine: Matter.Engine;
  renderer: Pixi.WebGLRenderer;
  tileSize: number;
  grid: Pixi.Graphics;

  fps: number;
  lastTime: number;
  fpsText: Pixi.Text;

  loader: Loader;
  resources: Object;
  audioContext: AudioContext;

  constructor(opts: PoolOpts): void {
    this._id = Utils.uuid();
    this.renderer =  new Pixi.WebGLRenderer(
      opts.width,
      opts.height,
      {view: opts.canvas},
    );
    this.renderer.backgroundColor = 0xFFFFFF;
    // TODO: tilesize hardcoded
    this.tileSize = 30;

    // array of gob objects
    this.gobs = [];

    this.stage = new Pixi.Container()

    this.engine = Matter.Engine.create();
    this.engine.world.gravity.x = 0;
    this.engine.world.gravity.y = 0;
    this._drawFPS();
    this._drawGrid(opts.showGrid);

    this.audioContext = new AudioContext();
    this.loader = new Loader(this.loaded, this.audioContext);
  }

  _drawFPS(): void {
    this.fps = 0;
    // TODO: move off Pixi
    this.fpsText = new Pixi.Text(Math.floor(this.fps),
      {fontFamily: 'Arial', fontSize: 12, fill: 0xff1010, align: 'center'});
    this.fpsText.position.set(0, 0);
    this.stage.addChild(this.fpsText);
  }

  _drawGrid(showGrid: bool): void {
    // draws a grid of given tilesize
    this.grid = new Pixi.Graphics();
    this.grid.moveTo(0, 0);
    this.grid.lineStyle(1, 0x336699, 0.3);
    for (let i = 0; i <= this.renderer.view.width; i += this.tileSize) {
      this.grid.moveTo(i, 0);
      this.grid.lineTo(i, this.renderer.view.height);
    }
    for (let i = 0; i < this.renderer.view.height; i += this.tileSize) {
      this.grid.moveTo(0, i);
      this.grid.lineTo(this.renderer.view.width, i);
    }
    if (!showGrid) {
      this.grid.visible = false;
    }
    this.stage.addChild(this.grid);
  }

  showGrid(): void {
    this.grid.visible = true;
  }

  hideGrid(): void {
    this.grid.visible = false;
  }

  // TODO: test this method
  // creates a gob of type GobClass
  // TODO: in the future allow gob-specific events? might be interesting for
  //       clicks. maybe see what phaser offers
  // TODO: OPTIMIZATION: allow batch create with a custom position, etc.
  //       function!
  createGob(opts: Object, GobClass: Class<Gob> = Gob): void {
    const gob: Gob = new GobClass();
    gob.__init(this, opts);

    // if it contains a collider, we need to put it into the collision engine,
    // regardless whether or not it is a rigid body
    if (gob.collider != null) {
      Matter.World.add(this.engine.world, gob.collider.body)
    }

    this.gobs.push(gob);
  }

  // TODO: test this method
  // for destroy() and cleanup
  removeGob(gob: Gob): void {
    // TODO: remove keyboard even handlers
    // this._keyboard.removeGobEventHandlers(gob._id, gob.events);
  }

  // TODO: EXPERIMENTAL: How do we handle allowing the definition of updates
  //                     from gobs themselves, as well as an overarching update?
  //                     Perhaps, have an internal method updateCanvas that
  //                     delegates and calls all the updates
  //
  // TODO: in the future, if game states are implemented, the Game object
  //       should shift between the states to determine which update methods
  //       are called
  updateCanvas(): void {
    // if the resources haven't been loaded yet, do not do anything!
    // TODO: support multiple states in the game
    if (this.resources == null) {
      return;
    }

    const currentTime: number = Date.now();

    this.fps = 1000 / (currentTime - this.lastTime);
    this.lastTime = currentTime;
    this.fpsText.text = Math.floor(this.fps);

    this.update();

    // because the spatial hash is now being regenerated on each update, we just
    // pass all the gobs to the ceng

    // TODO: Refactor this + above
    for (let i = 0; i < this.gobs.length; i++) {
      if (this.gobs[i].collider) {
        this.gobs[i].prePhysicsUpdate();
        this.gobs[i].__prePhysicsUpdate();
      }
    }

    // TODO: add ONLY THE ONES THAT CHANGED back to the spatial hash
    Matter.Engine.update(this.engine, Time.dtms);

    // TODO: Refactor this + above
    for (let i = 0; i < this.gobs.length; i++) {
      if (this.gobs[i].collider) {
        this.gobs[i].__postPhysicsUpdate();
        this.gobs[i].postPhysicsUpdate();
      }
    }

    this.gobs.map((gob) => {
      gob.__update();
      gob.update()
    })

    // sort by the new z depth
    this.stage.children.sort(
      (sprite1, sprite2) => {
        if (sprite1.zDepth < sprite2.zDepth) {
          return -1;
        }
        else if (sprite1.zDepth >= sprite2.zDepth) {
          return 1;
        }
        // null or undefined
        return 0;
      },
    );

    //
    // TODO: physics responses
    // TODO: need to update spatial hash post physics response
    this.renderer.render(this.stage);
  }

  // TODO: in the future, if game states are implemented, this should be found
  // in the game states. The Game object should shift between the states to
  // determine which update methods are called
  //
  // run through pool of objects and update positions based on the velocities
  // Should be defined/overwritten when extended
  update(): void {}

  preload(...gobs: Array<typeof Gob>) {
    const spritePromise = this.loader.loadSprites(...gobs);
    const audioPromise = this.loader.loadAudio(...gobs);

    Promise.all([spritePromise, audioPromise]).then(this.loaded).catch(
      (err) => console.log('Error loading audio sources: ', err)
    );
  }

  loaded = ([resources: Object, audioResources: Array<mixed>]: Array<mixed>) => {
    // TODO: report this bug
    // $FlowFixMe: not sure why it thinks that it's mixed
    this.resources = resources;
    this.gobs.map((gob: Gob) => {
      gob.__onGameLoaded()
      // add the sprite to the stage
      this.stage.addChild(gob.sprite.pixi);
    })
  }
}
<|endoftext|>


<commit_before>PlayState = {};

PlayState.preload = function() {
  this.game.load.json('level:1', 'data/level01.json');
  this.game.load.image('background', 'images/background.png');
  this.game.load.image('ground', 'images/ground.png');
  this.game.load.image('grass:8x1', 'images/grass_8x1.png');
  this.game.load.image('grass:6x1', 'images/grass_6x1.png');
  this.game.load.image('grass:4x1', 'images/grass_4x1.png');
  this.game.load.image('grass:2x1', 'images/grass_2x1.png');
  this.game.load.image('grass:1x1', 'images/grass_1x1.png');
};

PlayState.create = function() {
  this.game.add.image(0, 0, 'background');
  this._loadLevel(this.game.cache.getJSON('level:1'));
};

PlayState._loadLevel = function(data) {
  data.platforms.forEach(this._spawnPlatform, this);
};

PlayState._spawnPlatform = function(platform) {
  this.game.add.sprite(platform.x, platform.y, platform.image);
};

window.onload = function() {
  let game = new Phaser.Game(960, 600, Phaser.AUTO, 'game');
  game.state.add('play', PlayState);
  game.state.start('play');
};
<commit_msg>Add main character sprite<commit_after>function Hero(game, x, y) {
  Phaser.Sprite.call(this, game, x, y, 'hero');
  this.anchor.set(0.5, 0.5);
}

Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;

PlayState = {};

PlayState.preload = function() {
  this.game.load.json('level:1', 'data/level01.json');

  this.game.load.image('background', 'images/background.png');

  this.game.load.image('ground', 'images/ground.png');
  this.game.load.image('grass:8x1', 'images/grass_8x1.png');
  this.game.load.image('grass:6x1', 'images/grass_6x1.png');
  this.game.load.image('grass:4x1', 'images/grass_4x1.png');
  this.game.load.image('grass:2x1', 'images/grass_2x1.png');
  this.game.load.image('grass:1x1', 'images/grass_1x1.png');

  this.game.load.image('hero', 'images/hero_stopped.png');
};

PlayState.create = function() {
  this.game.add.image(0, 0, 'background');
  this._loadLevel(this.game.cache.getJSON('level:1'));
};

PlayState._loadLevel = function(data) {
  data.platforms.forEach(this._spawnPlatform, this);
  this._spawnCharaters({ hero: data.hero });
};

PlayState._spawnPlatform = function(platform) {
  this.game.add.sprite(platform.x, platform.y, platform.image);
};

PlayState._spawnCharaters = function(data) {
  this.hero = new Hero(this.game, data.hero.x, data.hero.y);
  this.game.add.existing(this.hero);
};

window.onload = function() {
  let game = new Phaser.Game(960, 600, Phaser.AUTO, 'game');
  game.state.add('play', PlayState);
  game.state.start('play');
};
<|endoftext|>


<commit_before>var logger = process.logging || require('./log');

var fs = require('fs');
var path = require('path');
var events = require('events');
var diff = require('diff');
var prompt = require('../actions/prompt').prompt;
var exists = fs.existsSync || path.existsSync;
var log = logger('conflicter');

var conflicter = module.exports = Object.create(events.EventEmitter.prototype);

conflicter.conflicts = [];

conflicter.add = function add(conflict) {
  if (typeof conflict === 'string') {
    conflict = {
      file: conflict,
      content: fs.readFileSync(conflict, 'utf8')
    };
  }

  if (!conflict.file) {
    throw new Error('Missing conflict.file option');
  }

  if (conflict.content === undefined) {
    throw new Error('Missing conflict.content option');
  }

  this.conflicts.push(conflict);
  return this;
};

conflicter.reset = function reset() {
  this.conflicts = [];
  return this;
};

conflicter.pop = function pop() {
  return this.conflicts.pop();
};

conflicter.shift = function shift() {
  return this.conflicts.shift();
};

conflicter.resolve = function resolve(cb) {
  var conflicts = this.conflicts;
  (function next(conflict) {
    if (!conflict) {
      return cb();
    }

    conflicter.collision(conflict.file, conflict.content, function (err, status) {
      if (err) {
        return cb(err);
      }

      conflicter.emit('resolved:' + conflict.file, status);
      next(conflicts.shift());
    });

  })(conflicts.shift());
};

conflicter.collision = function collision(filepath, content, cb) {
  var self = this;
  var menu = [
    ' ' + 'Y'.bold + '    yes         overwrite',
    ' ' + 'n'.bold + '    no          do not overwrite',
    ' ' + 'a'.bold + '    all         overwrite this and all others',
    ' ' + 'q'.bold + '    quit        abort',
    ' ' + 'd'.bold + '    diff        show the differences between the old and the new',
    ' ' + 'h'.bold + '    help        show help'
  ].join('\n');

  if (!exists(filepath)) {
    log.create(filepath);
    return cb(null, 'create');
  }

  // TODO(mklabs): handle non utf8 file (images, etc.) and compare mtime instead,
  // something like that.
  var actual = fs.readFileSync(path.resolve(filepath), 'utf8');
  if (actual === content) {
    log.identical(filepath);
    return cb(null, 'identical');
  }

  if (self.force) {
    log.force(filepath);
    return cb(null, 'force');
  }

  log.conflict(filepath);

  log(menu);

  // for this particular use case, might use prompt module directly to avoid
  // the additional "Are you sure?" prompt
  (function ask() {
    var config = {
      message: ('Overwrite ' + filepath + '? (enter "h" for help) [Ynaqdh]?'),
      name: 'overwrite',
      'default': 'h'
    };

    process.nextTick(function () {
      self.emit('prompt', config);
      self.emit('conflict', filepath);
    });

    prompt(config, function (err, result) {
      if (err) {
        return cb(err);
      }

      var answer = result.overwrite;

      var ok = 'Yynaqdh'.split('').some(function (valid) {
        return valid === answer;
      });

      if (answer === 'h' || !ok) {
        console.log(menu);
        return ask();
      }

      if (answer === 'n') {
        log.skip(filepath);
        return cb(null, 'skip');
      }

      if (answer === 'q') {
        log.writeln('Aborting...');
        return process.exit(0);
      }

      if (/Y|a/i.test(answer)) {
        log.force(filepath);
        if (answer === 'a') {
          self.force = true;
        }
        return cb(null, 'force');
      }

      if (answer === 'd') {
        console.log(conflicter.diff(fs.readFileSync(filepath, 'utf8'), content));
        return ask();
      }

      // default, even though we should have handled every possible value
      return cb();
    });
  })();
};

// below is borrowed code from visionmedia's excellent mocha (and its reporter)

conflicter.colors = {
  'diff added': 42,
  'diff removed': 41
};

conflicter.diff = function _diff(actual, expected) {
  var msg = diff.diffLines(actual, expected).map(function (str) {
    if (str.added) {
      return conflicter.colorLines('diff added', str.value);
    }

    if (str.removed) {
      return conflicter.colorLines('diff removed', str.value);
    }

    return str.value;
  }).join('');

  // legend
  msg = '\n' +
    conflicter.color('diff removed', 'removed') +
    ' ' +
    conflicter.color('diff added', 'added') +
    '\n\n' +
    msg +
    '\n';

  return msg;
};

conflicter.color = function (type, str) {
  return '\u001b[' + conflicter.colors[type] + 'm' + str + '\u001b[0m';
};

conflicter.colorLines = function colorLines(name, str) {
  return str.split('\n').map(function (str) {
    return conflicter.color(name, str);
  }).join('\n');
};
<commit_msg>Revert 'default' quoting<commit_after>var logger = process.logging || require('./log');

var fs = require('fs');
var path = require('path');
var events = require('events');
var diff = require('diff');
var prompt = require('../actions/prompt').prompt;
var exists = fs.existsSync || path.existsSync;
var log = logger('conflicter');

var conflicter = module.exports = Object.create(events.EventEmitter.prototype);

conflicter.conflicts = [];

conflicter.add = function add(conflict) {
  if (typeof conflict === 'string') {
    conflict = {
      file: conflict,
      content: fs.readFileSync(conflict, 'utf8')
    };
  }

  if (!conflict.file) {
    throw new Error('Missing conflict.file option');
  }

  if (conflict.content === undefined) {
    throw new Error('Missing conflict.content option');
  }

  this.conflicts.push(conflict);
  return this;
};

conflicter.reset = function reset() {
  this.conflicts = [];
  return this;
};

conflicter.pop = function pop() {
  return this.conflicts.pop();
};

conflicter.shift = function shift() {
  return this.conflicts.shift();
};

conflicter.resolve = function resolve(cb) {
  var conflicts = this.conflicts;
  (function next(conflict) {
    if (!conflict) {
      return cb();
    }

    conflicter.collision(conflict.file, conflict.content, function (err, status) {
      if (err) {
        return cb(err);
      }

      conflicter.emit('resolved:' + conflict.file, status);
      next(conflicts.shift());
    });

  })(conflicts.shift());
};

conflicter.collision = function collision(filepath, content, cb) {
  var self = this;
  var menu = [
    ' ' + 'Y'.bold + '    yes         overwrite',
    ' ' + 'n'.bold + '    no          do not overwrite',
    ' ' + 'a'.bold + '    all         overwrite this and all others',
    ' ' + 'q'.bold + '    quit        abort',
    ' ' + 'd'.bold + '    diff        show the differences between the old and the new',
    ' ' + 'h'.bold + '    help        show help'
  ].join('\n');

  if (!exists(filepath)) {
    log.create(filepath);
    return cb(null, 'create');
  }

  // TODO(mklabs): handle non utf8 file (images, etc.) and compare mtime instead,
  // something like that.
  var actual = fs.readFileSync(path.resolve(filepath), 'utf8');
  if (actual === content) {
    log.identical(filepath);
    return cb(null, 'identical');
  }

  if (self.force) {
    log.force(filepath);
    return cb(null, 'force');
  }

  log.conflict(filepath);

  log(menu);

  // for this particular use case, might use prompt module directly to avoid
  // the additional "Are you sure?" prompt
  (function ask() {
    var config = {
      message: ('Overwrite ' + filepath + '? (enter "h" for help) [Ynaqdh]?'),
      name: 'overwrite',
      default: 'h'
    };

    process.nextTick(function () {
      self.emit('prompt', config);
      self.emit('conflict', filepath);
    });

    prompt(config, function (err, result) {
      if (err) {
        return cb(err);
      }

      var answer = result.overwrite;

      var ok = 'Yynaqdh'.split('').some(function (valid) {
        return valid === answer;
      });

      if (answer === 'h' || !ok) {
        console.log(menu);
        return ask();
      }

      if (answer === 'n') {
        log.skip(filepath);
        return cb(null, 'skip');
      }

      if (answer === 'q') {
        log.writeln('Aborting...');
        return process.exit(0);
      }

      if (/Y|a/i.test(answer)) {
        log.force(filepath);
        if (answer === 'a') {
          self.force = true;
        }
        return cb(null, 'force');
      }

      if (answer === 'd') {
        console.log(conflicter.diff(fs.readFileSync(filepath, 'utf8'), content));
        return ask();
      }

      // default, even though we should have handled every possible value
      return cb();
    });
  })();
};

// below is borrowed code from visionmedia's excellent mocha (and its reporter)

conflicter.colors = {
  'diff added': 42,
  'diff removed': 41
};

conflicter.diff = function _diff(actual, expected) {
  var msg = diff.diffLines(actual, expected).map(function (str) {
    if (str.added) {
      return conflicter.colorLines('diff added', str.value);
    }

    if (str.removed) {
      return conflicter.colorLines('diff removed', str.value);
    }

    return str.value;
  }).join('');

  // legend
  msg = '\n' +
    conflicter.color('diff removed', 'removed') +
    ' ' +
    conflicter.color('diff added', 'added') +
    '\n\n' +
    msg +
    '\n';

  return msg;
};

conflicter.color = function (type, str) {
  return '\u001b[' + conflicter.colors[type] + 'm' + str + '\u001b[0m';
};

conflicter.colorLines = function colorLines(name, str) {
  return str.split('\n').map(function (str) {
    return conflicter.color(name, str);
  }).join('\n');
};
<|endoftext|>


<commit_before>module.exports = {
  glossary: [{
    term: 'Active',
    definition: 'If you have refills, you may request a refill of this prescription from your VA pharmacy.'
  },
  {
    term: 'Discontinued',
    definition: 'This prescription is no longer available. Contact your VA provider if you need more of this medication.'
  },
  {
    term: 'Expired',
    definition: 'Prescription too old. Call your VA healthcare team if you need more.'
  },
  {
    term: 'On hold',
    definition: 'This prescription cannot be refilled until a hold is resolved by the pharmacy. Contact your VA pharmacy if you need this prescription now.'
  },
  {
    term: 'Pending',
    definition: 'Your VA provider ordered this prescription. It may not be ready for pick up at the VA pharmacy window or to be mailed to you. Contact your VA pharmacy if you need this prescription now.'
  },
  {
    term: 'Suspended',
    definition: 'You requested this prescription too early. It will be sent to you before you run out. Contact your VA pharmacy if you need this prescription now.'
  }],
  rxStatuses: {
    active: 'Active',
    discontinued: 'Discontinued',
    expired: 'Expired',
    onHold: 'On hold',
    submitted: 'Pending',
    suspended: 'Suspended',
    refillinprocess: 'Refill in procress',
    unknown: 'Unknown'
  },
  sortOptions: [
    { value: 'prescription-name',
      label: 'Prescription name'
    },
    { value: 'facility-name',
      label: 'Facility name'
    },
    { value: 'last-requested',
      label: 'Last requested'
    }]
};
<commit_msg>Updating glossary definitions. closes #2805. (#2970)<commit_after>module.exports = {
  glossary: [{
    term: 'Active',
    definition: 'If you have refills, you may request a refill of this prescription from your VA pharmacy.'
  },
  {
    term: 'Discontinued',
    definition: 'This prescription is no longer available. Contact your VA health care team if you need more of this medication.'
  },
  {
    term: 'Expired',
    definition: 'This prescription is too old to fill. Contact your VA health care team if you need more of this medication.'
  },
  {
    term: 'On hold',
    definition: 'This prescription cannot be refilled until a hold is resolved by the pharmacy. Contact your VA pharmacy when you need more of this medication.'
  },
  {
    term: 'Pending',
    definition: 'This prescription was sent to the pharmacy for review. Contact your VA pharmacy if you need this medication now.'
  },
  {
    term: 'Submitted',
    definition: 'Your prescription refill has been submitted.'
  },
  {
    term: 'Suspended',
    definition: 'It is too early to request a refill for this prescription. It should be sent to you before you run out. Contact your VA pharmacy if you need this medication now.'
  },
  {
    term: 'Unknown',
    definition: 'An unknown error has occurred. Contact your VA pharmacy if you need this medication now.'
  }],
  rxStatuses: {
    active: 'Active',
    discontinued: 'Discontinued',
    expired: 'Expired',
    onHold: 'On hold',
    submitted: 'Pending',
    suspended: 'Suspended',
    refillinprocess: 'Refill in procress',
    unknown: 'Unknown'
  },
  sortOptions: [
    { value: 'prescription-name',
      label: 'Prescription name'
    },
    { value: 'facility-name',
      label: 'Facility name'
    },
    { value: 'last-requested',
      label: 'Last requested'
    }]
};
<|endoftext|>


<commit_before>/**
 * This function can be called with quip(res) or quip(req, res, next).
 * See README for examples.
 */

module.exports = function (req, res, next) {
    if (arguments.length === 1) {
        res = req;
        req = null;
    }

    ///// default response settings /////
    res._quip_headers = {};
    res._quip_status = 200;

    ///// private helper methods /////
    var withStatus = function (code) {
        return function (data) {
            return data ? res.status(code).send(data):
                          res.status(code);
        };
    };
    var redirection = function (code, message) {
        return function (loc) {
            res._quip_headers.Location = loc;
            return res.status(code).send(
                '' +
                    '' +
                        '<title>' + code + ' ' + message + '</title>' +
                    '' +
                    '' +
                        '
' +
                            message + ': ' +
                            '' + loc + '' +
                        '
' +
                    '' +
                ''
            );
        };
    }
    var withType = function (type) {
        return function (data) {
            res.headers({'Content-Type': type});
            return data ? res.send(data): res;
        }
    };

    ///// exported methods /////
    res.status = function (code) {
        res._quip_status = code;
        return res;
    };
    res.headers = function (headers) {
        for (var k in headers) {
            res._quip_headers[k] = headers[k];
        }
        return res;
    };

    // success
    res.ok = withStatus(200);
    res.created = withStatus(201);
    res.accepted = withStatus(202);

    // redirection
    res.moved = redirection(301, 'Moved Permanently');
    res.redirect = redirection(302, 'Found');
    res.found = res.redirect;
    res.notModified = function () {
        res.status(304).send();
    };

    // client error
    res.badRequest = withStatus(400);
    res.unauthorized = withStatus(401);
    res.forbidden = withStatus(403);
    res.notFound = withStatus(404);
    res.notAllowed = withStatus(405);
    res.conflict = withStatus(409);
    res.gone = withStatus(410);

    // server error
    res.error = withStatus(500, 'error');

    // mime types
    res.text = withType('text/plain');
    res.plain = res.text;
    res.html = withType('text/html');
    res.xhtml = withType('application/xhtml+xml');
    res.css = withType('text/css');
    res.xml = withType('text/xml');
    res.atom = withType('application/atom+xml');
    res.rss = withType('application/rss+xml');
    res.javascript = withType('application/javascript');
    res.json = withType('application/json');

    // custom mime type
    res.mime = function (type, data) {
        res.headers({'Content-Type': type});
        return data ? res.send(data): res;
    };

    // JSONP is a special case that should always respond with a 200,
    // there is no reliable way to receive a JSONP result on the
    // client-side if the HTTP status-code is not 200!
    res.jsonp = function (callback, data) {
        if(typeof data == 'object') data = JSON.stringify(data);
        data = callback + '(' + data + ');';
        return res.ok().javascript(data);
    };

    var _write = res.write;
    res.write = function () {
        // make sure headers are sent first
        // res.writeHead(res._quip_status, res._quip_headers);

        res.statusCode = res._quip_status;
        for(var header in res._quip_headers){
            res.setHeader(header, headers[header]);
        }

        res.write = _write;

        return _write.apply(this, arguments);
    };

    // respond with given data using current header and status code
    res.send = function (data) {
        if (data) {
            if (Buffer.isBuffer(data)) {
                res._quip_headers['Content-Length'] = data.length
            }
            else {
                if (typeof data === 'object') {
                    // assume data is JSON if passed an object (not a buffer)
                    if (!res._quip_headers['Content-Type']) {
                        res._quip_headers['Content-Type'] = 'application/json';
                    }
                    data = JSON.stringify(data);
                }
                res._quip_headers['Content-Length'] = Buffer.byteLength(data);
            }
        }
        if (!res._quip_headers['Content-Type']) {
            // assume HTML if data is a string and content type not set
            res._quip_headers['Content-Type'] = 'text/html';
        }
        //res.writeHead(res._quip_status, res._quip_headers);
        if (data) {
            res.write(data);
        }
        res.end();
        return null;
    };

    if (next) {
        // pass updated response object onto next connect middleware
        next(null, res);
    }
    else {
        // called directly, return updated response object
        return res;
    }

};
<commit_msg>Update quip.js<commit_after>/**
 * This function can be called with quip(res) or quip(req, res, next).
 * See README for examples.
 */

module.exports = function (req, res, next) {
    if (arguments.length === 1) {
        res = req;
        req = null;
    }

    ///// default response settings /////
    res._quip_headers = {};
    res._quip_status = 200;

    ///// private helper methods /////
    var withStatus = function (code) {
        return function (data) {
            return data ? res.status(code).send(data):
                          res.status(code);
        };
    };
    var redirection = function (code, message) {
        return function (loc) {
            res._quip_headers.Location = loc;
            return res.status(code).send(
                '' +
                    '' +
                        '<title>' + code + ' ' + message + '</title>' +
                    '' +
                    '' +
                        '
' +
                            message + ': ' +
                            '' + loc + '' +
                        '
' +
                    '' +
                ''
            );
        };
    }
    var withType = function (type) {
        return function (data) {
            res.headers({'Content-Type': type});
            return data ? res.send(data): res;
        }
    };

    ///// exported methods /////
    res.status = function (code) {
        res._quip_status = code;
        return res;
    };
    res.headers = function (headers) {
        for (var k in headers) {
            res._quip_headers[k] = headers[k];
        }
        return res;
    };

    // success
    res.ok = withStatus(200);
    res.created = withStatus(201);
    res.accepted = withStatus(202);

    // redirection
    res.moved = redirection(301, 'Moved Permanently');
    res.redirect = redirection(302, 'Found');
    res.found = res.redirect;
    res.notModified = function () {
        res.status(304).send();
    };

    // client error
    res.badRequest = withStatus(400);
    res.unauthorized = withStatus(401);
    res.forbidden = withStatus(403);
    res.notFound = withStatus(404);
    res.notAllowed = withStatus(405);
    res.conflict = withStatus(409);
    res.gone = withStatus(410);

    // server error
    res.error = withStatus(500, 'error');

    // mime types
    res.text = withType('text/plain');
    res.plain = res.text;
    res.html = withType('text/html');
    res.xhtml = withType('application/xhtml+xml');
    res.css = withType('text/css');
    res.xml = withType('text/xml');
    res.atom = withType('application/atom+xml');
    res.rss = withType('application/rss+xml');
    res.javascript = withType('application/javascript');
    res.json = withType('application/json');

    // custom mime type
    res.mime = function (type, data) {
        res.headers({'Content-Type': type});
        return data ? res.send(data): res;
    };

    // JSONP is a special case that should always respond with a 200,
    // there is no reliable way to receive a JSONP result on the
    // client-side if the HTTP status-code is not 200!
    res.jsonp = function (callback, data) {
        if(typeof data == 'object') data = JSON.stringify(data);
        data = callback + '(' + data + ');';
        return res.ok().javascript(data);
    };

    var _write = res.write;
    res.write = function () {
        // make sure headers are sent first
        // res.writeHead(res._quip_status, res._quip_headers);

        res.statusCode = res._quip_status;
        for(var header in res._quip_headers){
            res.setHeader(header, res._quip_headers[header]);
        }

        res.write = _write;

        return _write.apply(this, arguments);
    };

    // respond with given data using current header and status code
    res.send = function (data) {
        if (data) {
            if (Buffer.isBuffer(data)) {
                res._quip_headers['Content-Length'] = data.length
            }
            else {
                if (typeof data === 'object') {
                    // assume data is JSON if passed an object (not a buffer)
                    if (!res._quip_headers['Content-Type']) {
                        res._quip_headers['Content-Type'] = 'application/json';
                    }
                    data = JSON.stringify(data);
                }
                res._quip_headers['Content-Length'] = Buffer.byteLength(data);
            }
        }
        if (!res._quip_headers['Content-Type']) {
            // assume HTML if data is a string and content type not set
            res._quip_headers['Content-Type'] = 'text/html';
        }
        //res.writeHead(res._quip_status, res._quip_headers);
        if (data) {
            res.write(data);
        }
        res.end();
        return null;
    };

    if (next) {
        // pass updated response object onto next connect middleware
        next(null, res);
    }
    else {
        // called directly, return updated response object
        return res;
    }

};
<|endoftext|>