<commit_before>var oo = require('../../util/oo');
var uuid = require('../../util/uuid');

var annotationHelpers = require('../../model/annotationHelpers');
var deleteNode = require('../../model/transform/deleteNode');

var listUtils = require('./listUtils');

function ListEditing() {}

ListEditing.Prototype = function() {

  this.register = function(behavior) {
    behavior
      .defineBreak('list-item', this.breakList)
      .defineMerge('list','list', this.mergeListWithList)
      .defineMerge('list','textish', this.mergeListWithTextish)
      .defineMerge('textish','list', this.mergeTextishWithList)
      .defineComponentMerge('list', this.mergeListItems);
  };

  this.breakList = function(tx, args) {
    var selection = args.selection;
    if (!selection.isPropertySelection()) {
      throw new Error('Expected property selection.');
    }

    var range = selection.getRange();
    var path = range.start.path;
    var offset = range.start.offset;
    var node = tx.get(path[0]);

    // split the text property and create a new list item node with trailing text and annotations transferred
    var text = node.content;
    var id = uuid(node.type);
    var parentList = tx.get(node.parent);
    var newPath = [id, 'content'];
    var newNode;
    // when breaking at the beginning, a new list-item node will be inserted at the
    // current position of the current node
    if (offset === 0) {
      if (text.length === 0) {
        // if we hit return on an already empty list item, it should transform into
        // a paragraph
        args.nodes = [node];
        args.list = parentList;
        args = listUtils.listItemsToParagraph(tx, args);
      } else {
        newNode = tx.create({
          id: id,
          type: node.type,
          level: node.level,
          content: "",
          parent: node.parent
        });
        // update the items of the parent list
        tx.update([node.parent, 'items'], {insert: {offset: parentList.items.indexOf(node.id), value: id}});
        // maintain the selection at the beginning
        selection = tx.createSelection({
          type: 'property',
          path: path,
          startOffset: 0
        });
        args.selection = selection;
      }
    }
    // otherwise a new list-item node containing all the trailing text is inserted
    // just after the current position of the current node
    else {
      newNode = node.toJSON();
      newNode.id = id;
      newNode.content = text.substring(offset);
      tx.create(newNode);
      if (offset < text.length) {
        // transfer annotations which are after offset to the new node
        annotationHelpers.transferAnnotations(tx, path, offset, [id, 'content'], 0);
        // truncate the original property
        tx.update(path, {
          delete: { start: offset, end: text.length }
        });
      }
      // update the parent list to contain the newly created list item as a child item
      tx.update([node.parent, 'items'], {insert: {offset: parentList.items.indexOf(node.id)+1, value: id}});
      // update the selection
      selection = tx.createSelection({
        type: 'property',
        path: newPath,
        startOffset: 0
      });
      args.selection = selection;
    }
    args.node = newNode;
    return args;
  }.bind(this);

  /**
   * Implements a transformation that will be applied in situations like that
   * ```
   *   <ul>
   *    ...
   *   </ul>
   *   |<!-- cursor here, pressed backspace -->
   *   <ul>
   *    ...
   *   </ul>
   * ```
   *
   * @param tx the transaction
   * @param args an object with `first` being the first list node, and `second` being
   *        the second list node.
   */
  this.mergeListWithList = function(tx, args) {
    var node = tx.get(args.path[0]);
    if (args.direction === 'right') {
      // if delete is hit at the end of the first list, merge the first item of
      // the second list into the last item of the first list
      var item = tx.get(args.second.items[0]);
      var length = node.content.length;
      tx.update([node.id, 'content'], {insert: {offset: length, value: item.content}});
      annotationHelpers.transferAnnotations(tx, [item.id, 'content'], 0, [node.id, 'content'], length);
      tx.update([args.second.id, 'items'], {delete: {offset: 0}});
      if (tx.get([args.second.id, 'items']).length === 0) deleteNode(tx, {nodeId: args.second.id});
    } else {
      // if backspace is hit at the beginning of the second list, we just convert the
      // list item to a paragraph
      var defaultType = tx.getSchema().getDefaultTextType();
      var id = uuid(defaultType);
      var containerId = args.containerId;
      var container = tx.get(containerId);
      var index = container.getChildIndex(args.first);
      tx.create({
        id: id,
        type: defaultType,
        content: node.content
      });
      // show the paragraph node
      container.show(id, index+1);
      // transfer annotations
      annotationHelpers.transferAnnotations(tx, args.path, 0, [id, 'content'], 0);
      var selection = tx.createSelection({
        type: 'property',
        path: [id, 'content'],
        startOffset: 0
      });
      args.selection = selection;
      tx.update([args.second.id, 'items'], {delete: {offset: 0}});
      if (tx.get([args.second.id, 'items']).length === 0) deleteNode(tx, {nodeId: args.second.id});
    }
    return args;
  };

  /**
   * Implements a transformation that will be applied for
   * merging somthing like that
   * ```
   *   <ul>
   *    ...
   *   </ul>
   *   |<!-- cursor here, pressed backspace -->
   *   <p>...</p>
   * ```
   *
   * @param tx the transaction
   * @param args an object with `first` being the first list node, and `second` being
   *        a textish node.
   */
  this.mergeListWithTextish = function(tx, args) {
    // get the id of the last list item
    var lastListItemId = args.first.items[args.first.items.length-1];
    var originalOffset = tx.get(lastListItemId).content.length;
    // hide and delete the textish node
    deleteNode(tx, {nodeId: args.second.id});
    // add the content of the text node to the last item of the list
    tx.update([lastListItemId, 'content'], {insert: {offset: originalOffset, value: args.second.content}});
    annotationHelpers.transferAnnotations(tx, [args.second.id, 'content'], 0, [lastListItemId, 'content'], originalOffset);
    // update the selection
    var selection = tx.createSelection({
      type: 'property',
      path: [lastListItemId, 'content'],
      startOffset: originalOffset
    });
    args.selection = selection;
    return args;
  };

  /**
   * Implements a transformation that will be applied for
   * merging somthing like that
   * ```
   *   <p>...</p>
   *   <ul>
   *     <li>|<!-- cursor here, pressed backspace -->...</li>
   *     ...
   *   </ul>
   * ```
   *
   * @param tx the transaction
   * @param args an object with `first` being the first list node, and `second` being
   *        a textish node.
   */
  this.mergeTextishWithList = function(tx, args) {
    var containerId = args.containerId;
    var container = tx.get(containerId);
    var index = container.getChildIndex(args.second);
    var defaultType = tx.getSchema().getDefaultTextType();
    var id = uuid(defaultType);
    var content = tx.get(args.second.items[0]).content;
    var selection;
    if (args.direction === 'left') {
      // If backspace is hit at the beginning of the list,
      // we convert the first list item into a paragraph
      tx.create({
        id: id,
        type: defaultType,
        content: content
      });
      // show the paragraph node
      container.show(id, index);
      annotationHelpers.transferAnnotations(tx, [args.second.items[0], 'content'], 0, [id, 'content'], 0);
      selection = tx.createSelection({
        type: 'property',
        path: [id, 'content'],
        startOffset: 0
      });
    } else {
      // if delete is hit at the end of the paragraph, we merge the first list
      // element into the paragraph
      var contentLength = args.first.content.length;
      tx.update([args.first.id, 'content'], {insert: {offset: contentLength, value: content}});
      annotationHelpers.transferAnnotations(tx, [args.second.items[0], 'content'], 0, [args.first.id, 'content'], contentLength);
      selection = tx.createSelection({
        type: 'property',
        path: [args.first.id, 'content'],
        startOffset: contentLength
      });
    }
    args.selection = selection;
    tx.update([args.second.id, 'items'], {delete: {offset: 0}});
    if (tx.get([args.second.id, 'items']).length === 0) deleteNode(tx, {nodeId: args.second.id});
    return args;
  };

  this.mergeListItems = function(tx, args) {
    var selection;
    var node = tx.get(args.path[0]);
    var nodeIndex = args.node.items.indexOf(args.path[0]);
    if (args.direction === 'right') {
      // If delete is hit at the end of a list element, we merge the content of the
      // next element into the current one.
      var nextNode = tx.get(args.node.items[nodeIndex+1]);
      var contentLength = node.content.length;
      tx.update(args.path, {insert: {offset: contentLength, value: nextNode.content}});
      annotationHelpers.transferAnnotations(tx, [nextNode.id, 'content'], 0, args.path, contentLength);
      tx.update([args.node.id, 'items'], {delete: {offset: nodeIndex+1}});
      selection = tx.createSelection({
        type: 'property',
        path: args.path,
        startOffset: contentLength
      });
      args.selection = selection;
    } else {
      args.list = args.node;
      args.nodes = [node];
      args = listUtils.listItemsToParagraph(tx, args);
    }
    return args;
  };

};

oo.initClass(ListEditing);

module.exports = ListEditing;
<commit_msg>Delete the node only after transfering annotations<commit_after>var oo = require('../../util/oo');
var uuid = require('../../util/uuid');

var annotationHelpers = require('../../model/annotationHelpers');
var deleteNode = require('../../model/transform/deleteNode');

var listUtils = require('./listUtils');

function ListEditing() {}

ListEditing.Prototype = function() {

  this.register = function(behavior) {
    behavior
      .defineBreak('list-item', this.breakList)
      .defineMerge('list','list', this.mergeListWithList)
      .defineMerge('list','textish', this.mergeListWithTextish)
      .defineMerge('textish','list', this.mergeTextishWithList)
      .defineComponentMerge('list', this.mergeListItems);
  };

  this.breakList = function(tx, args) {
    var selection = args.selection;
    if (!selection.isPropertySelection()) {
      throw new Error('Expected property selection.');
    }

    var range = selection.getRange();
    var path = range.start.path;
    var offset = range.start.offset;
    var node = tx.get(path[0]);

    // split the text property and create a new list item node with trailing text and annotations transferred
    var text = node.content;
    var id = uuid(node.type);
    var parentList = tx.get(node.parent);
    var newPath = [id, 'content'];
    var newNode;
    // when breaking at the beginning, a new list-item node will be inserted at the
    // current position of the current node
    if (offset === 0) {
      if (text.length === 0) {
        // if we hit return on an already empty list item, it should transform into
        // a paragraph
        args.nodes = [node];
        args.list = parentList;
        args = listUtils.listItemsToParagraph(tx, args);
      } else {
        newNode = tx.create({
          id: id,
          type: node.type,
          level: node.level,
          content: "",
          parent: node.parent
        });
        // update the items of the parent list
        tx.update([node.parent, 'items'], {insert: {offset: parentList.items.indexOf(node.id), value: id}});
        // maintain the selection at the beginning
        selection = tx.createSelection({
          type: 'property',
          path: path,
          startOffset: 0
        });
        args.selection = selection;
      }
    }
    // otherwise a new list-item node containing all the trailing text is inserted
    // just after the current position of the current node
    else {
      newNode = node.toJSON();
      newNode.id = id;
      newNode.content = text.substring(offset);
      tx.create(newNode);
      if (offset < text.length) {
        // transfer annotations which are after offset to the new node
        annotationHelpers.transferAnnotations(tx, path, offset, [id, 'content'], 0);
        // truncate the original property
        tx.update(path, {
          delete: { start: offset, end: text.length }
        });
      }
      // update the parent list to contain the newly created list item as a child item
      tx.update([node.parent, 'items'], {insert: {offset: parentList.items.indexOf(node.id)+1, value: id}});
      // update the selection
      selection = tx.createSelection({
        type: 'property',
        path: newPath,
        startOffset: 0
      });
      args.selection = selection;
    }
    args.node = newNode;
    return args;
  }.bind(this);

  /**
   * Implements a transformation that will be applied in situations like that
   * ```
   *   <ul>
   *    ...
   *   </ul>
   *   |<!-- cursor here, pressed backspace -->
   *   <ul>
   *    ...
   *   </ul>
   * ```
   *
   * @param tx the transaction
   * @param args an object with `first` being the first list node, and `second` being
   *        the second list node.
   */
  this.mergeListWithList = function(tx, args) {
    var node = tx.get(args.path[0]);
    if (args.direction === 'right') {
      // if delete is hit at the end of the first list, merge the first item of
      // the second list into the last item of the first list
      var item = tx.get(args.second.items[0]);
      var length = node.content.length;
      tx.update([node.id, 'content'], {insert: {offset: length, value: item.content}});
      annotationHelpers.transferAnnotations(tx, [item.id, 'content'], 0, [node.id, 'content'], length);
      tx.update([args.second.id, 'items'], {delete: {offset: 0}});
      if (tx.get([args.second.id, 'items']).length === 0) deleteNode(tx, {nodeId: args.second.id});
    } else {
      // if backspace is hit at the beginning of the second list, we just convert the
      // list item to a paragraph
      var defaultType = tx.getSchema().getDefaultTextType();
      var id = uuid(defaultType);
      var containerId = args.containerId;
      var container = tx.get(containerId);
      var index = container.getChildIndex(args.first);
      tx.create({
        id: id,
        type: defaultType,
        content: node.content
      });
      // show the paragraph node
      container.show(id, index+1);
      // transfer annotations
      annotationHelpers.transferAnnotations(tx, args.path, 0, [id, 'content'], 0);
      var selection = tx.createSelection({
        type: 'property',
        path: [id, 'content'],
        startOffset: 0
      });
      args.selection = selection;
      tx.update([args.second.id, 'items'], {delete: {offset: 0}});
      if (tx.get([args.second.id, 'items']).length === 0) deleteNode(tx, {nodeId: args.second.id});
    }
    return args;
  };

  /**
   * Implements a transformation that will be applied for
   * merging somthing like that
   * ```
   *   <ul>
   *    ...
   *   </ul>
   *   |<!-- cursor here, pressed backspace -->
   *   <p>...</p>
   * ```
   *
   * @param tx the transaction
   * @param args an object with `first` being the first list node, and `second` being
   *        a textish node.
   */
  this.mergeListWithTextish = function(tx, args) {
    // get the id of the last list item
    var lastListItemId = args.first.items[args.first.items.length-1];
    var originalOffset = tx.get(lastListItemId).content.length;
    // add the content of the text node to the last item of the list
    tx.update([lastListItemId, 'content'], {insert: {offset: originalOffset, value: args.second.content}});
    annotationHelpers.transferAnnotations(tx, [args.second.id, 'content'], 0, [lastListItemId, 'content'], originalOffset);
    // hide and delete the textish node
    deleteNode(tx, {nodeId: args.second.id});
    // update the selection
    var selection = tx.createSelection({
      type: 'property',
      path: [lastListItemId, 'content'],
      startOffset: originalOffset
    });
    args.selection = selection;
    return args;
  };

  /**
   * Implements a transformation that will be applied for
   * merging somthing like that
   * ```
   *   <p>...</p>
   *   <ul>
   *     <li>|<!-- cursor here, pressed backspace -->...</li>
   *     ...
   *   </ul>
   * ```
   *
   * @param tx the transaction
   * @param args an object with `first` being the first list node, and `second` being
   *        a textish node.
   */
  this.mergeTextishWithList = function(tx, args) {
    var containerId = args.containerId;
    var container = tx.get(containerId);
    var index = container.getChildIndex(args.second);
    var defaultType = tx.getSchema().getDefaultTextType();
    var id = uuid(defaultType);
    var content = tx.get(args.second.items[0]).content;
    var selection;
    if (args.direction === 'left') {
      // If backspace is hit at the beginning of the list,
      // we convert the first list item into a paragraph
      tx.create({
        id: id,
        type: defaultType,
        content: content
      });
      // show the paragraph node
      container.show(id, index);
      annotationHelpers.transferAnnotations(tx, [args.second.items[0], 'content'], 0, [id, 'content'], 0);
      selection = tx.createSelection({
        type: 'property',
        path: [id, 'content'],
        startOffset: 0
      });
    } else {
      // if delete is hit at the end of the paragraph, we merge the first list
      // element into the paragraph
      var contentLength = args.first.content.length;
      tx.update([args.first.id, 'content'], {insert: {offset: contentLength, value: content}});
      annotationHelpers.transferAnnotations(tx, [args.second.items[0], 'content'], 0, [args.first.id, 'content'], contentLength);
      selection = tx.createSelection({
        type: 'property',
        path: [args.first.id, 'content'],
        startOffset: contentLength
      });
    }
    args.selection = selection;
    tx.update([args.second.id, 'items'], {delete: {offset: 0}});
    if (tx.get([args.second.id, 'items']).length === 0) deleteNode(tx, {nodeId: args.second.id});
    return args;
  };

  this.mergeListItems = function(tx, args) {
    var selection;
    var node = tx.get(args.path[0]);
    var nodeIndex = args.node.items.indexOf(args.path[0]);
    if (args.direction === 'right') {
      // If delete is hit at the end of a list element, we merge the content of the
      // next element into the current one.
      var nextNode = tx.get(args.node.items[nodeIndex+1]);
      var contentLength = node.content.length;
      tx.update(args.path, {insert: {offset: contentLength, value: nextNode.content}});
      annotationHelpers.transferAnnotations(tx, [nextNode.id, 'content'], 0, args.path, contentLength);
      tx.update([args.node.id, 'items'], {delete: {offset: nodeIndex+1}});
      selection = tx.createSelection({
        type: 'property',
        path: args.path,
        startOffset: contentLength
      });
      args.selection = selection;
    } else {
      args.list = args.node;
      args.nodes = [node];
      args = listUtils.listItemsToParagraph(tx, args);
    }
    return args;
  };

};

oo.initClass(ListEditing);

module.exports = ListEditing;
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var tape = require( 'tape' );
var endsWith = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.equal( typeof endsWith, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function throws an error if the first argument is not a string', function test( t ) {
	var values;
	var i;

	values = [
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			endsWith( value, 'abc' );
		};
	}
});

tape( 'the function throws an error if the second argument is not a string', function test( t ) {
	var values;
	var i;

	values = [
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			endsWith( 'abc', value );
		};
	}
});

tape( 'the function throws an error if the `position` argument is not an integer', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5.5,
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function(){}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function() {
			endsWith( 'abc', 'a', value );
		};
	}
});

tape( 'the function returns `true` if the input string ends with the search value', function test( t ) {
	var out;

	out = endsWith( 'Too late, I\'m afraid', 'afraid' );
	t.equal( out, true, 'returns true' );

	out = endsWith( 'Not too late, I\'m afraid...', 'afraid...' );
	t.equal( out, true, 'returns true' );

	out = endsWith( 'Welcome home!', 'home!' );
	t.equal( out, true, 'returns true' );

	t.end();
});

tape( 'the function returns `false` if the input string does not end with the search value', function test( t ) {
	var out;

	out = endsWith( 'Too late, I\'m afraid', 'Afraid' );
	t.equal( out, false, 'returns false' );

	out = endsWith( 'Not too late, I\'m afraid...', 'afraid' );
	t.equal( out, false, 'returns false' );

	out = endsWith( 'Welcome home!', 'welcome home!' );
	t.equal( out, false, 'returns false' );

	t.end();
});

tape( 'the function starts the search at the provided position (from the right)', function test( t ) {
	var out;

	out = endsWith( 'Too late, I\'m afraid', 'late', 7 );
	t.equal( out, false, 'returns false' );

	out = endsWith( 'Too late, I\'m afraid', 'late', 8 );
	t.equal( out, true, 'returns true' );

	out = endsWith( 'Too late, I\'m afraid', 'late', 9 );
	t.equal( out, false, 'returns false' );

	t.end();
});

tape( 'the function returns `false` if provided a negative position', function test( t ) {
	var out = endsWith( 'abc', 'c', -1 );
	t.equal( out, false, 'returns false' );
	t.end();
});
<commit_msg>Fix lint errors<commit_after>'use strict';

// MODULES //

var tape = require( 'tape' );
var endsWith = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.equal( typeof endsWith, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function throws an error if the first argument is not a string', function test( t ) {
	var values;
	var i;

	values = [
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function noop() {}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			endsWith( value, 'abc' );
		};
	}
});

tape( 'the function throws an error if the second argument is not a string', function test( t ) {
	var values;
	var i;

	values = [
		5,
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function noop() {}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			endsWith( 'abc', value );
		};
	}
});

tape( 'the function throws an error if the `position` argument is not an integer', function test( t ) {
	var values;
	var i;

	values = [
		'5',
		5.5,
		null,
		true,
		undefined,
		NaN,
		[],
		{},
		function noop() {}
	];

	for ( i = 0; i < values.length; i++ ) {
		t.throws( badValue( values[i] ), TypeError, 'throws an error when provided '+values[i] );
	}
	t.end();

	function badValue( value ) {
		return function badValue() {
			endsWith( 'abc', 'a', value );
		};
	}
});

tape( 'the function returns `true` if the input string ends with the search value', function test( t ) {
	var out;

	out = endsWith( 'Too late, I\'m afraid', 'afraid' );
	t.equal( out, true, 'returns true' );

	out = endsWith( 'Not too late, I\'m afraid...', 'afraid...' );
	t.equal( out, true, 'returns true' );

	out = endsWith( 'Welcome home!', 'home!' );
	t.equal( out, true, 'returns true' );

	t.end();
});

tape( 'the function returns `false` if the input string does not end with the search value', function test( t ) {
	var out;

	out = endsWith( 'Too late, I\'m afraid', 'Afraid' );
	t.equal( out, false, 'returns false' );

	out = endsWith( 'Not too late, I\'m afraid...', 'afraid' );
	t.equal( out, false, 'returns false' );

	out = endsWith( 'Welcome home!', 'welcome home!' );
	t.equal( out, false, 'returns false' );

	t.end();
});

tape( 'the function starts the search at the provided position (from the right)', function test( t ) {
	var out;

	out = endsWith( 'Too late, I\'m afraid', 'late', 7 );
	t.equal( out, false, 'returns false' );

	out = endsWith( 'Too late, I\'m afraid', 'late', 8 );
	t.equal( out, true, 'returns true' );

	out = endsWith( 'Too late, I\'m afraid', 'late', 9 );
	t.equal( out, false, 'returns false' );

	t.end();
});

tape( 'the function returns `false` if provided a negative position', function test( t ) {
	var out = endsWith( 'abc', 'c', -1 );
	t.equal( out, false, 'returns false' );
	t.end();
});
<|endoftext|>


<commit_before>//file FileBuffer loaded with fs.readFile or readFileSync
//returns a huge ass array of strings
function loadWords(file) {
  return file.toString("utf-8").split(/\r?\n/);
}

//reg a RegExp
//source a huge ass array of strings to be filtered
//returns the items in the array that match the RegExp
//please dont pass an array of shit other than strings
//this is one of those times I wish I was using TypeScript
function filter(reg, source) {
  let ret = [];
  for (let word of source) {
    if (reg.test(word) === true) {
      ret.push(word);
    }
  }
  return ret;
}

module.exports = {
  loadWords,
  filter
};<commit_msg>documenting with JSDoc<commit_after>/** Load the lines of text from a file into an array.
 * @param {String|Buffer} file The loaded file from fs.readFileSync()
 * @returns {Array}
 * **/
function loadWords(file) {
  return file.toString("utf-8").split(/\r?\n/);
}

/** Filter the array to only the objects that match the regex.
 * @param {RegExp} reg
 * @param {Array} source
 * @returns {Array}
 * **/
function filter(reg, source) {
  let ret = [];
  for (let word of source) {
    if (reg.test(word) === true) {
      ret.push(word);
    }
  }
  return ret;
}

module.exports = {
  loadWords,
  filter
};<|endoftext|>


<commit_before>// all.js (Alpha) version 0.0.1
// Author: Bheemarasetty Bhulok Sankar
// Docs & Web: http://all.js.org

;(function() {
  	// Initializing the main object as a window in the browser
	var main = this;
	// Defining an object
	var all = {};
  	// Print version of this all.js
  	all.version = '0.0.1';
	// All object set global to the main object as a window
 	main.all = all;

 	// API for Generate
 	var init_grandom = {
   		"u": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
   		"l": "abcdefghijklmnopqrstuvwxyz",
   		"n": "0123456789",
   		"s": '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\'
	};

	// Generate random alpha numeric string with a user specific length and input.
	// eg. all.grandom(10, "snl"); //output: "c$cnx=_?gq"
 	all.grandom = function grandom(length, input) {
  		result = "";
  		output = "";

  		for (var i = 0, len = input.length; i < len; i++) {
    		        result += init_grandom[input[i]];
  		}
  		for (var j=0; j<length; j++) {
	  		output = output + result.charAt(Math.floor(Math.random() * result.length));
		}
  		return output;
	}

  	// Generate random integer within a range or below a certain number.
  	// eg. all.gint(3,5); //output: 4
  	all.gint = function gint(a, b) {
  		if (arguments.length === 1) {
    			b = a;
    			a = 0;
  		}
  		return Math.floor(Math.random() * (b - a + 1) + a);
  	}

}.call(this));
// End of all.js
<commit_msg>Identify typeof function<commit_after>// all.js (Alpha) version 0.0.1
// Author: Bheemarasetty Bhulok Sankar
// Docs & Web: http://all.js.org

;(function() {
  	// Initializing the main object as a window in the browser
	var main = this;
	// Defining an object
	var all = {};
  	// Print version of this all.js
  	all.version = '0.0.1';
	// All object set global to the main object as a window
 	main.all = all;

 	// API for Generate
 	var init_grandom = {
   		"u": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
   		"l": "abcdefghijklmnopqrstuvwxyz",
   		"n": "0123456789",
   		"s": '~`!@#$%^&*()_+-={}[]:";\'<>?,./|\\'
	};

	// Generate random alpha numeric string with a user specific length and input.
	// eg. all.grandom(10, "snl"); //output: "c$cnx=_?gq"
 	all.grandom = function grandom(length, input) {
  		result = "";
  		output = "";

  		for (var i = 0, len = input.length; i < len; i++) {
    		        result += init_grandom[input[i]];
  		}
  		for (var j=0; j<length; j++) {
	  		output = output + result.charAt(Math.floor(Math.random() * result.length));
		}
  		return output;
	}

  	// Generate random integer within a range or below a certain number.
  	// eg. all.gint(3,5); //output: 4
  	all.gint = function gint(a, b) {
  		if (arguments.length === 1) {
    			b = a;
    			a = 0;
  		}
  		return Math.floor(Math.random() * (b - a + 1) + a);
  	}

  	// Identify the user input or a value of a variable.
  	all.identify = function identify(input) {
  		return typeof input;
  	}

}.call(this));
// End of all.js
<|endoftext|>


<commit_before>var sizlate = require('sizlate')

module.exports = function (page, layout, renderedComponents) {
  var selectors = {}
  var spec = page.spec
  for (var component in spec) {
    selectors[component] = renderedComponents[spec[component].component][component]
  }
  return sizlate.render(layout, selectors)
}
<commit_msg>adding  selectors to page level<commit_after>var sizlate = require('sizlate')

module.exports = function (page, layout, renderedComponents) {
  var selectors = {}
  var spec = page.spec
  for (var component in spec) {
    selectors[component] = renderedComponents[spec[component].component][component]
  }
  var out = sizlate.render(layout, selectors)
    if(page.selectors) {
        return  sizlate.render(out, page.selectors)
    }else {
        return out;
    }

}
<|endoftext|>


<commit_before>/**
 *
 */
var fs = require('fs'),
    request = require('request'),
    mkdirp = require('mkdirp'),
    onErrorQueue = [];

function callOnError(urlName) {
    onErrorQueue.forEach(function (fc) {
        fc(urlName);
    })
}
/**
 * add a / at the beginning or end
 * @param folder
 * @returns {*}
 */
function formatFolder(folder) {
    if (folder === undefined || folder.length === 0) {
        return '/';
    }
    // replace all spaces with underscores
    folder = folder.split(' ').join('_');

    if (folder[folder.length] !== '/') {
        folder += '/';
    }
    if (folder[0] !== '/') {
        folder = '/' + folder;
    }
    return folder;
}

/**
 * helper function to return the file name without the extension
 * @param fileName
 * @returns {string}
 */
function removeExtension(fileName) {
    var split = fileName.split('.');
    return split.splice(0, split.length - 1).join('.');
}

/**
 * Downloads all files and saves them in the destination folder with the passed files name
 * @param destFolder
 * @param urls []
 */
function loadFilesToFolder(destFolder, urls, singleFolder) {

    urls.forEach(function (url) {
        var split,
            fileName,
            urlSrc,
            err = false;

        if (url.hasOwnProperty('rename')) {
            fileName = url.rename;
            urlSrc = url.url;
        } else {
            split = url.split('/');
            fileName = split[split.length - 1];
            urlSrc = url;
        }
        request
            .get(urlSrc)
            .on('response', function(response) {
                if (response.statusCode !== 200) {
                    err = true;
                    callOnError(urlSrc);
                } else {
                    if (singleFolder === true) {
                        // can't use the async function here - otherwise the stream will fail
                        mkdirp.sync(destFolder + removeExtension(fileName));
                        response.pipe(fs.createWriteStream(destFolder + removeExtension(fileName) + '/' + fileName));
                    } else {
                        response.pipe(fs.createWriteStream(destFolder + fileName));
                    }
                }
            })
    })
}
module.exports = {
    /**
     * Loads files from a URL and saves this on local storage.
     *
     * @param config [{
     *   "destFolder": "", (default "/")
     *   "urls": [ "String" | {Object {url:"String", rename:"String"} } ]
     *  }]
     * @param rootFolder
     */
    load : function (config, rootFolder) {
        config.forEach(function (obj) {
            var destFolder = formatFolder(obj.destFolder);
            console.log('uploader:obj.destFolder', destFolder);

            mkdirp(rootFolder + destFolder, function () {
                if (obj.hasOwnProperty('singleFolder') && obj.singleFolder) {
                    loadFilesToFolder(rootFolder + destFolder, obj.urls, true);
                } else {
                    loadFilesToFolder(rootFolder + destFolder, obj.urls);
                }
            })
        })
    },
    /**
     * A error callback can be registered - If a image could not be loaded it will call the callback
     * with the none existing URL.
     * @param fc -> (URL)
     */
    onError : function (fc) {
        onErrorQueue.push(fc);
    }
};<commit_msg>work in progress<commit_after>/**
 *
 */
var fs = require('fs'),
    request = require('request'),
    mkdirp = require('mkdirp'),
    onErrorQueue = [];

function callOnError(urlName) {
    onErrorQueue.forEach(function (fc) {
        fc(urlName);
    })
}
/**
 * add a / at the beginning or end
 * @param folder
 * @returns {*}
 */
function formatFolder(folder) {
    if (folder === undefined || folder.length === 0) {
        return '/';
    }
    // replace all spaces with underscores
    folder = folder.split(' ').join('_');

    if (folder[folder.length] !== '/') {
        folder += '/';
    }
    if (folder[0] !== '/') {
        folder = '/' + folder;
    }
    return folder;
}

/**
 * helper function to return the file name without the extension
 * @param fileName
 * @returns {string}
 */
function removeExtension(fileName) {
    var split = fileName.split('.');
    return split.splice(0, split.length - 1).join('.');
}

/**
 * Downloads all files and saves them in the destination folder with the passed files name
 * @param destFolder
 * @param urls []
 */
function loadFilesToFolder(destFolder, urls, singleFolder, cb) {

    urls.forEach(function (url) {
        var split,
            fileName,
            urlSrc,
            err = false;

        if (url.hasOwnProperty('rename')) {
            fileName = url.rename;
            urlSrc = url.url;
        } else {
            split = url.split('/');
            fileName = split[split.length - 1];
            urlSrc = url;
        }
        request
            .get(urlSrc)
            .on('response', function(response) {
                var folder = destFolder + fileName,
                    imgStream;

                if (response.statusCode !== 200) {
                    err = true;
                    callOnError(urlSrc);
                } else {
                    if (singleFolder === true) {
                        // can't use the async function here - otherwise the stream will fail
                        mkdirp.sync(destFolder + removeExtension(fileName));
                        folder = destFolder + removeExtension(fileName) + '/' + fileName;
                    }
                    imgStream = fs.createWriteStream(folder);
                    imgStream.on('close', function () {
                        cb();
                    });
                    response.pipe(fs.createWriteStream(folder));
                }
            })
    })
}
module.exports = {
    /**
     * Loads files from a URL and saves this on local storage.
     *
     * @param config [{
     *   "destFolder": "", (default "/")
     *   "urls": [ "String" | {Object {url:"String", rename:"String"} } ]
     *  }]
     * @param rootFolder
     */
    load : function (config, rootFolder, cb) {
        config.forEach(function (obj) {
            var destFolder = formatFolder(obj.destFolder);
            console.log('uploader:obj.destFolder', destFolder);

            mkdirp(rootFolder + destFolder, function () {
                if (obj.hasOwnProperty('singleFolder') && obj.singleFolder) {
                    loadFilesToFolder(rootFolder + destFolder, obj.urls, true);
                } else {
                    loadFilesToFolder(rootFolder + destFolder, obj.urls);
                }
            })
        })
    },
    /**
     * A error callback can be registered - If a image could not be loaded it will call the callback
     * with the none existing URL.
     * @param fc -> (URL)
     */
    onError : function (fc) {
        onErrorQueue.push(fc);
    }
};<|endoftext|>


<commit_before>//api.js
var bcrypt = require('bcrypt');

const utl = require('./utl.js');

module.exports = api;

///////////////////////////

function api(req, res) {

	if (utl.parseurl(req.url)[0] === 'api') {return false};

	var command = utl.parseurl(req.url)[1];
	var username = utl.parseurl(req.url)[2];

	return true;
};

function authenticate(token, req) {

	bcrypt.compare(token)
};

function authorize(command, username) {
	try {
		return command in db.private(users, username).permissions;
	} catch (e){
		utl.log(e);
		return false;
	};
};<commit_msg>api.switch(), authentication and authorization<commit_after>//api.js

var bcrypt = require('bcrypt');

const utl = require('./utl.js');

api.switch = apiSwitch;

module.exports = api;

///////////////////////////

function api(req, res) {

	var command = utl.parseurl(req.url)[1];
	var username = utl.parseurl(req.url)[2];

	if (!authorized(command, username)) {sendCode(403, res)};

	var msg=[];

	req.on('error', (err)=>{
		utl.log(err);
		sendCode(res, 400);
	}).on('data', (d)=>{
		msg.push(d);
	}).on('end', ()=>{
		msg = JSON.parse(Buffer.concat(msg).toString());
		authenticate(username, msg.token, res, req, ()=>{
			api[command](msg, res);
		});
	});

};

function authenticate(username, token, res, req, cb) {
	token = token || '';

	bcrypt.compare(token, db.private(users, username).token, (err,result)=>{
		if (err) {
			sendCode(400, res);
			utl.log(`bcrypt error ${err}`);
		} else if (!result) {
			sendCode(403, res);
			utl.log(`failed authentification for user ${username} from ${req.connection.remoteAdress}`);
		} else {
			cb();
		}
	});
};

function authorized(command, username) {

	const users = db.private(users);

	return (username in users && command in users[username].permissions);

};

function sendCode(code, res, text) {
	res.writeHead(code, {'Content-Type':'text/plain'});
	res.end(text || '');
};

function apiSwitch(req) {
	return (utl.parseurl(req.url)[0] === 'api' && req.method === 'POST');
};<|endoftext|>


<commit_before>const fs = require('fs');
const qs = require('qs');
const https = require('https');
const url = require('url');
const request = require('request');
const passport = require('passport');
const config = require('./config');
var bodyParser = require('body-parser');
var helper = require('sendgrid').mail;
var sg = require('sendgrid')(config.app.sendGridApiKey);
var requestUtils = require('./client/src/shared/requestUtils');
var notifications = require('./notifications');

module.exports = function (app) {

  const GITHUB_API_ROOT = 'https://api.github.com';

  const getlocalAppRootUrl = (request) => {
    return process.env.NODE_ENV == 'production' ? url.format({
      protocol: 'https', // request.protocol returns http for now since the node server itself is only using http. However the api is used over https thanks to azure / IIS
      host: request.hostname,
      // port: request.port,
      pathname: ''
    }) : 'https://localhost:3000'; // hard coded value in development because the request came through the webpack dev server on a different port and via https.

  }
  function loadAppData() {
    return JSON.parse(fs.readFileSync(`${__dirname}/${config.app.groupConfigPath}`, 'utf-8'));
  }

  const appData = loadAppData();

  const sendMail = (to, title, body) => {
    const from_email = new helper.Email(config.app.emailSender);
    const to_email = new helper.Email(to);
    const subject = title;
    const content = new helper.Content('text/html', body);
    const mail = new helper.Mail(from_email, subject, to_email, content);

    const request = sg.emptyRequest({
      method: 'POST',
      path: '/v3/mail/send',
      body: mail.toJSON(),
    });

    return sg.API(request)
  }

  // Utility functions
  const getProxyRequestOptions = url => (
    {
      url: GITHUB_API_ROOT + url.replace('/api', ''),
      headers: {
        'Authorization': `token ${config.github.botToken}`
      }
    }
  );

  const rewriteResponseHeaders = (request, response) => {
    if (response.headers.link) {
      response.headers.link = response.headers.link.replace(GITHUB_API_ROOT, getlocalAppRootUrl(request) + '/api')
    }
  }

  const genericErrorHandler = (error, response, body) => {
    if (error) {
      if (error.code === 'ECONNREFUSED') {
        console.error('Refused connection');
      } else if (error.code === 'ETIMEDOUT') {
        console.error('Connection timed out');
      }
      else {
        throw error;
      }
    }
  };

  const validateRepository = (organisation, repo) => {
    if (projects.filter(p => p.organisation == organisation && p.repository == repo).length == 0) {
      console.log(`listing issues on repository ${req.params.organisation}/${req.params.repo}`);
      return false;
    }
    return true;
  }

  // App end points
  app.get('/api/projects', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    res.json(appData)
  })

  app.get('/api/authenticate/github/:code', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    const r = request.post({
      url: 'https://github.com/login/oauth/access_token',
      json: {
        client_id: config.github.clientID,
        client_secret: config.github.clientSecret,
        code: req.params.code
      }
    }, genericErrorHandler)
    r.pipe(res);
  });

  // proxy to github api end points
  app.get('/api/repos/:organisation/:repo/issues', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    if (!validateRepository(req.params.organisation, req.params.repo))
      res.status(403).send({ error: 'Invalid repository name' });

    console.log(`listing issues on repository ${req.params.organisation}/${req.params.repo}`);
    const r = request(getProxyRequestOptions(req.url))
    console.log('Proxied request options: ', getProxyRequestOptions(req.url))
    req.pipe(r, genericErrorHandler)
      .on('response', response => rewriteResponseHeaders(req, response))
      .pipe(res);
  });

  app.get('/api/repositories/:repoId/issues', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    console.log(`going through pages on repository ${req.params.repoId}`);
    const r = request(getProxyRequestOptions(req.url))
    console.log('Proxied request options: ', getProxyRequestOptions(req.url))
    req.pipe(r, genericErrorHandler)
      .on('response', response => rewriteResponseHeaders(req, response))
      .pipe(res);
  });

  app.get('/api/repos/:organisation/:repo/issues/:issueId', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    if (!validateRepository(req.params.organisation, req.params.repo))
      res.status(403).send({ error: 'Invalid repository name' });

    const r = request(getProxyRequestOptions(req.url))
    console.log('Proxied request options: ', getProxyRequestOptions(req.url))
    req.pipe(r, genericErrorHandler).pipe(res);
  });

  app.post('/api/repos/:organisation/:repo/issues', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    if (!validateRepository(req.params.organisation, req.params.repo))
      res.status(403).send({ error: 'Invalid repository name' });

    console.log(`creating issue on repository ${req.params.organisation}/${req.params.repo}`);
    const r = request.post(getProxyRequestOptions(req.url));
    req.pipe(r, genericErrorHandler).pipe(res);
  });

  // handle github web hooks
  app.post('/api/github-webhooks', [passport.authenticate('oauth-bearer', { session: false }), bodyParser.json()], function (req, res) {
    console.log(`received web hook: ${req.headers['x-github-event']}`);
    const eventType = req.headers['x-github-event'];
    if (
      eventType && ['issues', 'issue_comment'].includes(eventType) &&
      req.body && req.body.issue &&
      req.body.issue.user.login === config.github.botLogin
    ) {
      const { email } = requestUtils.getCreator(req.body.issue);

      try {
        const project = notifications.findProject(req.body, appData.projects);
        const requestUrl = notifications.getRequestUrl(req.body.issue.number, project, getlocalAppRootUrl(req));
        const { subject, content } = notifications.getNotificationText(eventType, req.body, project.name, requestUrl);

        if (email && subject && content) {
          sendMail(email, subject, content)
            .then(response => {
              console.log(response.statusCode, `Email notification sent successfully to ${email}`);
            })
            .catch(error => {
              //error is an instance of SendGridError
              //The full response is attached to error.response
              console.log(error.response);
            });
        }
      }
      catch (error) {
        console.log('An error occcured while attempting to process the following github webhook:');
        console.log(error);
        console.log('webhook data:')
        console.dir(req.body);
        res.json({
          message: 'An error occcured while attempting to process a github webhook',
          webhook: req.body
        })
      }
    }
    res.json({ message: 'web hook processed' });
  });
}
<commit_msg>Fix code following rebase<commit_after>const fs = require('fs');
const qs = require('qs');
const https = require('https');
const url = require('url');
const request = require('request');
const passport = require('passport');
const config = require('./config');
var bodyParser = require('body-parser');
var helper = require('sendgrid').mail;
var sg = require('sendgrid')(config.app.sendGridApiKey);
var requestUtils = require('./client/src/shared/requestUtils');
var notifications = require('./notifications');

module.exports = function (app) {

  const GITHUB_API_ROOT = 'https://api.github.com';

  const getlocalAppRootUrl = (request) => {
    return process.env.NODE_ENV == 'production' ? url.format({
      protocol: 'https', // request.protocol returns http for now since the node server itself is only using http. However the api is used over https thanks to azure / IIS
      host: request.hostname,
      // port: request.port,
      pathname: ''
    }) : 'https://localhost:3000'; // hard coded value in development because the request came through the webpack dev server on a different port and via https.

  }

  function loadAppData() {
    return JSON.parse(fs.readFileSync(`${__dirname}/${config.app.groupConfigPath}`, 'utf-8'));
  }

  const appData = loadAppData();

  // Utility functions
  const sendMail = (to, title, body) => {
    const from_email = new helper.Email(config.app.emailSender);
    const to_email = new helper.Email(to);
    const subject = title;
    const content = new helper.Content('text/html', body);
    const mail = new helper.Mail(from_email, subject, to_email, content);

    const request = sg.emptyRequest({
      method: 'POST',
      path: '/v3/mail/send',
      body: mail.toJSON(),
    });

    return sg.API(request)
  }

  const getProxyRequestOptions = url => (
    {
      url: GITHUB_API_ROOT + url.replace('/api', ''),
      headers: {
        'Authorization': `token ${config.github.botToken}`
      }
    }
  );

  const rewriteResponseHeaders = (request, response) => {
    if (response.headers.link) {
      response.headers.link = response.headers.link.replace(GITHUB_API_ROOT, getlocalAppRootUrl(request) + '/api')
    }
  }

  const genericErrorHandler = (error, response, body) => {
    if (error) {
      if (error.code === 'ECONNREFUSED') {
        console.error('Refused connection');
      } else if (error.code === 'ETIMEDOUT') {
        console.error('Connection timed out');
      }
      else {
        throw error;
      }
    }
  };

  const validateRepository = (organisation, repo, projects) => {
    if (projects.filter(p => p.organisation == organisation && p.repository == repo).length == 0) {
      console.log(`listing issues on repository ${req.params.organisation}/${req.params.repo}`);
      return false;
    }
    return true;
  }

  // App end points
  app.get('/api/projects', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    res.json(appData)
  })

  app.get('/api/authenticate/github/:code', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    const r = request.post({
      url: 'https://github.com/login/oauth/access_token',
      json: {
        client_id: config.github.clientID,
        client_secret: config.github.clientSecret,
        code: req.params.code
      }
    }, genericErrorHandler)
    r.pipe(res);
  });

  // proxy to github api end points
  app.get('/api/repos/:organisation/:repo/issues', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    if (!validateRepository(req.params.organisation, req.params.repo, appData.projects))
      res.status(403).send({ error: 'Invalid repository name' });

    console.log(`listing issues on repository ${req.params.organisation}/${req.params.repo}`);
    const r = request(getProxyRequestOptions(req.url))
    console.log('Proxied request options: ', getProxyRequestOptions(req.url))
    req.pipe(r, genericErrorHandler)
      .on('response', response => rewriteResponseHeaders(req, response))
      .pipe(res);
  });

  app.get('/api/repositories/:repoId/issues', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    console.log(`going through pages on repository ${req.params.repoId}`);
    const r = request(getProxyRequestOptions(req.url))
    console.log('Proxied request options: ', getProxyRequestOptions(req.url))
    req.pipe(r, genericErrorHandler)
      .on('response', response => rewriteResponseHeaders(req, response))
      .pipe(res);
  });

  app.get('/api/repos/:organisation/:repo/issues/:issueId', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    if (!validateRepository(req.params.organisation, req.params.repo, appData.projects))
      res.status(403).send({ error: 'Invalid repository name' });

    const r = request(getProxyRequestOptions(req.url))
    console.log('Proxied request options: ', getProxyRequestOptions(req.url))
    req.pipe(r, genericErrorHandler).pipe(res);
  });

  app.post('/api/repos/:organisation/:repo/issues', passport.authenticate('oauth-bearer', { session: false }), function (req, res) {
    if (!validateRepository(req.params.organisation, req.params.repo, appData.projects))
      res.status(403).send({ error: 'Invalid repository name' });

    console.log(`creating issue on repository ${req.params.organisation}/${req.params.repo}`);
    const r = request.post(getProxyRequestOptions(req.url));
    req.pipe(r, genericErrorHandler).pipe(res);
  });

  // handle github web hooks
  app.post('/api/github-webhooks', [passport.authenticate('oauth-bearer', { session: false }), bodyParser.json()], function (req, res) {
    console.log(`received web hook: ${req.headers['x-github-event']}`);
    const eventType = req.headers['x-github-event'];
    if (
      eventType && ['issues', 'issue_comment'].includes(eventType) &&
      req.body && req.body.issue &&
      req.body.issue.user.login === config.github.botLogin
    ) {
      const { email } = requestUtils.getCreator(req.body.issue);

      try {
        const project = notifications.findProject(req.body, appData.projects);
        const requestUrl = notifications.getRequestUrl(req.body.issue.number, project, getlocalAppRootUrl(req));
        const { subject, content } = notifications.getNotificationText(eventType, req.body, project.name, requestUrl);

        if (email && subject && content) {
          sendMail(email, subject, content)
            .then(response => {
              console.log(response.statusCode, `Email notification sent successfully to ${email}`);
            })
            .catch(error => {
              //error is an instance of SendGridError
              //The full response is attached to error.response
              console.log(error.response);
            });
        }
      }
      catch (error) {
        console.log('An error occcured while attempting to process the following github webhook:');
        console.log(error);
        console.log('webhook data:')
        console.dir(req.body);
        res.json({
          message: 'An error occcured while attempting to process a github webhook',
          webhook: req.body
        })
      }
    }
    res.json({ message: 'web hook processed' });
  });
}
<|endoftext|>


<commit_before>var express = require('express'),
    router = express.Router(),
    pg = require('pg');

// GET /api page.
router.get( '/', function(request, response ) {
  response.render('api', { site_name: 'TrivNode' });
});

// GET /api/clues -- 100 RANDOM clues
router.get('/clues', function(req, res) {
  var results = [];
  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if(err) {
      done();
      console.log(err);
      return res.status(500).json({ success: false, data: err});
    }
    // join with category name
    var query = client.query(
        "SELECT a.category_name AS category, b.question, b.answer FROM clue AS b " +
        "JOIN category AS a ON a.category_id = b.category_id ORDER BY random() LIMIT 100;");
    query.on('row', function(row) {
      results.push(row);
    });
    query.on('end', function() {
      done();
      return res.json(results);
    });
  });
});

// GET /api/clues/cat/{id} -- clues by cat id
router.get('/clues/cat/:id', function(req, res) {
  var results = [];
  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if(err) {
      done();
      console.log(err);
      return res.status(500).json({ success: false, data: err});
    }
    var query = client.query(
        "SELECT a.category_name AS category, b.question, b.answer FROM clue AS b " +
        "JOIN category AS a ON a.category_id = b.category_id " +
        "WHERE b.category_id = $1;", [req.params.id]);
    query.on('row', function(row) {
      results.push(row);
    });
    query.on('end', function() {
      done();
      return res.json(results);
    });
  });
});

// GET /api/cats -- 20 RANDOM categories
router.get('/cats', function(req, res) {
  var results = [];
  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if(err) {
      done();
      console.log(err);
      return res.status(500).json({ success: false, data: err});
    }
    var query = client.query("SELECT * FROM category ORDER BY random() LIMIT 20;");
    query.on('row', function(row) {
      results.push(row);
    });
    query.on('end', function() {
      done();
      return res.json(results);
    });
  });
})

//// GET  /api/cats/r/  -- random categories with optional limit
//router.get('/cats/r/:limit?', function(request, response) {
//  var limit = request.params.limit;
//  if (!limit) {
//    limit = 10;  // default
//  }
//
//  return Clue.getRandomCats(limit, function(err, cats) {
//
//    if (!err) {
//      return response.send(cats);
//    } else {
//      return console.log(err);
//    }
//  });
//
//});

module.exports = router;
<commit_msg>arrow functions<commit_after>const express = require('express');
const  router = express.Router();
const pg = require('pg');

// GET /api page.
router.get( '/', (request, response) => {
  response.render('api', { site_name: 'TrivNode' });
});

// GET /api/clues -- 100 RANDOM clues
router.get('/clues', (req, res) => {
  var results = [];
  pg.connect(process.env.DATABASE_URL, (err, client, done) => {
    if(err) {
      done();
      console.log(err);
      return res.status(500).json({ success: false, data: err});
    }
    // join with category name
    var query = client.query(
        "SELECT a.category_name AS category, b.question, b.answer FROM clue AS b " +
        "JOIN category AS a ON a.category_id = b.category_id ORDER BY random() LIMIT 100;");
    query.on('row', (row) => {
      results.push(row);
    });
    query.on('end', () => {
      done();
      return res.json(results);
    });
  });
});

// GET /api/clues/cat/{id} -- clues by cat id
router.get('/clues/cat/:id', (req, res) => {
  var results = [];
  pg.connect(process.env.DATABASE_URL, (err, client, done) => {
    if(err) {
      done();
      console.log(err);
      return res.status(500).json({ success: false, data: err});
    }
    var query = client.query(
        "SELECT a.category_name AS category, b.question, b.answer FROM clue AS b " +
        "JOIN category AS a ON a.category_id = b.category_id " +
        "WHERE b.category_id = $1;", [req.params.id]);
    query.on('row', (row) => {
      results.push(row);
    });
    query.on('end', () => {
      done();
      return res.json(results);
    });
  });
});

// GET /api/cats -- 20 RANDOM categories
router.get('/cats', (req, res) => {
  var results = [];
  pg.connect(process.env.DATABASE_URL, (err, client, done) => {
    if(err) {
      done();
      console.log(err);
      return res.status(500).json({ success: false, data: err});
    }
    var query = client.query("SELECT * FROM category ORDER BY random() LIMIT 20;");
    query.on('row', (row)  =>{
      results.push(row);
    });
    query.on('end', ()  =>{
      done();
      return res.json(results);
    });
  });
})

//// GET  /api/cats/r/  -- random categories with optional limit
//router.get('/cats/r/:limit?', function(request, response) {
//  var limit = request.params.limit;
//  if (!limit) {
//    limit = 10;  // default
//  }
//
//  return Clue.getRandomCats(limit, function(err, cats) {
//
//    if (!err) {
//      return response.send(cats);
//    } else {
//      return console.log(err);
//    }
//  });
//
//});

module.exports = router;
<|endoftext|>


<commit_before>import React from 'react';
import { Link } from 'react-router';
import './Contributing.scss';

const Contributing = () => (

            Contributing

<Link to="#">How to contribute</Link>

<Link to="#">FAQ</Link>

<Link to="#">Release cycle</Link>

<Link to="#">GitHub</Link>

);

export default Contributing;
<commit_msg>Add Contributing links<commit_after>import React from 'react';
import './Contributing.scss';

const items = [
  {
    title: 'How to contribute',
    url: 'https://github.com/SkygearIO/skygear-server#how-to-contribute',
  },
  {
    title: 'GitHub',
    url: 'https://github.com/skygeario/skygear-server',
  },
  {
    title: 'Releases',
    url: 'https://github.com/SkygearIO/skygear-server/releases',
  },
];

const Contributing = () => (

            Contributing

          {items.map(({ title, url }) => (

{title}

          ))}

);

export default Contributing;
<|endoftext|>


<commit_before>module.exports = function ( name, filter, payload ) {
    'use strict';

    var listeners = require('./listeners'),
        listener = listeners.filter(name, filter);

    console.log(listener);
};<commit_msg>Hooked up the event emitter<commit_after>/**
 * Module that is a function that will get a list of handlers for a named event.
 * Then call each of the handlers with the passed in payload.
 *
 * @param name - Name of the event
 * @param filter - Event filter (node, type, contentid)
 * @param payload - Custom object that will get passed to the event handler
 */
module.exports = function ( name, filter, payload ) {
    'use strict';

    var async = require('async'),
        listeners = require('./listeners'),
        handlers = listeners.filter(name, filter);

    async.each(handlers, function(handler){
        handler(payload);
    });
};<|endoftext|>


<commit_before>/**
 * Copyright (c) ActiveState 2013 - ALL RIGHTS RESERVED.
 */

if (typeof define !== 'function') { var define = require('amdefine')(module) }

define([
    './lib/apps',
    './lib/services',
    './lib/service-instances',
    './lib/service-plans',
    './lib/service-bindings',
    './lib/spaces',
    './lib/users',
    './lib/organizations',
    './lib/domains',
    './lib/routes',
    './lib/quota-definitions',
    './lib/http-client'],
    function (Apps, Services, ServiceInstances, ServicePlans, ServiceBindings, Spaces, Users, Organizations, Domains, Routes, QuotaDefinitions, HttpClient) {

        var api = function (api_endpoint, options) {

            options = options || {};

            this.api_endpoint = api_endpoint;
            this.authorization_endpoint = options.authorization_endpoint || null;
            this.http_client = new HttpClient();
            this.token = options.token || null;
            this.scopes = options.scopes || null;
            this.redirect_uri = options.redirect_uri || null;
            this.client_id = options.client_id || 'cf';
            this.apps = new Apps(this);
            this.users = new Users(this);
            this.services = new Services(this);
            this.service_instances = new ServiceInstances(this);
            this.service_plans = new ServicePlans(this);
            this.service_bindings = new ServiceBindings(this);
            this.spaces = new Spaces(this);
            this.organizations = new Organizations(this);
            this.domains = new Domains(this);
            this.routes = new Routes(this);
            this.quota_definitions = new QuotaDefinitions(this);

            this.initialize();
        };

        var makeErrorMessageFromResponse = function (res) {

            var response_body = res.body;
            if (typeof response_body !== 'string') {
                try {
                    response_body = JSON.stringify(response_body);
                } catch (e) {
                    /* eat it */
                }
            }

            return 'Status: ' + res.status_code + '. Response: ' + response_body;
        };

        api.prototype = {

            initialize: function () {
                /* faux-constructor to use as an extension point for derived clients */
            },

            makeAuthorizationHeader: function () {
                return 'bearer ' + this.token;
            },

            addAuthorizationHeaderToOptions: function (options) {
                options.headers['Authorization'] = this.makeAuthorizationHeader();
            },

            normalizeOptions: function (options) {

                options = options || {};
                options.headers = options.headers || {};

                if (this.token) {
                    this.addAuthorizationHeaderToOptions(options);
                }

                return options;
            },

            getAuthorizationEndpoint: function (done) {

                var self = this;
                if (this.authorization_endpoint) {
                    setTimeout(function () {
                        done(null, self.authorization_endpoint)
                    }, 1);
                } else {
                    this.get('/info', {status_code: 200}, function (err, res) {
                        if (err) {return done(err);}
                        self.authorization_endpoint = res.body.authorization_endpoint;
                        done(null, self.authorization_endpoint);
                    });
                }
            },

            authorize: function () {
                if (typeof window !== 'undefined') {
                    this.authorizeBrowser();
                } else {
                    this.authorizeNodejs();
                }
            },

            authorizeBrowser: function () {

                if (this.authorizing) {return;}

                var self = this;
                this.token = null;
                this.authorizing = true;

                this.getAuthorizationEndpoint(function (err, authorization_endpoint) {

                    var oauth_url = authorization_endpoint + '/oauth/authorize?' +
                        'response_type=token&' +
                        'client_id=' + encodeURIComponent(self.client_id) + '&' +
                        'redirect_uri=' + encodeURIComponent(self.redirect_uri);

                    if (self.scopes) {
                        oauth_url = oauth_url + '&scope=' + encodeURIComponent(self.scopes);
                    }

                    window.location = oauth_url;
                });
            },

            authorizeNodejs: function (res, done) {

                /**
                 * 1. If acting on behalf of a user e.g. just passing through a user token then a 401 is an error that should bubble out to the user
                 * 2. If acting as a resource server a 401 should trigger a refresh token attempt and failing that it should try to re-authenticate using client credentials
                 *
                 * Assume 1. for now until 2. is implemented. Users of this lib could override this function to patch in their desired behaviour.
                 */
                if (done) {
                    done(new Error(makeErrorMessageFromResponse(res)), res);
                }
            },

            processResponse: function (options, err, res, done) {
                if (err) {return done(err, res);}
                if (res.status_code === 401 && !options.ignore_unauthorized && typeof window !== 'undefined') {return this.authorizeBrowser();}
                if (res.status_code === 401 && !options.ignore_unauthorized && typeof window === 'undefined') {return this.authorizeNodejs(res, done);}
                if (options.status_code && options.status_code !== res.status_code) {return done(new Error(makeErrorMessageFromResponse(res)), res);}
                if (options.status_codes && options.status_codes.indexOf(res.status_code) === -1) {return done(new Error(makeErrorMessageFromResponse(res)), res);}
                this.marshalResponse(options, res, done);
            },

            marshalResponse: function (options, res, done) {
                /* this is effectively a no-op implementation. derived clients can use it to modify the response. */
                var self = this;
                setTimeout(function () {
                    done.call(self, null, res);
                }, 1);
            },

            executeRequest: function (path, options, done) {

                // Allow the special case where api components may need to talk to the UAA with it's own host.
                var prepend_host = true;
                if (path.indexOf('http') !== -1) {
                    prepend_host = false;
                }

                var self = this;
                this.http_client.request(
                    (prepend_host ? this.api_endpoint : '') + path + (options.query ? options.query : ''),
                    options,
                    function (err, res) {
                        self.processResponse(options, err, res, done);
                    }
                );
            },

            getApiInfo: function (done) {

                this.get('/v2/info', {status_code: 200}, function (err, res) {
                    done(err, res ? res.body : null);
                });
            },

            get: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'GET';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 200;
                }

                this.executeRequest(path, options, done);
            },

            delete_: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'DELETE';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 200;
                }

                this.executeRequest(path, options, done);
            },

            put: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'PUT';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 200;
                }

                this.executeRequest(path, options, done);
            },

            post: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'POST';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 201;
                }

                this.executeRequest(path, options, done);
            }
        };

        return api;
    }
);
<commit_msg>add extension point to api that allows for interception/manipulation of request before it is sent<commit_after>/**
 * Copyright (c) ActiveState 2013 - ALL RIGHTS RESERVED.
 */

if (typeof define !== 'function') { var define = require('amdefine')(module) }

define([
    './lib/apps',
    './lib/services',
    './lib/service-instances',
    './lib/service-plans',
    './lib/service-bindings',
    './lib/spaces',
    './lib/users',
    './lib/organizations',
    './lib/domains',
    './lib/routes',
    './lib/quota-definitions',
    './lib/http-client'],
    function (Apps, Services, ServiceInstances, ServicePlans, ServiceBindings, Spaces, Users, Organizations, Domains, Routes, QuotaDefinitions, HttpClient) {

        var api = function (api_endpoint, options) {

            options = options || {};

            this.api_endpoint = api_endpoint;
            this.authorization_endpoint = options.authorization_endpoint || null;
            this.http_client = new HttpClient();
            this.token = options.token || null;
            this.scopes = options.scopes || null;
            this.redirect_uri = options.redirect_uri || null;
            this.client_id = options.client_id || 'cf';
            this.apps = new Apps(this);
            this.users = new Users(this);
            this.services = new Services(this);
            this.service_instances = new ServiceInstances(this);
            this.service_plans = new ServicePlans(this);
            this.service_bindings = new ServiceBindings(this);
            this.spaces = new Spaces(this);
            this.organizations = new Organizations(this);
            this.domains = new Domains(this);
            this.routes = new Routes(this);
            this.quota_definitions = new QuotaDefinitions(this);

            this.initialize();
        };

        var makeErrorMessageFromResponse = function (res) {

            var response_body = res.body;
            if (typeof response_body !== 'string') {
                try {
                    response_body = JSON.stringify(response_body);
                } catch (e) {
                    /* eat it */
                }
            }

            return 'Status: ' + res.status_code + '. Response: ' + response_body;
        };

        api.prototype = {

            initialize: function () {
                /* faux-constructor to use as an extension point for derived clients */
            },

            makeAuthorizationHeader: function () {
                return 'bearer ' + this.token;
            },

            addAuthorizationHeaderToOptions: function (options) {
                options.headers['Authorization'] = this.makeAuthorizationHeader();
            },

            normalizeOptions: function (options) {

                options = options || {};
                options.headers = options.headers || {};

                if (this.token) {
                    this.addAuthorizationHeaderToOptions(options);
                }

                return options;
            },

            getAuthorizationEndpoint: function (done) {

                var self = this;
                if (this.authorization_endpoint) {
                    setTimeout(function () {
                        done(null, self.authorization_endpoint)
                    }, 1);
                } else {
                    this.get('/info', {status_code: 200}, function (err, res) {
                        if (err) {return done(err);}
                        self.authorization_endpoint = res.body.authorization_endpoint;
                        done(null, self.authorization_endpoint);
                    });
                }
            },

            authorize: function () {
                if (typeof window !== 'undefined') {
                    this.authorizeBrowser();
                } else {
                    this.authorizeNodejs();
                }
            },

            authorizeBrowser: function () {

                if (this.authorizing) {return;}

                var self = this;
                this.token = null;
                this.authorizing = true;

                this.getAuthorizationEndpoint(function (err, authorization_endpoint) {

                    var oauth_url = authorization_endpoint + '/oauth/authorize?' +
                        'response_type=token&' +
                        'client_id=' + encodeURIComponent(self.client_id) + '&' +
                        'redirect_uri=' + encodeURIComponent(self.redirect_uri);

                    if (self.scopes) {
                        oauth_url = oauth_url + '&scope=' + encodeURIComponent(self.scopes);
                    }

                    window.location = oauth_url;
                });
            },

            authorizeNodejs: function (res, done) {

                /**
                 * 1. If acting on behalf of a user e.g. just passing through a user token then a 401 is an error that should bubble out to the user
                 * 2. If acting as a resource server a 401 should trigger a refresh token attempt and failing that it should try to re-authenticate using client credentials
                 *
                 * Assume 1. for now until 2. is implemented. Users of this lib could override this function to patch in their desired behaviour.
                 */
                if (done) {
                    done(new Error(makeErrorMessageFromResponse(res)), res);
                }
            },

            processResponse: function (options, err, res, done) {
                if (err) {return done(err, res);}
                if (res.status_code === 401 && !options.ignore_unauthorized && typeof window !== 'undefined') {return this.authorizeBrowser();}
                if (res.status_code === 401 && !options.ignore_unauthorized && typeof window === 'undefined') {return this.authorizeNodejs(res, done);}
                if (options.status_code && options.status_code !== res.status_code) {return done(new Error(makeErrorMessageFromResponse(res)), res);}
                if (options.status_codes && options.status_codes.indexOf(res.status_code) === -1) {return done(new Error(makeErrorMessageFromResponse(res)), res);}
                this.marshalResponse(options, res, done);
            },

            marshalResponse: function (options, res, done) {
                /* this is effectively a no-op implementation. derived clients can use it to modify the response. */
                var self = this;
                setTimeout(function () {
                    done.call(self, null, res);
                }, 1);
            },

            marshalRequest: function (url, options, done) {
                /* this is effectively a no-op implementation. derived clients can use it to modify the request. */
                var self = this;
                setTimeout(function () {
                    done.call(self, null, url, options);
                }, 1);
            },

            executeRequest: function (path, options, done) {

                // Allow the special case where api components may need to talk to the UAA with it's own host.
                var prepend_host = true;
                if (path.indexOf('http') !== -1) {
                    prepend_host = false;
                }

                var self = this;
                this.marshalRequest(path, options, function (err, path, options) {
                    if (err) {return done(err);}

                    self.http_client.request(
                        (prepend_host ? self.api_endpoint : '') + path + (options.query ? options.query : ''),
                        options,
                        function (err, res) {
                            self.processResponse(options, err, res, done);
                        }
                    );
                });
            },

            getApiInfo: function (done) {

                this.get('/v2/info', {status_code: 200}, function (err, res) {
                    done(err, res ? res.body : null);
                });
            },

            get: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'GET';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 200;
                }

                this.executeRequest(path, options, done);
            },

            delete_: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'DELETE';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 200;
                }

                this.executeRequest(path, options, done);
            },

            put: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'PUT';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 200;
                }

                this.executeRequest(path, options, done);
            },

            post: function (path, options, done) {

                options = this.normalizeOptions(options);
                options.verb = 'POST';

                if (!options.status_code && !options.status_codes) {
                    options.status_code = 201;
                }

                this.executeRequest(path, options, done);
            }
        };

        return api;
    }
);
<|endoftext|>


<commit_before>'use strict';
var tough = require('tough-cookie');
var Store = tough.Store;
var permuteDomain = tough.permuteDomain;
var permutePath = tough.permutePath;
var util = require('util');
var fs = require('fs');

function FileCookieStore(filePath) {
    Store.call(this);
    this.idx = {}; // idx is memory cache
    this.filePath = filePath;
    var self = this;
    loadFromFile(this.filePath, function(dataJson) {
        if (dataJson)
            self.idx = dataJson;
    })
}

util.inherits(FileCookieStore, Store);
exports.FileCookieStore = FileCookieStore;
FileCookieStore.prototype.idx = null;
FileCookieStore.prototype.synchronous = true;

// force a default depth:
FileCookieStore.prototype.inspect = function() {
    return "{ idx: " + util.inspect(this.idx, false, 2) + ' }';
};

FileCookieStore.prototype.findCookie = function(domain, path, key, cb) {
    if (!this.idx[domain]) {
        return cb(null, undefined);
    }
    if (!this.idx[domain][path]) {
        return cb(null, undefined);
    }
    return cb(null, this.idx[domain][path][key] || null);
};

FileCookieStore.prototype.findCookies = function(domain, path, cb) {
    var results = [];
    if (!domain) {
        return cb(null, []);
    }

    var pathMatcher;
    if (!path) {
        // null or '/' means "all paths"
        pathMatcher = function matchAll(domainIndex) {
            for (var curPath in domainIndex) {
                var pathIndex = domainIndex[curPath];
                for (var key in pathIndex) {
                    results.push(pathIndex[key]);
                }
            }
        };

    } else if (path === '/') {
        pathMatcher = function matchSlash(domainIndex) {
            var pathIndex = domainIndex['/'];
            if (!pathIndex) {
                return;
            }
            for (var key in pathIndex) {
                results.push(pathIndex[key]);
            }
        };

    } else {
        var paths = permutePath(path) || [path];
        pathMatcher = function matchRFC(domainIndex) {
            paths.forEach(function(curPath) {
                var pathIndex = domainIndex[curPath];
                if (!pathIndex) {
                    return;
                }
                for (var key in pathIndex) {
                    results.push(pathIndex[key]);
                }
            });
        };
    }

    var domains = permuteDomain(domain) || [domain];
    var idx = this.idx;
    domains.forEach(function(curDomain) {
        var domainIndex = idx[curDomain];
        if (!domainIndex) {
            return;
        }
        pathMatcher(domainIndex);
    });

    cb(null, results);
};

FileCookieStore.prototype.putCookie = function(cookie, cb) {
    if (!this.idx[cookie.domain]) {
        this.idx[cookie.domain] = {};
    }
    if (!this.idx[cookie.domain][cookie.path]) {
        this.idx[cookie.domain][cookie.path] = {};
    }
    this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
    saveToFile(this.filePath, this.idx, function() {
        cb(null);
    });
};

FileCookieStore.prototype.updateCookie = function updateCookie(oldCookie, newCookie, cb) {
    // updateCookie() may avoid updating cookies that are identical.  For example,
    // lastAccessed may not be important to some stores and an equality
    // comparison could exclude that field.
    this.putCookie(newCookie, cb);
};

FileCookieStore.prototype.removeCookie = function removeCookie(domain, path, key, cb) {
    if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
        delete this.idx[domain][path][key];
    }
    saveToFile(this.filePath, this.idx, function() {
        cb(null);
    });
};

FileCookieStore.prototype.removeCookies = function removeCookies(domain, path, cb) {
    if (this.idx[domain]) {
        if (path) {
            delete this.idx[domain][path];
        } else {
            delete this.idx[domain];
        }
    }
    saveToFile(this.filePath, this.idx, function() {
        return cb(null);
    });
};

FileCookieStore.prototype.checkExpired = function(domain, path, key) {
    var domains = []
    if (domain) domains = [domain]
    else domains = Object.keys(this.idx)
    var isExpired = false
    var that = this
    domains.forEach(function(d) {
        var paths = []
        if (path) paths = [path]
        else paths = Object.keys(that.idx[d])
        paths.forEach(function(p) {
            var keys = []
            if (key) keys = [key]
            else keys = Object.keys(that.idx[d][p])
            keys.forEach(function(k) {
                var expiresDate = that.idx[d][p][k].expires
                var now = new Date()
                if (isFinite(expiresDate)) {
                    if (expiresDate - now < 30 * 60)
                        isExpired = true
                }
            })
        })
    })
    return isExpired
}

FileCookieStore.prototype.isExpired = function() {
    return this.checkExpired(null, null, null)
}

function saveToFile(filePath, data, cb) {
    var dataJson = JSON.stringify(data);
    fs.writeFileSync(filePath, dataJson);
    cb();
}

function loadFromFile(filePath, cb) {
    if (fs.existsSync(filePath)) {
        var data = fs.readFileSync(filePath, 'utf8');
    } else {
        var data = null;
    }

    if (data) {
        try {
            var dataJson = JSON.parse(data);
        } catch (e) {
            throw new Error('Could not parse cookie file ' + filePath + '. Please ensure it is not corrupted.');
        }
    } else {
        var dataJson = null;
    }

    for (var domainName in dataJson) {
        for (var pathName in dataJson[domainName]) {
            for (var cookieName in dataJson[domainName][pathName]) {
                dataJson[domainName][pathName][cookieName] = tough.fromJSON(JSON.stringify(dataJson[domainName][pathName][cookieName]));
            }
        }
    }
    cb(dataJson);
}<commit_msg>Check empty cookie feature<commit_after>'use strict';
var tough = require('tough-cookie');
var Store = tough.Store;
var permuteDomain = tough.permuteDomain;
var permutePath = tough.permutePath;
var util = require('util');
var fs = require('fs');

function FileCookieStore(filePath) {
    Store.call(this);
    this.idx = {}; // idx is memory cache
    this.filePath = filePath;
    var self = this;
    loadFromFile(this.filePath, function(dataJson) {
        if (dataJson)
            self.idx = dataJson;
    })
}

util.inherits(FileCookieStore, Store);
exports.FileCookieStore = FileCookieStore;
FileCookieStore.prototype.idx = null;
FileCookieStore.prototype.synchronous = true;

// force a default depth:
FileCookieStore.prototype.inspect = function() {
    return "{ idx: " + util.inspect(this.idx, false, 2) + ' }';
};

FileCookieStore.prototype.findCookie = function(domain, path, key, cb) {
    if (!this.idx[domain]) {
        return cb(null, undefined);
    }
    if (!this.idx[domain][path]) {
        return cb(null, undefined);
    }
    return cb(null, this.idx[domain][path][key] || null);
};

FileCookieStore.prototype.findCookies = function(domain, path, cb) {
    var results = [];
    if (!domain) {
        return cb(null, []);
    }

    var pathMatcher;
    if (!path) {
        // null or '/' means "all paths"
        pathMatcher = function matchAll(domainIndex) {
            for (var curPath in domainIndex) {
                var pathIndex = domainIndex[curPath];
                for (var key in pathIndex) {
                    results.push(pathIndex[key]);
                }
            }
        };

    } else if (path === '/') {
        pathMatcher = function matchSlash(domainIndex) {
            var pathIndex = domainIndex['/'];
            if (!pathIndex) {
                return;
            }
            for (var key in pathIndex) {
                results.push(pathIndex[key]);
            }
        };

    } else {
        var paths = permutePath(path) || [path];
        pathMatcher = function matchRFC(domainIndex) {
            paths.forEach(function(curPath) {
                var pathIndex = domainIndex[curPath];
                if (!pathIndex) {
                    return;
                }
                for (var key in pathIndex) {
                    results.push(pathIndex[key]);
                }
            });
        };
    }

    var domains = permuteDomain(domain) || [domain];
    var idx = this.idx;
    domains.forEach(function(curDomain) {
        var domainIndex = idx[curDomain];
        if (!domainIndex) {
            return;
        }
        pathMatcher(domainIndex);
    });

    cb(null, results);
};

FileCookieStore.prototype.putCookie = function(cookie, cb) {
    if (!this.idx[cookie.domain]) {
        this.idx[cookie.domain] = {};
    }
    if (!this.idx[cookie.domain][cookie.path]) {
        this.idx[cookie.domain][cookie.path] = {};
    }
    this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
    saveToFile(this.filePath, this.idx, function() {
        cb(null);
    });
};

FileCookieStore.prototype.updateCookie = function updateCookie(oldCookie, newCookie, cb) {
    // updateCookie() may avoid updating cookies that are identical.  For example,
    // lastAccessed may not be important to some stores and an equality
    // comparison could exclude that field.
    this.putCookie(newCookie, cb);
};

FileCookieStore.prototype.removeCookie = function removeCookie(domain, path, key, cb) {
    if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
        delete this.idx[domain][path][key];
    }
    saveToFile(this.filePath, this.idx, function() {
        cb(null);
    });
};

FileCookieStore.prototype.removeCookies = function removeCookies(domain, path, cb) {
    if (this.idx[domain]) {
        if (path) {
            delete this.idx[domain][path];
        } else {
            delete this.idx[domain];
        }
    }
    saveToFile(this.filePath, this.idx, function() {
        return cb(null);
    });
};

FileCookieStore.prototype.checkExpired = function(domain, path, key) {
    var domains = []
    if (domain) domains = [domain]
    else domains = Object.keys(this.idx)
    var isExpired = false
    var that = this
    domains.forEach(function(d) {
        var paths = []
        if (path) paths = [path]
        else paths = Object.keys(that.idx[d])
        paths.forEach(function(p) {
            var keys = []
            if (key) keys = [key]
            else keys = Object.keys(that.idx[d][p])
            keys.forEach(function(k) {
                var expiresDate = that.idx[d][p][k].expires
                var now = new Date()
                if (isFinite(expiresDate)) {
                    if (expiresDate - now < 30 * 60)
                        isExpired = true
                }
            })
        })
    })
    return isExpired
}

FileCookieStore.prototype.isExpired = function() {
    return this.checkExpired(null, null, null)
}

FileCookieStore.prototype.isEmpty = function (){
    return isEmptyObject(this.idx);
}

function saveToFile(filePath, data, cb) {
    var dataJson = JSON.stringify(data);
    fs.writeFileSync(filePath, dataJson);
    cb();
}

function loadFromFile(filePath, cb) {
    if (fs.existsSync(filePath)) {
        var data = fs.readFileSync(filePath, 'utf8');
    } else {
        var data = null;
    }

    if (data) {
        try {
            var dataJson = JSON.parse(data);
        } catch (e) {
            throw new Error('Could not parse cookie file ' + filePath + '. Please ensure it is not corrupted.');
        }
    } else {
        var dataJson = null;
    }

    for (var domainName in dataJson) {
        for (var pathName in dataJson[domainName]) {
            for (var cookieName in dataJson[domainName][pathName]) {
                dataJson[domainName][pathName][cookieName] = tough.fromJSON(JSON.stringify(dataJson[domainName][pathName][cookieName]));
            }
        }
    }
    cb(dataJson);
}

function isEmptyObject(obj) {
    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            return false;
        }
    }
    return true;
}<|endoftext|>