<commit_before>// External Dependencies
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

// Our Dependencies
import Book from '../Book';
import * as BooksAPI from '../../BooksAPI'

export default class BookDetail extends Component {
  static propTypes = {
    bookId: PropTypes.string.isRequired,
    isUpdatingShelf: PropTypes.bool.isRequired,
    onShelfChange: PropTypes.func.isRequired,
  }

  state = {
    book: {},
    isLoading: true,
  }

  getBook = (bookId) => (
    BooksAPI.get(this.props.bookId)
  )

  componentDidMount() {
    this.getBook(this.props.bookId).then(book =>
      this.setState({ book, isLoading: false })
    )
  }

  componentWillReceiveProps(nextProps) {
    this.getBook(nextProps.bookId).then(book =>
      this.setState({ book, isLoading: false })
    )
  }

  render() {
    const { book, isLoading} = this.state;
    const { onShelfChange, isUpdatingShelf } = this.props;

    return (

        { isLoading && (

              <Link to="/" className="back-button"> Close </Link>

                Loading...

        )}

        {!isLoading && (

                <Link to="/" className="back-button"> Close </Link>

                  {book.title}
                  by {book.authors.join(', ')}

            {/* Container */}

                <Book
                  book={book}
                  onShelfChange={onShelfChange}
                />

              {/* Meta  */}

                { book.pageCount &&
                   Page Count:
                    {` ${book.pageCount}`}

                }

                { book.ratingsCount &&
                   Rating:
                    {` ${book.ratingsCount}`}

                }

                { book.categories &&
                   Category:
                    {` ${book.categories.join(', ')}`}

                }

                {book.previewLink &&

                    more info

                }

                {/* Description  */}

{book.description}

                    <input
                      id="currentlyReading"
                      type="radio"
                      name="shelf"
                      checked={book.shelf === "currentlyReading"}
                      onChange={(e) => onShelfChange(book, e.target.id)}
                    />
                    Reading

                    <input
                      id="wantToRead"
                      type="radio"
                      name="shelf"
                      checked={book.shelf === "wantToRead"}
                      onChange={(e) => onShelfChange(book, e.target.id)}
                    />
                    Want

                    <input
                      id="read"
                      type="radio"
                      name="shelf"
                      checked={book.shelf === "read"}
                      onChange={(e) => onShelfChange(book, e.target.id)}
                    />
                    Read

        )}

    )
  }
}<commit_msg>test: Asdded a istanbul ignore to componentDidMount<commit_after>// External Dependencies
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

// Our Dependencies
import Book from '../Book';
import * as BooksAPI from '../../BooksAPI'

export default class BookDetail extends Component {
  static propTypes = {
    bookId: PropTypes.string.isRequired,
    isUpdatingShelf: PropTypes.bool.isRequired,
    onShelfChange: PropTypes.func.isRequired,
  }

  state = {
    book: {},
    isLoading: true,
  }

  getBook = (bookId) => (
    BooksAPI.get(this.props.bookId)
  )

  // istanbul ignore next
  componentDidMount() {
    this.getBook(this.props.bookId).then(book =>
      this.setState({ book, isLoading: false })
    )
  }

  componentWillReceiveProps(nextProps) {
    this.getBook(nextProps.bookId).then(book =>
      this.setState({ book, isLoading: false })
    )
  }

  render() {
    const { book, isLoading} = this.state;
    const { onShelfChange, isUpdatingShelf } = this.props;

    return (

        { isLoading && (

              <Link to="/" className="back-button"> Close </Link>

                Loading...

        )}

        {!isLoading && (

                <Link to="/" className="back-button"> Close </Link>

                  {book.title}
                  by {book.authors.join(', ')}

            {/* Container */}

                <Book
                  book={book}
                  onShelfChange={onShelfChange}
                />

              {/* Meta  */}

                { book.pageCount &&
                   Page Count:
                    {` ${book.pageCount}`}

                }

                { book.ratingsCount &&
                   Rating:
                    {` ${book.ratingsCount}`}

                }

                { book.categories &&
                   Category:
                    {` ${book.categories.join(', ')}`}

                }

                {book.previewLink &&

                    more info

                }

                {/* Description  */}

{book.description}

                    <input
                      id="currentlyReading"
                      type="radio"
                      name="shelf"
                      checked={book.shelf === "currentlyReading"}
                      onChange={(e) => onShelfChange(book, e.target.id)}
                    />
                    Reading

                    <input
                      id="wantToRead"
                      type="radio"
                      name="shelf"
                      checked={book.shelf === "wantToRead"}
                      onChange={(e) => onShelfChange(book, e.target.id)}
                    />
                    Want

                    <input
                      id="read"
                      type="radio"
                      name="shelf"
                      checked={book.shelf === "read"}
                      onChange={(e) => onShelfChange(book, e.target.id)}
                    />
                    Read

        )}

    )
  }
}<|endoftext|>


<commit_before>/**
 * Factory for tools.
 *
 * @class
 * @extends OO.Factory
 * @constructor
 */
OO.ui.ToolFactory = function OoUiToolFactory() {
	// Parent constructor
	OO.ui.ToolFactory.super.call( this );
};

/* Setup */

OO.inheritClass( OO.ui.ToolFactory, OO.Factory );

/* Methods */

/** */
OO.ui.ToolFactory.prototype.getTools = function ( include, exclude, promote, demote ) {
	var i, len, included, promoted, demoted,
		auto = [],
		used = {};

	// Collect included and not excluded tools
	included = OO.simpleArrayDifference( this.extract( include ), this.extract( exclude ) );

	// Promotion
	promoted = this.extract( promote, used );
	demoted = this.extract( demote, used );

	// Auto
	for ( i = 0, len = included.length; i < len; i++ ) {
		if ( !used[included[i]] ) {
			auto.push( included[i] );
		}
	}

	return promoted.concat( auto ).concat( demoted );
};

/**
 * Get a flat list of names from a list of names or groups.
 *
 * Tools can be specified in the following ways:
 *
 * - A specific tool: `{ name: 'tool-name' }` or `'tool-name'`
 * - All tools in a group: `{ group: 'group-name' }`
 * - All tools: `'*'`
 *
 * @private
 * @param {Array|string} collection List of tools
 * @param {Object} [used] Object with names that should be skipped as properties; extracted
 *  names will be added as properties
 * @return {string[]} List of extracted names
 */
OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
	var i, len, item, name, tool,
		names = [];

	if ( collection === '*' ) {
		for ( name in this.registry ) {
			tool = this.registry[name];
			if (
				// Only add tools by group name when auto-add is enabled
				tool.static.autoAddToCatchall &&
				// Exclude already used tools
				( !used || !used[name] )
			) {
				names.push( name );
				if ( used ) {
					used[name] = true;
				}
			}
		}
	} else if ( $.isArray( collection ) ) {
		for ( i = 0, len = collection.length; i < len; i++ ) {
			item = collection[i];
			// Allow plain strings as shorthand for named tools
			if ( typeof item === 'string' ) {
				item = { name: item };
			}
			if ( OO.isPlainObject( item ) ) {
				if ( item.group ) {
					for ( name in this.registry ) {
						tool = this.registry[name];
						if (
							// Include tools with matching group
							tool.static.group === item.group &&
							// Only add tools by group name when auto-add is enabled
							tool.static.autoAddToGroup &&
							// Exclude already used tools
							( !used || !used[name] )
						) {
							names.push( name );
							if ( used ) {
								used[name] = true;
							}
						}
					}
				// Include tools with matching name and exclude already used tools
				} else if ( item.name && ( !used || !used[item.name] ) ) {
					names.push( item.name );
					if ( used ) {
						used[item.name] = true;
					}
				}
			}
		}
	}
	return names;
};
<commit_msg>Add missing documentation to ToolFactory<commit_after>/**
 * Factory for tools.
 *
 * @class
 * @extends OO.Factory
 * @constructor
 */
OO.ui.ToolFactory = function OoUiToolFactory() {
	// Parent constructor
	OO.ui.ToolFactory.super.call( this );
};

/* Setup */

OO.inheritClass( OO.ui.ToolFactory, OO.Factory );

/* Methods */

/**
 * Get tools from the factory
 *
 * @param {Array} include Included tools
 * @param {Array} exclude Excluded tools
 * @param {Array} promote Promoted tools
 * @param {Array} demote Demoted tools
 * @return {string[]} List of tools
 */
OO.ui.ToolFactory.prototype.getTools = function ( include, exclude, promote, demote ) {
	var i, len, included, promoted, demoted,
		auto = [],
		used = {};

	// Collect included and not excluded tools
	included = OO.simpleArrayDifference( this.extract( include ), this.extract( exclude ) );

	// Promotion
	promoted = this.extract( promote, used );
	demoted = this.extract( demote, used );

	// Auto
	for ( i = 0, len = included.length; i < len; i++ ) {
		if ( !used[included[i]] ) {
			auto.push( included[i] );
		}
	}

	return promoted.concat( auto ).concat( demoted );
};

/**
 * Get a flat list of names from a list of names or groups.
 *
 * Tools can be specified in the following ways:
 *
 * - A specific tool: `{ name: 'tool-name' }` or `'tool-name'`
 * - All tools in a group: `{ group: 'group-name' }`
 * - All tools: `'*'`
 *
 * @private
 * @param {Array|string} collection List of tools
 * @param {Object} [used] Object with names that should be skipped as properties; extracted
 *  names will be added as properties
 * @return {string[]} List of extracted names
 */
OO.ui.ToolFactory.prototype.extract = function ( collection, used ) {
	var i, len, item, name, tool,
		names = [];

	if ( collection === '*' ) {
		for ( name in this.registry ) {
			tool = this.registry[name];
			if (
				// Only add tools by group name when auto-add is enabled
				tool.static.autoAddToCatchall &&
				// Exclude already used tools
				( !used || !used[name] )
			) {
				names.push( name );
				if ( used ) {
					used[name] = true;
				}
			}
		}
	} else if ( $.isArray( collection ) ) {
		for ( i = 0, len = collection.length; i < len; i++ ) {
			item = collection[i];
			// Allow plain strings as shorthand for named tools
			if ( typeof item === 'string' ) {
				item = { name: item };
			}
			if ( OO.isPlainObject( item ) ) {
				if ( item.group ) {
					for ( name in this.registry ) {
						tool = this.registry[name];
						if (
							// Include tools with matching group
							tool.static.group === item.group &&
							// Only add tools by group name when auto-add is enabled
							tool.static.autoAddToGroup &&
							// Exclude already used tools
							( !used || !used[name] )
						) {
							names.push( name );
							if ( used ) {
								used[name] = true;
							}
						}
					}
				// Include tools with matching name and exclude already used tools
				} else if ( item.name && ( !used || !used[item.name] ) ) {
					names.push( item.name );
					if ( used ) {
						used[item.name] = true;
					}
				}
			}
		}
	}
	return names;
};
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import HiGlassComponent from './HiGlassComponent';

export { default as ChromosomeInfo } from './ChromosomeInfo.js';
export { default as HiGlassComponent } from './HiGlassComponent';
export { ChromosomeInfo } from './ChromosomeInfo';

const launch = (element, config, options) => {
  /**
   * The instance's public API will be passed into the callback
   *
   * @param   {Object} higlass - HiGlass instance
   * @return  {Object} The instance's public API
   */
  let component;
  ReactDOM.render(
    <HiGlassComponent
      ref={(c) => { component = c; }}
      options={options || {}}
      viewConfig={config}
    />,
    element,
  );
  return component;
};

/**
 * Create a HiGlass component
 *
 * @param  {Object}  element  DOM element the HiGlass component should be
 *   associated to.
 * @param  {Object|String}  viewConfig  Dictionary or URL of a view config.
 * @param  {Object}  options  Dictionary of public options.
 * @param  {Function}  callback  Callback function for the API.
 * @return  {Object}  Newly created HiGlass component.
 */
export const createHgComponent = (element, viewConfig, options, callback) => {
  /**
   * Available options:
   *
   *  bounded: [true/false]
   *      Fit the container to the bounds of the element
   */
  const hg = launch(element, viewConfig, options);
  try {
    callback(hg.api);
    console.warn(
      'Deprecated use of `createHgComponent`. The api is directly returned ' +
      'now.'
    );
  } catch (e) { /* Nothing */ }
  return hg.api;
};

export const viewer = createHgComponent;

export default createHgComponent;
<commit_msg>Fix double export error<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import HiGlassComponent from './HiGlassComponent';

export { default as ChromosomeInfo } from './ChromosomeInfo';
export { default as HiGlassComponent } from './HiGlassComponent';

const launch = (element, config, options) => {
  /**
   * The instance's public API will be passed into the callback
   *
   * @param   {Object} higlass - HiGlass instance
   * @return  {Object} The instance's public API
   */
  let component;
  ReactDOM.render(
    <HiGlassComponent
      ref={(c) => { component = c; }}
      options={options || {}}
      viewConfig={config}
    />,
    element,
  );
  return component;
};

/**
 * Create a HiGlass component
 *
 * @param  {Object}  element  DOM element the HiGlass component should be
 *   associated to.
 * @param  {Object|String}  viewConfig  Dictionary or URL of a view config.
 * @param  {Object}  options  Dictionary of public options.
 * @param  {Function}  callback  Callback function for the API.
 * @return  {Object}  Newly created HiGlass component.
 */
export const createHgComponent = (element, viewConfig, options, callback) => {
  /**
   * Available options:
   *
   *  bounded: [true/false]
   *      Fit the container to the bounds of the element
   */
  const hg = launch(element, viewConfig, options);
  try {
    callback(hg.api);
    console.warn(
      'Deprecated use of `createHgComponent`. The api is directly returned ' +
      'now.'
    );
  } catch (e) { /* Nothing */ }
  return hg.api;
};

export const viewer = createHgComponent;

export default createHgComponent;
<|endoftext|>


<commit_before>// Substance.Document 0.4.0
// (c) 2010-2013 Michael Aufreiter
// Substance.Document may be freely distributed under the MIT license.
// For all details and documentation:
// http://interior.substance.io/modules/document.html

(function(root) {

// Import
// ========

var _,
    util,
    errors,
    Chronicle,
    ot,
    Data;

if (typeof exports !== 'undefined') {
  _    = require('underscore');
  ot   = require('./lib/operation');
  util   = require('./lib/util/util');
  errors   = require('./lib/util/errors');
  Chronicle = require('./lib/chronicle/chronicle');
  ot = require('./lib/chronicle/lib/ot/index');
  Data = require('./lib/data/data');
} else {
  _ = root._;
  ot = root.ot;
  util = root.Substance.util;
  errors   = root.Substance.errors;
  Chronicle = root.Substance.Chronicle;
  ot = Chronicle.ot;
  Data = root.Substance.Data;
}

// Default Document Schema
// --------

var SCHEMA = {
  // Static indexes
  "indexes": {
    // all comments are now indexed by node association
    "comments": {
      "type": "comment",
      "properties": ["node"]
    },
    // All annotations are now indexed by node
    "annotations": {
      "type": "annotation",
      "properties": ["node"]
    }
  },

  "types": {
    // Specific type for substance documents, holding all content elements
    "content": {
      "properties": {
      }
    },

    "text": {
      "parent": "content",
      "properties": {
        "content": "string"
      }
    },

    "document": {
      "properties": {
        "views": ["array", "view"],
        "title": "string",
        "abstract": "string"
      }
    },

    "view": {
      "properties": {
        "nodes": ["array", "content"]
      }
    },

    "codeblock": {
      "parent": "content",
      "properties": {
        "content": "string"
      }
    },

    "image": {
      "parent": "content",
      "properties": {
        "large": "string",
        "medium": "string",
        "caption": "string"
      }
    },

    "heading": {
      "parent": "content",
      "properties": {
        "content": "string",
        "level": "number"
      }
    },

    // Annotations
    "annotation": {
      "properties": {
        "node": "content",
        "property": "string",
        "range": "object"
      }
    },

    "strong": {
      "parent": "annotation",
      "properties": {
      }
    },

    "emphasis": {
      "properties": {
      },
      "parent": "annotation"
    },

    "code": {
      "parent": "annotation",
      "properties": {
      }
    },

    "link": {
      "parent": "annotation",
      "properties": {
        "url": "string"
      }
    },

    "idea": {
      "parent": "annotation",
      "properties": {
      }
    },

    "error": {
      "parent": "annotation",
      "properties": {
      }
    },

    "question": {
      "parent": "annotation",
      "properties": {
      }
    },

    // Comments
    "comment": {
      "properties": {
        "content": "string",
        "created_at": "string", // should be date
        "creator": "string", // should be date
        "node": "node" // references either a content node or annotation
      }
    }
  }
};

// Seed Data
// ========
//
// Some pre-defined nodes that are required for each document
// `document` stores all meta-information about the document
// `content` is the main view

var SEED = [
  ["create", {
      "id": "document",
      "type": "document",
      "views": ["content", "figures", "publications"],
      "title": "Untitled",
      "abstract": "Enter abstract"
    }
  ],
  ["create", {
      "id": "content",
      "type": "view",
      "nodes": []
    }
  ],
  ["create", {
      "id": "figures",
      "type": "view",
      "nodes": []
    }
  ],
  ["create", {
      "id": "publications",
      "type": "view",
      "nodes": []
    }
  ]
];

// Command Converter
// ========
//
// Turns document command into Data.Graph commands

var Converter = function() {

  // Delete nodes from document
  // --------
  //
  // ["delete", {nodes: ["h1", "t1"]}]

  this.delete = function(graph, command) {
    var nodes = command.args.nodes;

    var commands = [];
    _.each(nodes, function(n) {
      commands.push(Data.Graph.Delete({id: n}));
    }, this);

    _.each(graph.get('document').views, function(view) {
      var match = _.intersection(graph.get(view).nodes, nodes);
      commands.push(this.hide(graph, {
        "path": [view],
        args: {nodes: match}
      }));
    }, this);
    return commands;
  };

  // Hide elements from provided view
  // --------
  //
  // ["hide", {"nodes": ["t1", "t2"]}]
  //

  this.hide = function(graph, command) {
    var path = command.path.concat(["nodes"]);
    var view = graph.resolve(path).get();
    var nodes = command.args.nodes;

    var indices = [];
    _.each(nodes, function(n) {
      var i = view.indexOf(n);
      if (i>=0) indices.push(i);
    }, this);

    indices = indices.sort().reverse();

    var ops = _.map(indices, function(index) {
      return ot.ArrayOperation.Delete(index, view[index]);
    });
    return Data.Graph.Update(path, ot.ArrayOperation.Compound(ops));
  },

  // Position nodes in document
  // --------
  //
  // ["position", {"nodes": ["t1", "t2"], "target": -1}]
  //

  // TODO: we could use Graph.set which computes a rather minimal set of ArrayOps
  //       to get rid of the ArrayOperation dependency here
  this.position = function(graph, command) {
    var path = command.path.concat(["nodes"]);
    var view = graph.resolve(path).get();
    var nodes = command.args.nodes;
    var ops = [];
    var idx;

    // Create a sequence of atomic array operations that
    // are bundled into a Compound operation
    // 1. Remove elements (from right to left)
    // 2. Insert at the very position

    // the sequence contains selected nodes in the order they
    // occurr in the view
    var seq = _.intersection(view, nodes);
    var l = view.length;

    while(seq.length > 0) {
      var id = seq.pop();
      idx = view.indexOf(id);
      if (idx >= 0) {
        ops.push(ot.ArrayOperation.Delete(idx, id));
        l--;
      }
    }

    // target index can be given as negative number (as known from python/ruby)
    var target = Math.min(command.args.target, l);
    if (target<0) target = Math.max(0, l+target+1);

    for (idx = 0; idx < nodes.length; idx++) {
      ops.push(ot.ArrayOperation.Insert(target + idx, nodes[idx]));
    }

    var compound = ot.ArrayOperation.Compound(ops);

    return Data.Graph.Update(path, compound);
  };

  // Update incrementally
  // --------
  //

  // TODO: if we would integrate the convenience mechanisms for update and set into
  // Data.Graph, we could get rid of the OT dependencies here.

  this.update = function(graph, command) {
    var property = graph.resolve(command.path);
    var val = property.get();

    var update;

    // String
    if (property.baseType === 'string') {
      update = ot.TextOperation.fromOT(val, command.args);
    }

    // Array
    else if (property.baseType === 'array') {
      throw new Error("Not yet implemented for arrays");
    }

    // Object
    else if (property.baseType === 'object') {
      update = ot.ObjectOperation.Extend(val, command.args);
    }

    // Other
    else {
      throw new Error("Unsupported type for update: " + property.baseType);
    }

    return Data.Graph.Update(command.path, update);
  };

  // Set property values
  // --------
  //
  // Unlike update you can set values directly
  // ["set", "h1", "content", "Hello Welt"] // string update
  // ["set", "a1", "pos", ["a", "b", "c"]] // array update (not yet implemented)

  this.set = function(graph, command) {

    if (!command.args) { // for string updates
      command.args = command.path.pop();
    }

    var result;

    var property, val, newVal, update;

    property = graph.resolve(command.path);
    val = property.get();
    newVal = command.args;

    // String
    if (property.baseType === 'string') {
      update = ot.TextOperation.fromOT(val, [-val.length, newVal]);
    }
    // Array
    else if (property.baseType === 'array') {
      update = ot.ArrayOperation.Update(val, newVal);
    }
    // Object
    else if (property.baseType === 'object') {
      // TODO: for that we need to delete all keys and create the new ones
      //  or a more intelligent solution (i.e. diff)
      throw new Error("Not yet implemented for objects");
    }
    // Other
    else {
      // treating any other type via string operation
      val = val.toString();
      newVal = newVal.toString();
      update = ot.TextOperation.fromOT(val, [-val.length, newVal]);
    }

    result = Data.Graph.Update(command.path, update);
    return result;
  };

  // Annotate document
  // --------
  //
  // `command.path` defines the referenced content node and property
  // ["annotate", "t1", "content", {"id": "a1",  type": "idea"}]

  this.annotate = function(graph, command) {
    // TODO: check if source exists, otherwise reject annotation
    if (command.path.length !== 2) throw new Error("Invalid target: " + command.path);

    var annotation = _.extend({}, command.args, {
      node: command.path[0],
      property: command.path[1]
    });

    return Data.Graph.Create(annotation);
  };

  // Comment
  // --------
  //
  // `command.path` holds the node id, where the comment should stick on

  this.comment = function(graph, command) {
    // Delegates to graph.create method
    // TODO: check if source exists, otherwise reject annotation

    // keep track of annotation
    if (command.path.length !== 1) throw new Error("Invalid target: " + command.path);

    var comment = _.extend({}, command.args);
    comment.node = command.path[0];
    comment.type = comment.type || 'comment';

    return Data.Graph.Create(comment);
  };
};

// Document
// --------
//
// A generic model for representing and transforming digital documents

var Document = function(doc, schema) {
  Data.Graph.call(this, schema || SCHEMA);
  this.id = doc.id;
};

Document.__prototype__ = function() {

  var __super__ = util.prototype(this);
  var converter = new Converter();

  this.init = function() {
    __super__.init.call(this);
    this.meta = {};
    _.each(SEED, function(cmd) {
      this.exec(cmd);
    }, this);
  };

  var updateAnnotations = function(node, property, change) {
    // We need to update the range of affected annotations.

    var annotations = this.find("annotations", node.id);
    annotations = _.filter(annotations, function(a) {
      return a.property === property;
    });
    for (var idx = 0; idx < annotations.length; idx++) {
      ot.TextOperation.Range.transform(annotations[idx].range, change);
    }
  };

  // Executes a document manipulation command.
  // --------
  // The command is converted into a sequence of graph commands

  this.exec = function(command) {
    // console.log("Executing command: ", command);
    var graphCommand;
    // convert the command into a Data.Graph compatible command
    command = new Data.Command(command);
    if (converter[command.op]) {
      graphCommand = converter[command.op](this, command);
    } else {
      graphCommand = command;
    }

    var commands = _.isArray(graphCommand) ? graphCommand : [graphCommand];

    _.each(commands, function(c) {
      __super__.exec.call(this, c);
      if (c.op === "update") {
        var node = this.get(c.path[0]);
        var property = c.path[1];
        var change = c.args;
        updateAnnotations.call(this, node, property, change);
      }
    }, this);
    this.trigger('command:executed', command);
  };
};

Document.__prototype__.prototype = Data.Graph.prototype;
Document.prototype = new Document.__prototype__();

// AnnotatedText
// --------
//
// Ties together a text node with its annotations
// Interface defined by Substance.Surface

Document.AnnotatedText = function(doc, path) {
  this.doc = doc;
  this.path = path;
  this.property = doc.resolve(path);
  this.resetCache();
};

Document.AnnotatedText.prototype.setAnnotation = function(annotation) {
  this.cache.annotations[annotation.id] = annotation;
  this.commit();
};

Document.AnnotatedText.prototype.getAnnotation = function(id) {
  return this.cache.annotations[id] || this.doc.get(id);
};

Document.AnnotatedText.prototype.deleteAnnotation = function(id) {
  delete this.cache.annotations[id];
  this.cache.deleted_annotations.push(id);
};

Document.AnnotatedText.prototype.setContent = function(content) {
  this.cache.content = content;
};

Document.AnnotatedText.prototype.getContent = function() {
  if (this.cache.content !== null) return this.cache.content;
  return this.property.get();
};

// Transform Hook
// --------
//

Document.AnnotatedText.prototype.each = function(fn) {
  var annos = this.doc.find('annotations', this.property.node.id);
  _.each(this.cache.annotations, fn);

  _.each(annos, function(a) {
    if (!this.cache.annotations[a.id] && !_.include(this.cache.deleted_annotations, a.id)) fn(a, a.id);
  }, this);
};

// Transform Hook
// --------
//
// triggered implicitly by Surface.insert|deleteTransformer)

Document.AnnotatedText.prototype.transformAnnotation = function(a, op, expand) {
  if (this.cache.annotations[a.id]) {
    a = this.cache.annotations[a.id];
  } else {
    a = util.deepclone(a);
  }
  ot.TextOperation.Range.transform(a.range, op, expand);
  this.cache.annotations[a.id] = a;
};

Document.AnnotatedText.prototype.resetCache = function() {
  this.cache = {
    annotations: {},
    content: null,
    deleted_annotations: []
  };
};

// Commit changes
// --------
//

Document.AnnotatedText.prototype.commit = function(fn) {

  // 1. Insert Annotations
  var newAnnotations = [];
  var updatedAnnotations = [];
  _.each(this.cache.annotations, function(a) {
    var oa = this.doc.get(a.id);
    if (!oa) newAnnotations.push(a);
    else if (a.type !== oa.type) updatedAnnotations.push(a);
  }, this);

  var cmds = [];

  _.each(newAnnotations, function(a) {
    a.node = this.property.node.id;
    cmds.push(Substance.Document.Create(a));
  }, this);

  // Text diff computation
  if (this.cache.content !== null) {
    var delta = _.extractOperation(this.property.get(), this.cache.content);
    cmds.push(Data.Graph.Update(this.path, ot.TextOperation.fromOT(delta)));
  }

  _.each(cmds, function(c) {
    this.doc.exec(c);
  }, this);
  this.resetCache();
};

// Command Factories
// --------

Document.Create = function(node) {
  return Data.Graph.Create(node);
};

Document.Delete = function(nodes) {
  return ["delete", {nodes: nodes}];
};

// Add event support
_.extend(Document.prototype, util.Events);

Document.SCHEMA = SCHEMA;

// Export
// ========

if (typeof exports !== 'undefined') {
  module.exports = Document;
} else {
  root.Substance.Document = Document;
}

})(this);
<commit_msg>Updates due to changes in Data.Property.<commit_after>// Substance.Document 0.4.0
// (c) 2010-2013 Michael Aufreiter
// Substance.Document may be freely distributed under the MIT license.
// For all details and documentation:
// http://interior.substance.io/modules/document.html

(function(root) {

// Import
// ========

var _,
    util,
    errors,
    Chronicle,
    ot,
    Data;

if (typeof exports !== 'undefined') {
  _    = require('underscore');
  ot   = require('./lib/operation');
  util   = require('./lib/util/util');
  errors   = require('./lib/util/errors');
  Chronicle = require('./lib/chronicle/chronicle');
  ot = require('./lib/chronicle/lib/ot/index');
  Data = require('./lib/data/data');
} else {
  _ = root._;
  ot = root.ot;
  util = root.Substance.util;
  errors   = root.Substance.errors;
  Chronicle = root.Substance.Chronicle;
  ot = Chronicle.ot;
  Data = root.Substance.Data;
}

// Default Document Schema
// --------

var SCHEMA = {
  // Static indexes
  "indexes": {
    // all comments are now indexed by node association
    "comments": {
      "type": "comment",
      "properties": ["node"]
    },
    // All annotations are now indexed by node
    "annotations": {
      "type": "annotation",
      "properties": ["node"]
    }
  },

  "types": {
    // Specific type for substance documents, holding all content elements
    "content": {
      "properties": {
      }
    },

    "text": {
      "parent": "content",
      "properties": {
        "content": "string"
      }
    },

    "document": {
      "properties": {
        "views": ["array", "view"],
        "title": "string",
        "abstract": "string"
      }
    },

    "view": {
      "properties": {
        "nodes": ["array", "content"]
      }
    },

    "codeblock": {
      "parent": "content",
      "properties": {
        "content": "string"
      }
    },

    "image": {
      "parent": "content",
      "properties": {
        "large": "string",
        "medium": "string",
        "caption": "string"
      }
    },

    "heading": {
      "parent": "content",
      "properties": {
        "content": "string",
        "level": "number"
      }
    },

    // Annotations
    "annotation": {
      "properties": {
        "node": "content",
        "property": "string",
        "range": "object"
      }
    },

    "strong": {
      "parent": "annotation",
      "properties": {
      }
    },

    "emphasis": {
      "properties": {
      },
      "parent": "annotation"
    },

    "code": {
      "parent": "annotation",
      "properties": {
      }
    },

    "link": {
      "parent": "annotation",
      "properties": {
        "url": "string"
      }
    },

    "idea": {
      "parent": "annotation",
      "properties": {
      }
    },

    "error": {
      "parent": "annotation",
      "properties": {
      }
    },

    "question": {
      "parent": "annotation",
      "properties": {
      }
    },

    // Comments
    "comment": {
      "properties": {
        "content": "string",
        "created_at": "string", // should be date
        "creator": "string", // should be date
        "node": "node" // references either a content node or annotation
      }
    }
  }
};

// Seed Data
// ========
//
// Some pre-defined nodes that are required for each document
// `document` stores all meta-information about the document
// `content` is the main view

var SEED = [
  ["create", {
      "id": "document",
      "type": "document",
      "views": ["content", "figures", "publications"],
      "title": "Untitled",
      "abstract": "Enter abstract"
    }
  ],
  ["create", {
      "id": "content",
      "type": "view",
      "nodes": []
    }
  ],
  ["create", {
      "id": "figures",
      "type": "view",
      "nodes": []
    }
  ],
  ["create", {
      "id": "publications",
      "type": "view",
      "nodes": []
    }
  ]
];

// Command Converter
// ========
//
// Turns document command into Data.Graph commands

var Converter = function() {

  // Delete nodes from document
  // --------
  //
  // ["delete", {nodes: ["h1", "t1"]}]

  this.delete = function(graph, command) {
    var nodes = command.args.nodes;

    var commands = [];
    _.each(nodes, function(n) {
      commands.push(Data.Graph.Delete({id: n}));
    }, this);

    _.each(graph.get('document').views, function(view) {
      var match = _.intersection(graph.get(view).nodes, nodes);
      commands.push(this.hide(graph, {
        "path": [view],
        args: {nodes: match}
      }));
    }, this);
    return commands;
  };

  // Hide elements from provided view
  // --------
  //
  // ["hide", {"nodes": ["t1", "t2"]}]
  //

  this.hide = function(graph, command) {
    var path = command.path.concat(["nodes"]);
    var view = graph.resolve(path).get();
    var nodes = command.args.nodes;

    var indices = [];
    _.each(nodes, function(n) {
      var i = view.indexOf(n);
      if (i>=0) indices.push(i);
    }, this);

    indices = indices.sort().reverse();

    var ops = _.map(indices, function(index) {
      return ot.ArrayOperation.Delete(index, view[index]);
    });
    return Data.Graph.Update(path, ot.ArrayOperation.Compound(ops));
  },

  // Position nodes in document
  // --------
  //
  // ["position", {"nodes": ["t1", "t2"], "target": -1}]
  //

  // TODO: we could use Graph.set which computes a rather minimal set of ArrayOps
  //       to get rid of the ArrayOperation dependency here
  this.position = function(graph, command) {
    var path = command.path.concat(["nodes"]);
    var view = graph.resolve(path).get();
    var nodes = command.args.nodes;
    var ops = [];
    var idx;

    // Create a sequence of atomic array operations that
    // are bundled into a Compound operation
    // 1. Remove elements (from right to left)
    // 2. Insert at the very position

    // the sequence contains selected nodes in the order they
    // occurr in the view
    var seq = _.intersection(view, nodes);
    var l = view.length;

    while(seq.length > 0) {
      var id = seq.pop();
      idx = view.indexOf(id);
      if (idx >= 0) {
        ops.push(ot.ArrayOperation.Delete(idx, id));
        l--;
      }
    }

    // target index can be given as negative number (as known from python/ruby)
    var target = Math.min(command.args.target, l);
    if (target<0) target = Math.max(0, l+target+1);

    for (idx = 0; idx < nodes.length; idx++) {
      ops.push(ot.ArrayOperation.Insert(target + idx, nodes[idx]));
    }

    var compound = ot.ArrayOperation.Compound(ops);

    return Data.Graph.Update(path, compound);
  };

  // Update incrementally
  // --------
  //

  // TODO: if we would integrate the convenience mechanisms for update and set into
  // Data.Graph, we could get rid of the OT dependencies here.

  this.update = function(graph, command) {
    var property = graph.resolve(command.path);
    var val = property.get();
    var valueType = property.type()[0];

    var update;

    if (valueType === 'string') {
      update = ot.TextOperation.fromOT(val, command.args);
    }
    else if (valueType === 'array') {
      throw new Error("Not yet implemented for arrays");
    }
    else if (valueType === 'object') {
      update = ot.ObjectOperation.Extend(val, command.args);
    }
    else {
      throw new Error("Unsupported type for update: " + valueType);
    }

    return Data.Graph.Update(command.path, update);
  };

  // Set property values
  // --------
  //
  // Unlike update you can set values directly
  // ["set", "h1", "content", "Hello Welt"] // string update
  // ["set", "a1", "pos", ["a", "b", "c"]] // array update (not yet implemented)

  this.set = function(graph, command) {

    if (!command.args) { // for string updates
      command.args = command.path.pop();
    }

    var result;

    var property, val, newVal, update;

    property = graph.resolve(command.path);
    val = property.get();
    newVal = command.args;

    // String
    if (property.baseType === 'string') {
      update = ot.TextOperation.fromOT(val, [-val.length, newVal]);
    }
    // Array
    else if (property.baseType === 'array') {
      update = ot.ArrayOperation.Update(val, newVal);
    }
    // Object
    else if (property.baseType === 'object') {
      // TODO: for that we need to delete all keys and create the new ones
      //  or a more intelligent solution (i.e. diff)
      throw new Error("Not yet implemented for objects");
    }
    // Other
    else {
      // treating any other type via string operation
      val = val.toString();
      newVal = newVal.toString();
      update = ot.TextOperation.fromOT(val, [-val.length, newVal]);
    }

    result = Data.Graph.Update(command.path, update);
    return result;
  };

  // Annotate document
  // --------
  //
  // `command.path` defines the referenced content node and property
  // ["annotate", "t1", "content", {"id": "a1",  type": "idea"}]

  this.annotate = function(graph, command) {
    // TODO: check if source exists, otherwise reject annotation
    if (command.path.length !== 2) throw new Error("Invalid target: " + command.path);

    var annotation = _.extend({}, command.args, {
      node: command.path[0],
      property: command.path[1]
    });

    return Data.Graph.Create(annotation);
  };

  // Comment
  // --------
  //
  // `command.path` holds the node id, where the comment should stick on

  this.comment = function(graph, command) {
    // Delegates to graph.create method
    // TODO: check if source exists, otherwise reject annotation

    // keep track of annotation
    if (command.path.length !== 1) throw new Error("Invalid target: " + command.path);

    var comment = _.extend({}, command.args);
    comment.node = command.path[0];
    comment.type = comment.type || 'comment';

    return Data.Graph.Create(comment);
  };
};

// Document
// --------
//
// A generic model for representing and transforming digital documents

var Document = function(doc, schema) {
  Data.Graph.call(this, schema || SCHEMA);
  this.id = doc.id;
};

Document.__prototype__ = function() {

  var __super__ = util.prototype(this);
  var converter = new Converter();

  this.init = function() {
    __super__.init.call(this);
    this.meta = {};
    _.each(SEED, function(cmd) {
      this.exec(cmd);
    }, this);
  };

  var updateAnnotations = function(node, property, change) {
    // We need to update the range of affected annotations.

    var annotations = this.find("annotations", node.id);
    annotations = _.filter(annotations, function(a) {
      return a.property === property;
    });
    for (var idx = 0; idx < annotations.length; idx++) {
      ot.TextOperation.Range.transform(annotations[idx].range, change);
    }
  };

  // Executes a document manipulation command.
  // --------
  // The command is converted into a sequence of graph commands

  this.exec = function(command) {
    // console.log("Executing command: ", command);
    var graphCommand;
    // convert the command into a Data.Graph compatible command
    command = new Data.Command(command);
    if (converter[command.op]) {
      graphCommand = converter[command.op](this, command);
    } else {
      graphCommand = command;
    }

    var commands = _.isArray(graphCommand) ? graphCommand : [graphCommand];

    _.each(commands, function(c) {
      __super__.exec.call(this, c);
      if (c.op === "update") {
        var node = this.get(c.path[0]);
        var property = c.path[1];
        var change = c.args;
        updateAnnotations.call(this, node, property, change);
      }
    }, this);
    this.trigger('command:executed', command);
  };
};

Document.__prototype__.prototype = Data.Graph.prototype;
Document.prototype = new Document.__prototype__();

// AnnotatedText
// --------
//
// Ties together a text node with its annotations
// Interface defined by Substance.Surface

Document.AnnotatedText = function(doc, path) {
  this.doc = doc;
  this.path = path;
  this.property = doc.resolve(path);
  this.resetCache();
};

Document.AnnotatedText.prototype.setAnnotation = function(annotation) {
  this.cache.annotations[annotation.id] = annotation;
  this.commit();
};

Document.AnnotatedText.prototype.getAnnotation = function(id) {
  return this.cache.annotations[id] || this.doc.get(id);
};

Document.AnnotatedText.prototype.deleteAnnotation = function(id) {
  delete this.cache.annotations[id];
  this.cache.deleted_annotations.push(id);
};

Document.AnnotatedText.prototype.setContent = function(content) {
  this.cache.content = content;
};

Document.AnnotatedText.prototype.getContent = function() {
  if (this.cache.content !== null) return this.cache.content;
  return this.property.get();
};

// Transform Hook
// --------
//

Document.AnnotatedText.prototype.each = function(fn) {
  var annos = this.doc.find('annotations', this.property.node.id);
  _.each(this.cache.annotations, fn);

  _.each(annos, function(a) {
    if (!this.cache.annotations[a.id] && !_.include(this.cache.deleted_annotations, a.id)) fn(a, a.id);
  }, this);
};

// Transform Hook
// --------
//
// triggered implicitly by Surface.insert|deleteTransformer)

Document.AnnotatedText.prototype.transformAnnotation = function(a, op, expand) {
  if (this.cache.annotations[a.id]) {
    a = this.cache.annotations[a.id];
  } else {
    a = util.deepclone(a);
  }
  ot.TextOperation.Range.transform(a.range, op, expand);
  this.cache.annotations[a.id] = a;
};

Document.AnnotatedText.prototype.resetCache = function() {
  this.cache = {
    annotations: {},
    content: null,
    deleted_annotations: []
  };
};

// Commit changes
// --------
//

Document.AnnotatedText.prototype.commit = function(fn) {

  // 1. Insert Annotations
  var newAnnotations = [];
  var updatedAnnotations = [];
  _.each(this.cache.annotations, function(a) {
    var oa = this.doc.get(a.id);
    if (!oa) newAnnotations.push(a);
    else if (a.type !== oa.type) updatedAnnotations.push(a);
  }, this);

  var cmds = [];

  _.each(newAnnotations, function(a) {
    a.node = this.property.node.id;
    cmds.push(Substance.Document.Create(a));
  }, this);

  // Text diff computation
  if (this.cache.content !== null) {
    var delta = _.extractOperation(this.property.get(), this.cache.content);
    cmds.push(Data.Graph.Update(this.path, ot.TextOperation.fromOT(delta)));
  }

  _.each(cmds, function(c) {
    this.doc.exec(c);
  }, this);
  this.resetCache();
};

// Command Factories
// --------

Document.Create = function(node) {
  return Data.Graph.Create(node);
};

Document.Delete = function(nodes) {
  return ["delete", {nodes: nodes}];
};

// Add event support
_.extend(Document.prototype, util.Events);

Document.SCHEMA = SCHEMA;

// Export
// ========

if (typeof exports !== 'undefined') {
  module.exports = Document;
} else {
  root.Substance.Document = Document;
}

})(this);
<|endoftext|>


<commit_before>/**
 * Extensions of CFG
 * Created by chengfulin on 2015/4/15.
 */
var esgraph = require('esgraph'),
    esprima = require('esprima'),
    walkes = require('walkes'),
    namespace = require('./namespace'),
    internal = namespace();

/**
 * Extended for CFG build by esgraph
 * @constructor
 */
function CFGExt() {
    'use strict';
    internal(this).lastId = -1;
}

/**
 * Parse the code to AST with default range option
 * @param code source code
 * @returns {*}
 */
CFGExt.prototype.parseAST = function parseAST(code) {
    'use strict';
    return esprima.parse(code, {range: true});
};

/**
 * Get the CFG of the AST
 * @param ast
 * @returns {Array} CFG
 */
CFGExt.prototype.getCFG = function getCFG(ast) {
    'use strict';
    var cfg = esgraph(ast);
    for(var index = 0; index < cfg[2].length; ++index) {
        (cfg[2][index]).cfgId = internal(this).lastId + index + 1;
    }
    internal(this).lastId += cfg[2].length;
    return cfg;
};

/**
 * Find function scopes of AST parsed from source
 * @param ast
 * @returns {Array}
 */
CFGExt.prototype.findScopes = function findScopes(ast) {
    'use strict';
    var scopes = [];
    function handleInnerFunction(astNode, recurse) {
        scopes.push(astNode);
        recurse(astNode.body);
    }

    walkes(ast, {
        Program: function (node, recurse) {
            scopes.push(node);
            node.body.forEach(function (elem) {
                recurse(elem);
            });
        },
        FunctionDeclaration: handleInnerFunction,
        FunctionExpression: handleInnerFunction
    });
    return scopes;
};

/**
 * Convert a CFG to dot language for Graphviz output (with AST node type labeled, default)
 * @param cfg
 * @returns {*|string}
 */
CFGExt.prototype.toDot = function (cfg) {
    'use strict';
    var outputCFG = [].concat(cfg),
        option = {counter: outputCFG[0].cfgId};
    return esgraph.dot(cfg, option);
};

/**
 * Convert a CFG to dot language for Graphviz output (with id labeled)
 * @param cfg
 * @returns {*|string}
 */
CFGExt.prototype.toDotWithLabelId = function (cfg) {
    'use strict';
    var outputCFG = [].concat(cfg),
        option = {counter: outputCFG[0].cfgId};
    outputCFG[2].forEach(function (node) {
        if (!node.type) {
            node.label = node.cfgId;
        }
    });
    return esgraph.dot(cfg, option);
};

/**
 * Utility for resetting the current counter for Id
 */
CFGExt.prototype.resetCounter = function () {
    'use strict';
    internal(this).lastId = -1;
};

var singleTon = new CFGExt();
module.exports = singleTon;<commit_msg>Add label when get CFG<commit_after>/**
 * Extensions of CFG
 * Created by chengfulin on 2015/4/15.
 */
var esgraph = require('esgraph'),
    esprima = require('esprima'),
    walkes = require('walkes'),
    namespace = require('./namespace'),
    internal = namespace();

/**
 * Extended for CFG build by esgraph
 * @constructor
 */
function CFGExt() {
    'use strict';
    internal(this).lastId = -1;
}

/**
 * Parse the code to AST with default range option
 * @param code source code
 * @returns {*}
 */
CFGExt.prototype.parseAST = function parseAST(code) {
    'use strict';
    return esprima.parse(code, {range: true});
};

/**
 * Get the CFG of the AST with additional information
 * @param ast
 * @returns {Array} CFG
 */
CFGExt.prototype.getCFG = function getCFG(ast) {
    'use strict';
    var cfg = esgraph(ast);
    /// Add cfgId and label to each node
    for(var index = 0; index < cfg[2].length; ++index) {
        (cfg[2][index]).cfgId = internal(this).lastId + index + 1;
        (cfg[2][index]).label = (cfg[2][index]).cfgId;
    }
    /// label for entry and exit node
    cfg[0].label = 'entry';
    cfg[1].label = 'exit';
    internal(this).lastId += cfg[2].length;
    return cfg;
};

/**
 * Find function scopes of AST parsed from source
 * @param ast
 * @returns {Array}
 */
CFGExt.prototype.findScopes = function findScopes(ast) {
    'use strict';
    var scopes = [];
    function handleInnerFunction(astNode, recurse) {
        scopes.push(astNode);
        recurse(astNode.body);
    }

    walkes(ast, {
        Program: function (node, recurse) {
            scopes.push(node);
            node.body.forEach(function (elem) {
                recurse(elem);
            });
        },
        FunctionDeclaration: handleInnerFunction,
        FunctionExpression: handleInnerFunction
    });
    return scopes;
};

/**
 * Convert a CFG to dot language for Graphviz output (with AST node type labeled, default)
 * @param cfg
 * @returns {*|string}
 */
CFGExt.prototype.toDot = function (cfg) {
    'use strict';
    var outputCFG = [].concat(cfg),
        option = {counter: outputCFG[0].cfgId};
    return esgraph.dot(cfg, option);
};

/**
 * Convert a CFG to dot language for Graphviz output (with id labeled)
 * @param cfg
 * @returns {*|string}
 */
CFGExt.prototype.toDotWithLabelId = function (cfg) {
    'use strict';
    var outputCFG = [].concat(cfg),
        option = {counter: outputCFG[0].cfgId};
    outputCFG[2].forEach(function (node) {
        if (!node.type) {
            node.label = node.cfgId;
        }
    });
    return esgraph.dot(cfg, option);
};

/**
 * Utility for resetting the current counter for Id
 */
CFGExt.prototype.resetCounter = function () {
    'use strict';
    internal(this).lastId = -1;
};

var singleTon = new CFGExt();
module.exports = singleTon;<|endoftext|>


<commit_before>let mix = require('laravel-mix')
let tailwindcss = require('tailwindcss')
let path = require('path')
require('webpack')
require('laravel-mix-purgecss')

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.webpackConfig({
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'resources/assets/js/')
    }
  }
})

mix
  .js('resources/assets/js/app.js', 'public/js')
  .vue()
  .sass('resources/assets/sass/app.scss', 'public/css/app.css')
  .options({
    processCssUrls: false,
    postCss: [tailwindcss('tailwind.config.js')]
  })
  .purgeCss({
    extend: {
      safelist: {
        deep: [
          /^mx-/,
          /^CodeMirror(-\w*)?/,
          /^cm-\w*/,
          /^EasyMDEContainer/,
          /^editor-toolbar/,
          /^easymde-\w*/,
          /^collection-(cluster|space)$/
        ]
      }
    }
  })

if (mix.inProduction()) {
  mix.version()
}
<commit_msg>purgecss fixes for readme content<commit_after>let mix = require('laravel-mix')
let tailwindcss = require('tailwindcss')
let path = require('path')
require('webpack')
require('laravel-mix-purgecss')

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.webpackConfig({
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'resources/assets/js/')
    }
  }
})

mix
  .js('resources/assets/js/app.js', 'public/js')
  .vue()
  .sass('resources/assets/sass/app.scss', 'public/css/app.css')
  .options({
    processCssUrls: false,
    postCss: [tailwindcss('tailwind.config.js')]
  })
  .purgeCss({
    extend: {
      safelist: {
        deep: [
          /^mx-/,
          /^CodeMirror(-\w*)?/,
          /^cm-\w*/,
          /^EasyMDEContainer/,
          /^editor-toolbar/,
          /^easymde-\w*/,
          /^collection-(cluster|space)$/,
          /^repo-readme/
        ]
      }
    }
  })

if (mix.inProduction()) {
  mix.version()
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            todos: ['hi', 'bye'],
        };

        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleSubmit(input) {
        this.setState(prevState => ({todos: prevState.todos.concat(input)}));
    }

    render() {
        return (

Welcome to React

                    Let's make a todo list

                <TodoList todos={this.state.todos} />
                <AddTodo handleSubmit={this.handleSubmit} />

        );
    }
}

const TodoList = (props) => { /* the array of todos from App state are on the props object */
    // map thru the props.todos array to populate the
 with <TodoItems>
    return (

            {props.todos.map((todo, i) => (
                <TodoItem key={i} todo={todo} />
            ))}

    );
};

const TodoItem = (props) => { /* the array of todos from App state passed to TodoList are on the props object */
    const { todo } = props; // so you don't have to write 'props.todo'

    return
{todo}
;
};

const AddTodo = (props) => { /* the handleSubmit function from App is on the props object  */
    let input;

    return (

            <form
                onSubmit={e => {
                    e.preventDefault();
                    if(!input.value.trim()) return;
                    props.handleSubmit(input.value);
                    input.value = '';
                }}>
                <input ref={node => {input = node;}}/>
                <button type="submit">Add Todo</button>
            </form>

    );
};

export default App;
<commit_msg>changes state.todos to an array of objects {todo, completed} - TodoList displays todo property of state.todos objects<commit_after>import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
    constructor(props) {
        super(props);

        this.state = {
            todos: [],
        };

        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleSubmit(input) {
        this.setState(prevState => ({todos: prevState.todos.concat({todo: input, completed: false})}));
    }

    render() {
        return (

Welcome to React

                    Let's make a todo list

                <TodoList todos={this.state.todos} />
                <AddTodo handleSubmit={this.handleSubmit} />

        );
    }
}

const TodoList = (props) => { /* the array of todos from App state are on the props object */
    // map thru the props.todos array to populate the
 with <TodoItems>
    return (

            {props.todos.map((todo, i) => (
                <TodoItem key={i} todo={todo.todo} />
            ))}

    );
};

const TodoItem = (props) => { /* the array of todos from App state passed to TodoList are on the props object */
    const { todo } = props; // so you don't have to write 'props.todo'

    return
{todo}
;
};

const AddTodo = (props) => { /* the handleSubmit function from App is on the props object  */
    let input;

    return (

            <form
                onSubmit={e => {
                    e.preventDefault();
                    if(!input.value.trim()) return;
                    props.handleSubmit(input.value);
                    input.value = '';
                }}>
                <input ref={node => {input = node;}}/>
                <button type="submit">Add Todo</button>
            </form>

    );
};

export default App;
<|endoftext|>


<commit_before>$(function() {
  var reservations;
  var resources;
  function renderGraph() {
    if(!(reservations && resources))
      return;

    // Sort the rooms alphabetically
    resources = resources.sort(function(a, b) {
      var keyA = a.name.toLowerCase(),
          keyB = b.name.toLowerCase();
      if(keyA < keyB) return -1;
      if(keyA > keyB) return 1;
      return 0;
    });

    // Set up the reservation list
    var start = 28800 ; // 8:00 AM
    var end = 82800; // 7:00 PM

    // Convert start / end times to seconds in the day
    reservations = reservations.map(function(r) {
      var from = new Date(r.from);
      var to = new Date(r.to);

      r.from = from.getSeconds() + (60 * from.getMinutes()) + (60 * 60 * from.getHours());
      r.to = to.getSeconds() + (60 * to.getMinutes()) + (60 * 60 * to.getHours());
      return r;
    });

    // Set scales
    var xscale = d3.scale.linear()
      .domain([start, end])
      .range([0, 100]);

    // Generate the x axis
    var hours = [];
    for(var x = start; x < end; x += 3600) {
      var hour = (Math.floor(x/3600) - 1) % 12 + 1;
      var minute = (x % 3600);
      hours.push(hour + ":" + ((minute < 10)?"0":"") + minute);
    }

    // Render the x axis.
    var xaxis = d3.select("#x-axis").selectAll("div")
      .data(hours)
      .enter()
      .append("div")
      .attr("class", function(d, i) {
        return "time " + ((i%2 == 0)?"even": "odd") + ((i == hours.length - 1)?" last":"");
      })
      .style("width", (100 / hours.length) + "%")
      .append("div")
      .text(function(d) {
        return d;
      })
      .attr("class", "time-label");

    // Set the height of the reservations
    $("#reservations").height($(window).height() - 170 - 60); // 170 is header height, 60 is footer padding

    // Render the rooms.
    var timeline = d3.select("#reservations");

    // Create one row per entry
    var rows = timeline.selectAll("div")
      .data(resources)
      .enter()
      .append("div")
      .attr("class","row")
      .style("height", (100/resources.length)  + "%")

    // Add the label
    rows.append("div")
      .attr("class", "resource")
      .append("div")
      .attr("class", "resource-label")
      .text(function(d) {
        return d.name;
      })

    // Add the reservations
    rows.each(function(resource) {
      var row = d3.select(this);
      var filtered = reservations.filter(function(d) {
        return d.resource.id == resource.id && d.from < (end - 60 * 60);
      });
      filtered.sort(function(a,b) {
        return a.from - b.from;
      })

      row.append("div")
        .attr("class","timeline")
        .selectAll("div")
        .data(filtered)
        .enter()
        .append("div")
        .attr("class","reservation")
        .style("width", function(d) {
          return (xscale(Math.min(d.to, end - 60 * 60)) - xscale(d.from)) + "%";
        })
        .style("left", function(d) {
          return (xscale(d.from)) + "%";
        })
        .append("div")
        .attr("class", "cell-bubble")
        .append("div")
        .attr("class","reservation-label")
        .text(function(d) {
          if(d.membership && d.title) return d.membership.name;
          else if(d.membership) return d.membership.name;
          else return "";
        });
    })
  }

  // Refresh reservations
  function refreshReservations() {
    $.ajax('/reservations', {
      method: 'GET',
      dataType: 'json'
    })
    .done(function(data) {
      reservations = data;
      renderGraph();
    })
  }

  refreshReservations();
  setInterval(refreshReservations,30000);

  // Load the resources
  $.ajax('/resources', {
    method: 'GET',
    dataType: 'json'
  })
  .done(function(data) {
    resources = data;
    renderGraph();
  })

})
<commit_msg>removed later times<commit_after>$(function() {
  var reservations;
  var resources;
  function renderGraph() {
    if(!(reservations && resources))
      return;

    // Sort the rooms alphabetically
    resources = resources.sort(function(a, b) {
      var keyA = a.name.toLowerCase(),
          keyB = b.name.toLowerCase();
      if(keyA < keyB) return -1;
      if(keyA > keyB) return 1;
      return 0;
    });

    // Set up the reservation list
    var start = 28800 ; // 8:00 AM
    var end = 68400; // 7:00 PM

    // Convert start / end times to seconds in the day
    reservations = reservations.map(function(r) {
      var from = new Date(r.from);
      var to = new Date(r.to);

      r.from = from.getSeconds() + (60 * from.getMinutes()) + (60 * 60 * from.getHours());
      r.to = to.getSeconds() + (60 * to.getMinutes()) + (60 * 60 * to.getHours());
      return r;
    });

    // Set scales
    var xscale = d3.scale.linear()
      .domain([start, end])
      .range([0, 100]);

    // Generate the x axis
    var hours = [];
    for(var x = start; x < end; x += 3600) {
      var hour = (Math.floor(x/3600) - 1) % 12 + 1;
      var minute = (x % 3600);
      hours.push(hour + ":" + ((minute < 10)?"0":"") + minute);
    }

    // Render the x axis.
    var xaxis = d3.select("#x-axis").selectAll("div")
      .data(hours)
      .enter()
      .append("div")
      .attr("class", function(d, i) {
        return "time " + ((i%2 == 0)?"even": "odd") + ((i == hours.length - 1)?" last":"");
      })
      .style("width", (100 / hours.length) + "%")
      .append("div")
      .text(function(d) {
        return d;
      })
      .attr("class", "time-label");

    // Set the height of the reservations
    $("#reservations").height($(window).height() - 170 - 60); // 170 is header height, 60 is footer padding

    // Render the rooms.
    var timeline = d3.select("#reservations");

    // Create one row per entry
    var rows = timeline.selectAll("div")
      .data(resources)
      .enter()
      .append("div")
      .attr("class","row")
      .style("height", (100/resources.length)  + "%")

    // Add the label
    rows.append("div")
      .attr("class", "resource")
      .append("div")
      .attr("class", "resource-label")
      .text(function(d) {
        return d.name;
      })

    // Add the reservations
    rows.each(function(resource) {
      var row = d3.select(this);
      var filtered = reservations.filter(function(d) {
        return d.resource.id == resource.id && d.from < (end - 60 * 60);
      });
      filtered.sort(function(a,b) {
        return a.from - b.from;
      })

      row.append("div")
        .attr("class","timeline")
        .selectAll("div")
        .data(filtered)
        .enter()
        .append("div")
        .attr("class","reservation")
        .style("width", function(d) {
          return (xscale(Math.min(d.to, end - 60 * 60)) - xscale(d.from)) + "%";
        })
        .style("left", function(d) {
          return (xscale(d.from)) + "%";
        })
        .append("div")
        .attr("class", "cell-bubble")
        .append("div")
        .attr("class","reservation-label")
        .text(function(d) {
          if(d.membership && d.title) return d.membership.name;
          else if(d.membership) return d.membership.name;
          else return "";
        });
    })
  }

  // Refresh reservations
  function refreshReservations() {
    $.ajax('/reservations', {
      method: 'GET',
      dataType: 'json'
    })
    .done(function(data) {
      reservations = data;
      renderGraph();
    })
  }

  refreshReservations();
  setInterval(refreshReservations,30000);

  // Load the resources
  $.ajax('/resources', {
    method: 'GET',
    dataType: 'json'
  })
  .done(function(data) {
    resources = data;
    renderGraph();
  })

})
<|endoftext|>


<commit_before>import React from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { FormattedMessage, useIntl } from 'react-intl'
import { connect } from 'react-redux'

import { LinkContainerWithQuery } from '../form/connected-links'
import { CREATE_TRIP_PATH } from '../../util/constants'
import Icon from '../util/icon'
import { itineraryCanBeMonitored } from '../../util/itinerary'
import { getActiveItinerary } from '../../util/state'

/**
 * This connected component encapsulates the states and behavior of the button
 * to save an itinerary for notifications.
 */
const SaveTripButton = ({
  itinerary,
  loggedInUser,
  persistence
}) => {
  const intl = useIntl()
  // We are dealing with the following states:
  // 1. Persistence disabled => just return null
  // 2. User is not logged in => render something like: "Please sign in to save trip".
  // 3. itin cannot be monitored => disable the button with prompt and tooltip.

  let buttonDisabled
  let buttonText
  let tooltipText
  let iconType

  if (!persistence || !persistence.enabled) {
    return null
  } else if (!loggedInUser) {
    buttonDisabled = true
    buttonText = <FormattedMessage id='components.SaveTripButton.signInText' />
    iconType = 'lock'
    tooltipText = intl.formatMessage({id: 'components.SaveTripButton.signInTooltip'})
  } else if (!itineraryCanBeMonitored(itinerary)) {
    buttonDisabled = true
    buttonText = <FormattedMessage id='components.SaveTripButton.cantSaveText' />
    iconType = 'ban'
    tooltipText = intl.formatMessage({id: 'components.SaveTripButton.cantSaveTooltip'})
  } else {
    buttonText = <FormattedMessage id='components.SaveTripButton.saveTripText' />
    iconType = 'plus-circle'
  }
  const button = (
    <button
      // Apply pull-right style class so that the element is flush right,
      // even with LineItineraries.
      className='clear-button-formatting pull-right'
      disabled={buttonDisabled}
      style={buttonDisabled ? { pointerEvents: 'none' } : {}}
    >
      <Icon type={iconType} withSpace />
      {buttonText}
    </button>
  )
  // Show tooltip with help text if button is disabled.
  if (buttonDisabled) {
    return (
      <OverlayTrigger
        overlay={(
          <Tooltip id='disabled-save-tooltip'>
            {/* Must get text using intl.formatMessage here because the rendering
             of OverlayTrigger seems to occur outside of the IntlProvider context. */}
            {tooltipText}
          </Tooltip>
        )}
        placement='top'
      >
        {/* An active element around the disabled button is necessary
            for the OverlayTrigger to render. */}

          {button}

      </OverlayTrigger>
    )
  }

  return (
    <LinkContainerWithQuery to={CREATE_TRIP_PATH}>
      {button}
    </LinkContainerWithQuery>
  )
}

// connect to the redux store

const mapStateToProps = (state, ownProps) => {
  const { persistence } = state.otp.config
  return {
    itinerary: getActiveItinerary(state),
    loggedInUser: state.user.loggedInUser,
    persistence
  }
}

export default connect(mapStateToProps)(SaveTripButton)
<commit_msg>refactor(save-trip-button): remove fa icon<commit_after>// TODO: Typescript
/* eslint-disable react/prop-types */
import { Ban } from '@styled-icons/fa-solid/Ban'
import { connect } from 'react-redux'
import { FormattedMessage, useIntl } from 'react-intl'
import { Lock } from '@styled-icons/fa-solid/Lock'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { PlusCircle } from '@styled-icons/fa-solid/PlusCircle'
import React from 'react'

import { CREATE_TRIP_PATH } from '../../util/constants'
import { getActiveItinerary } from '../../util/state'
import { itineraryCanBeMonitored } from '../../util/itinerary'
import { LinkContainerWithQuery } from '../form/connected-links'
import StyledIconWrapper from '../util/styledIcon'

/**
 * This connected component encapsulates the states and behavior of the button
 * to save an itinerary for notifications.
 */
const SaveTripButton = ({ itinerary, loggedInUser, persistence }) => {
  const intl = useIntl()
  // We are dealing with the following states:
  // 1. Persistence disabled => just return null
  // 2. User is not logged in => render something like: "Please sign in to save trip".
  // 3. itin cannot be monitored => disable the button with prompt and tooltip.

  let buttonDisabled
  let buttonText
  let tooltipText
  let iconType
  let ButtonIcon

  if (!persistence || !persistence.enabled) {
    return null
  } else if (!loggedInUser) {
    buttonDisabled = true
    buttonText = <FormattedMessage id="components.SaveTripButton.signInText" />
    iconType = 'lock'
    ButtonIcon = Lock
    tooltipText = intl.formatMessage({
      id: 'components.SaveTripButton.signInTooltip'
    })
  } else if (!itineraryCanBeMonitored(itinerary)) {
    buttonDisabled = true
    buttonText = (
      <FormattedMessage id="components.SaveTripButton.cantSaveText" />
    )
    iconType = 'ban'
    ButtonIcon = Ban
    tooltipText = intl.formatMessage({
      id: 'components.SaveTripButton.cantSaveTooltip'
    })
  } else {
    buttonText = (
      <FormattedMessage id="components.SaveTripButton.saveTripText" />
    )
    iconType = 'plus-circle'
    ButtonIcon = PlusCircle
  }
  const button = (
    <button
      // Apply pull-right style class so that the element is flush right,
      // even with LineItineraries.
      className="clear-button-formatting pull-right"
      disabled={buttonDisabled}
      style={buttonDisabled ? { pointerEvents: 'none' } : {}}
    >

      {buttonText}
    </button>
  )
  // Show tooltip with help text if button is disabled.
  if (buttonDisabled) {
    return (
      <OverlayTrigger
        overlay={
          <Tooltip id="disabled-save-tooltip">
            {/* Must get text using intl.formatMessage here because the rendering
             of OverlayTrigger seems to occur outside of the IntlProvider context. */}
            {tooltipText}
          </Tooltip>
        }
        placement="top"
      >
        {/* An active element around the disabled button is necessary
            for the OverlayTrigger to render. */}

          {button}

      </OverlayTrigger>
    )
  }

  return (
    <LinkContainerWithQuery to={CREATE_TRIP_PATH}>
      {button}
    </LinkContainerWithQuery>
  )
}

// connect to the redux store

const mapStateToProps = (state, ownProps) => {
  const { persistence } = state.otp.config
  return {
    itinerary: getActiveItinerary(state),
    loggedInUser: state.user.loggedInUser,
    persistence
  }
}

export default connect(mapStateToProps)(SaveTripButton)
<|endoftext|>


<commit_before>const Eris = require("eris");
const Steam = require("steam");
const redis = require("redis");
const pg = require("pg");
const config = require("./json/config.json");
var client = new Eris(config.token, config.options);
client.steam_client = new Steam.SteamClient();
client.steam_user = new Steam.SteamUser(client.steam_client);
client.steam_friends = new Steam.SteamFriends(client.steam_client);

const schedule = require("node-schedule");
const Mika = require("mika");
const bignumber = require("bignumber.js");

const util = require("util");
const fs = require("fs");

var _members = {};
var _channels = {};

client.commands = {};
client.all_usage = require("./json/usage.json");
client.usage = { "all": 0 };
client.mika = new Mika();
client.redis = redis.createClient();
client.pg = new pg.Client(config.pgconfig);
client.config = config;

for (let cmd of require("./util/consts.json").cmdlist) {
    client.commands[cmd] = require(`./commands/${cmd}`);
    client.usage[cmd] = 0;
    client.all_usage[cmd] = isNaN(client.all_usage[cmd]) ? 0 : client.all_usage[cmd];
}

function Helper(prefix) {
    this.prefix = prefix;

    this.log = (message, text) => {
        require("util").log(`${message.channel.guild.name}/${message.channel.name}: ${text}`);
    };

    this.handle = (message, err) => {
        let result = err.toString().split(" ")[1];
        if (result == "400") {
            this.log(message, "probably don't have permissions to embed here");
        } else if (result == "403") {
            this.log(message, "probably don't have permissions to send messages here");
        } else {
            this.log(message, err.toString());
        }
    };
}

client.write_usage_stats = schedule.scheduleJob("*/10 * * * *", () => {
    fs.writeFile("./json/usage.json", JSON.stringify(client.all_usage), (err) => {
        if (err) util.log(err);
    });
});

process.on("exit", (code) => {
    util.log(`Exiting with code ${code}`);
    fs.writeFileSync("./json/usage.json", JSON.stringify(client.all_usage));
});

client.pg.on("error", (err, pgclient) => {
    util.log("idle pgclient error", err.message, err.stack);
});

client.on("ready", () => {
    util.log("listen-bot ready.");

    client.shards.forEach(shard => {
        shard.editStatus("online", {
            "name": `${config.default_prefix}info | ${config.default_prefix}help [${shard.id + 1}/${client.shards.size}]`
        });
    });

    client.stats_messages = schedule.scheduleJob("*/15 * * * *", () => {
        client.editMessage(config.edit_channel, config.shard_edit_message, {
            "embed": {
                "description": require("./util/shardinfo_helper")(client)
            }
        }).catch(err => util.log(err));
        client.editMessage(config.edit_channel, config.stats_edit_message, {
            "embed": require("./util/stats_helper")(client)
        }).catch(err => util.log(err));
        if (config.dbots_token) {
            require("needle").post(
                `https://bots.discord.pw/api/bots/${config.master_id}/stats`,
                JSON.stringify({
                    "server_count": client.guilds.size
                }), {
                    "headers": {
                        "Authorization": config.dbots_token,
                        "Content-Type": "application/json"
                    }
                },
                (err, resp) => {
                    if (err) {
                        util.log(err);
                        return;
                    } else if (resp.statusCode != 200) {
                        util.log(resp.statusCode);
                    }
                }
            );
        }
    });
});

client.on("guildCreate", guild => {
    util.log(`${guild.id}/${guild.name}: joined guild on shard ${guild.shard.id}`);

    util.log("  inserting into database");
    client.pg.query({
        "text": "INSERT INTO public.guilds (id, name, prefix, climit, mlimit) VALUES ($1, $2, $3, $4, $5);",
        "values": [guild.id, guild.name, "--", 0, 0]
    }).then(() => {
        util.log("  inserted");
    }).catch(err => {
        util.log("  something went wrong inserting");
        util.log(err);
    });
});

client.on("guildUpdate", guild => {
    client.pg.query(`SELECT * FROM public.guilds WHERE id = '${guild.id}';`).then(res => {
        if (res.rows[0].name != guild.name) {
            util.log(`${guild.id}/${guild.name}: guild updated, modifying name`);

            client.pg.query({
                "text": "UPDATE public.guilds SET name = $1 WHERE id = $2",
                "values": [guild.name, guild.id]
            }).then(() => {
                util.log("  updated guild successfully");
            }).catch(err => {
                util.log("  something went wrong updating");
                util.log(err);
            });
        }
    }).catch(err => {
        util.log(` something went wrong selecting guild ${guild.id}/${guild.name}`);
        util.log(err);
    });
});

client.on("guildDelete", guild => {
    util.log(`${guild.id}/${guild.name}: left guild`);

    client.pg.query({
        "text": "DELETE FROM public.guilds WHERE id = $1",
        "values": [guild.id]
    }).then(() => {
        util.log("  deleted guild successfully");
    }).catch(err => {
        util.log("  something went wrong deleting");
        util.log(err);
    });
});

client.on("messageCreate", message => {
    if (!message.channel.guild) return;
    if (message.member && message.member.bot) return;
    if (message.author.id == client.user.id) return;

    if (!message.author) {
        util.log("no author");
        util.log(`${message.channel.guild.id}/${message.channel.guild.name}`);
        util.log(`${message.content}`);
        return;
    }

    client.pg.query({
        "text": "SELECT * FROM public.guilds WHERE id = $1;",
        "values": [message.channel.guild.id]
    }).then(res => {
        message.gcfg = res.rows[0];
        let _helper = new Helper(message.gcfg.prefix);

        if (message.content.startsWith(message.gcfg.prefix) || message.content.startsWith(config.default_prefix)) {
            if (message.content.startsWith(config.default_prefix)) message.content = message.content.replace(config.default_prefix, "");
            if (message.content.startsWith(message.gcfg.prefix)) message.content = message.content.replace(message.gcfg.prefix, "");
            message.content = message.content.trim();

            const command = message.content.split(" ").shift();
            if (command in client.commands) {
                if (!_members[message.member.id] || _members[message.member.id].last_message + message.gcfg.mlimit < Date.now()) {
                    if (!_channels[message.channel.id] || _channels[message.channel.id].last_message + message.gcfg.climit < Date.now()) {
                        let rate = {
                            "last_message": Date.now(),
                            "cooldown_sent": false
                        };

                        _members[message.member.id] = JSON.parse(JSON.stringify(rate));
                        _channels[message.channel.id] = JSON.parse(JSON.stringify(rate));
                        client.commands[command](message, client, _helper);
                        client.usage["all"] += 1;
                        client.usage[command] += 1;
                        client.all_usage["all"] += 1;
                        client.all_usage[command] += 1;
                    } else {
                        if (!_channels[message.channel.id].cooldown_sent) {
                            let timeleft = Math.ceil((_channels[message.channel.id].last_message + message.gcfg.climit - Date.now()) / 1000);
                            message.channel.createMessage(`\`#${message.channel.name}\` Please cool down! ${timeleft} second(s) left.`).then(new_message => {
                                setTimeout(() => { new_message.delete(); }, 4000);
                                _helper.log(message, "channel ratelimited");
                                _channels[message.channel.id].cooldown_sent = true;
                            }).catch(err => _helper.handle(err));
                        }
                    }
                } else {
                    if (!_members[message.member.id].cooldown_sent) {
                        let timeleft = Math.ceil((_members[message.member.id].last_message + message.gcfg.mlimit - Date.now()) / 1000);
                        message.channel.createmessage(`<@!${message.member.id}>, Please cool down! ${timeleft} second(s) left.`).then(new_message => {
                            setTimeout(() => { new_message.delete(); }, 4000);
                            _helper.log(message, "member ratelimited");
                            _members[message.member.id].cooldown_sent = true;
                        }).catch(err => _helper.handle(err));
                    }
                }
            }
        }
    }).catch(err => {
        util.log(`something went wrong with selcting ${message.channel.guild.id}/${message.channel.guild.name}`);
        util.log(err);
    });
});

client.steam_client.on("connected", () => {
    util.log("connected to steam.");
    util.log("logging on to steam...");
    client.steam_user.logOn(config.steam_config);
});

client.steam_client.on("logOnResponse", () => {
    client.steam_friends.setPersonaState(Steam.EPersonaState.Online);
});

client.steam_client.once("logOnResponse", () => {
    util.log("logged on to steam.");
    util.log("connecting to discord...");
    client.connect();
});

client.steam_friends.on("friend", (id, relationship) => {
    if (relationship == 3) {
        client.steam_friends.sendMessage(id, "Please send me the code you recieved on Discord!");
    }
});

client.steam_friends.on("friendMsg", (steam_id, message) => {
    let q = `register:${steam_id}`;
    client.redis.get(q, (err, reply) => {
        if (err) util.log(err);
        reply = reply.split(":");
        let code = reply[0];
        let discord_id = reply[1];
        if (code == message.trim()) {
            let dota_id = new bignumber(steam_id).minus("76561197960265728");

            client.pg.query({
                "text": "SELECT * FROM public.users WHERE id = $1;",
                "values": [discord_id]
            }).then(res => {
                if (res.rowCount == 0) {
                    client.pg.query({
                        "text": "INSERT INTO public.users (id, steamid, dotaid) VALUES ($1, $2, $3);",
                        "values": [discord_id, parseInt(steam_id), parseInt(dota_id)]
                    }).then(() => {
                        util.log(`  inserted dota id ${dota_id}`);
                        client.steam_friends.removeFriend(steam_id);
                        client.users.get(discord_id).getDMChannel().then(dm_channel => {
                            dm_channel.createMessage(`All set! Dota ID ${dota_id} associated with Discord ID ${discord_id} (<@${discord_id}>).`);
                        });
                    }).catch(err => {
                        util.log("  something went wrong inserting a user");
                        util.log(err);
                    });
                } else {
                    client.pg.query({
                        "text": "UPDATE public.users SET id = $1, steamid = $2, dotaid = $3 WHERE id = $1;",
                        "values": [discord_id, parseInt(steam_id), parseInt(dota_id)]
                    }).then(() => {
                        util.log(`  updated dota id ${res.rows[0].dotaid} -> ${dota_id}`);
                        client.steam_friends.removeFriend(steam_id);
                        client.users.get(discord_id).getDMChannel().then(dm_channel => {
                            dm_channel.createMessage(`All set! Dota ID ${dota_id} associated with Discord ID ${discord_id} (<@${discord_id}>).`);
                        });
                    }).catch(err => {
                        util.log("  something went wrong updating a user");
                        util.log(err);
                    });
                }
            }).catch(err => {
                util.log("  something went wrong selecting a user");
                util.log(err);
            });
        }
    });
});

// connect to everthing in order
client.redis.on("ready", () => {
    util.log("redis ready.");
    util.log("connecting to pg...");
    client.pg.connect((err) => {
        if (err) {
            util.log("err conencting to client");
            util.log(err);
            process.exit(1);
        }

        util.log("pg ready.");
        if (config.steam_config) {
            util.log("conncting to steam...");
            client.steam_client.connect();
        } else {
            util.log("conncting to discord...");
            client.connect();
        }
    });
});
<commit_msg>add steam flag instead of testing for config<commit_after>const Eris = require("eris");
const Steam = require("steam");
const redis = require("redis");
const pg = require("pg");
const config = require("./json/config.json");
var client = new Eris(config.token, config.options);
client.steam_client = new Steam.SteamClient();
client.steam_user = new Steam.SteamUser(client.steam_client);
client.steam_friends = new Steam.SteamFriends(client.steam_client);

const schedule = require("node-schedule");
const Mika = require("mika");
const bignumber = require("bignumber.js");

const util = require("util");
const fs = require("fs");

var _members = {};
var _channels = {};

client.commands = {};
client.all_usage = require("./json/usage.json");
client.usage = { "all": 0 };
client.mika = new Mika();
client.redis = redis.createClient();
client.pg = new pg.Client(config.pgconfig);
client.config = config;

for (let cmd of require("./util/consts.json").cmdlist) {
    client.commands[cmd] = require(`./commands/${cmd}`);
    client.usage[cmd] = 0;
    client.all_usage[cmd] = isNaN(client.all_usage[cmd]) ? 0 : client.all_usage[cmd];
}

function Helper(prefix) {
    this.prefix = prefix;

    this.log = (message, text) => {
        require("util").log(`${message.channel.guild.name}/${message.channel.name}: ${text}`);
    };

    this.handle = (message, err) => {
        let result = err.toString().split(" ")[1];
        if (result == "400") {
            this.log(message, "probably don't have permissions to embed here");
        } else if (result == "403") {
            this.log(message, "probably don't have permissions to send messages here");
        } else {
            this.log(message, err.toString());
        }
    };
}

client.write_usage_stats = schedule.scheduleJob("*/10 * * * *", () => {
    fs.writeFile("./json/usage.json", JSON.stringify(client.all_usage), (err) => {
        if (err) util.log(err);
    });
});

process.on("exit", (code) => {
    util.log(`Exiting with code ${code}`);
    fs.writeFileSync("./json/usage.json", JSON.stringify(client.all_usage));
});

client.pg.on("error", (err, pgclient) => {
    util.log("idle pgclient error", err.message, err.stack);
});

client.on("ready", () => {
    util.log("listen-bot ready.");

    client.shards.forEach(shard => {
        shard.editStatus("online", {
            "name": `${config.default_prefix}info | ${config.default_prefix}help [${shard.id + 1}/${client.shards.size}]`
        });
    });

    client.stats_messages = schedule.scheduleJob("*/15 * * * *", () => {
        client.editMessage(config.edit_channel, config.shard_edit_message, {
            "embed": {
                "description": require("./util/shardinfo_helper")(client)
            }
        }).catch(err => util.log(err));
        client.editMessage(config.edit_channel, config.stats_edit_message, {
            "embed": require("./util/stats_helper")(client)
        }).catch(err => util.log(err));
        if (config.dbots_token) {
            require("needle").post(
                `https://bots.discord.pw/api/bots/${config.master_id}/stats`,
                JSON.stringify({
                    "server_count": client.guilds.size
                }), {
                    "headers": {
                        "Authorization": config.dbots_token,
                        "Content-Type": "application/json"
                    }
                },
                (err, resp) => {
                    if (err) {
                        util.log(err);
                        return;
                    } else if (resp.statusCode != 200) {
                        util.log(resp.statusCode);
                    }
                }
            );
        }
    });
});

client.on("guildCreate", guild => {
    util.log(`${guild.id}/${guild.name}: joined guild on shard ${guild.shard.id}`);

    util.log("  inserting into database");
    client.pg.query({
        "text": "INSERT INTO public.guilds (id, name, prefix, climit, mlimit) VALUES ($1, $2, $3, $4, $5);",
        "values": [guild.id, guild.name, "--", 0, 0]
    }).then(() => {
        util.log("  inserted");
    }).catch(err => {
        util.log("  something went wrong inserting");
        util.log(err);
    });
});

client.on("guildUpdate", guild => {
    client.pg.query(`SELECT * FROM public.guilds WHERE id = '${guild.id}';`).then(res => {
        if (res.rows[0].name != guild.name) {
            util.log(`${guild.id}/${guild.name}: guild updated, modifying name`);

            client.pg.query({
                "text": "UPDATE public.guilds SET name = $1 WHERE id = $2",
                "values": [guild.name, guild.id]
            }).then(() => {
                util.log("  updated guild successfully");
            }).catch(err => {
                util.log("  something went wrong updating");
                util.log(err);
            });
        }
    }).catch(err => {
        util.log(` something went wrong selecting guild ${guild.id}/${guild.name}`);
        util.log(err);
    });
});

client.on("guildDelete", guild => {
    util.log(`${guild.id}/${guild.name}: left guild`);

    client.pg.query({
        "text": "DELETE FROM public.guilds WHERE id = $1",
        "values": [guild.id]
    }).then(() => {
        util.log("  deleted guild successfully");
    }).catch(err => {
        util.log("  something went wrong deleting");
        util.log(err);
    });
});

client.on("messageCreate", message => {
    if (!message.channel.guild) return;
    if (message.member && message.member.bot) return;
    if (message.author.id == client.user.id) return;

    if (!message.author) {
        util.log("no author");
        util.log(`${message.channel.guild.id}/${message.channel.guild.name}`);
        util.log(`${message.content}`);
        return;
    }

    client.pg.query({
        "text": "SELECT * FROM public.guilds WHERE id = $1;",
        "values": [message.channel.guild.id]
    }).then(res => {
        message.gcfg = res.rows[0];
        let _helper = new Helper(message.gcfg.prefix);

        if (message.content.startsWith(message.gcfg.prefix) || message.content.startsWith(config.default_prefix)) {
            if (message.content.startsWith(config.default_prefix)) message.content = message.content.replace(config.default_prefix, "");
            if (message.content.startsWith(message.gcfg.prefix)) message.content = message.content.replace(message.gcfg.prefix, "");
            message.content = message.content.trim();

            const command = message.content.split(" ").shift();
            if (command in client.commands) {
                if (!_members[message.member.id] || _members[message.member.id].last_message + message.gcfg.mlimit < Date.now()) {
                    if (!_channels[message.channel.id] || _channels[message.channel.id].last_message + message.gcfg.climit < Date.now()) {
                        let rate = {
                            "last_message": Date.now(),
                            "cooldown_sent": false
                        };

                        _members[message.member.id] = JSON.parse(JSON.stringify(rate));
                        _channels[message.channel.id] = JSON.parse(JSON.stringify(rate));
                        client.commands[command](message, client, _helper);
                        client.usage["all"] += 1;
                        client.usage[command] += 1;
                        client.all_usage["all"] += 1;
                        client.all_usage[command] += 1;
                    } else {
                        if (!_channels[message.channel.id].cooldown_sent) {
                            let timeleft = Math.ceil((_channels[message.channel.id].last_message + message.gcfg.climit - Date.now()) / 1000);
                            message.channel.createMessage(`\`#${message.channel.name}\` Please cool down! ${timeleft} second(s) left.`).then(new_message => {
                                setTimeout(() => { new_message.delete(); }, 4000);
                                _helper.log(message, "channel ratelimited");
                                _channels[message.channel.id].cooldown_sent = true;
                            }).catch(err => _helper.handle(err));
                        }
                    }
                } else {
                    if (!_members[message.member.id].cooldown_sent) {
                        let timeleft = Math.ceil((_members[message.member.id].last_message + message.gcfg.mlimit - Date.now()) / 1000);
                        message.channel.createmessage(`<@!${message.member.id}>, Please cool down! ${timeleft} second(s) left.`).then(new_message => {
                            setTimeout(() => { new_message.delete(); }, 4000);
                            _helper.log(message, "member ratelimited");
                            _members[message.member.id].cooldown_sent = true;
                        }).catch(err => _helper.handle(err));
                    }
                }
            }
        }
    }).catch(err => {
        util.log(`something went wrong with selcting ${message.channel.guild.id}/${message.channel.guild.name}`);
        util.log(err);
    });
});

client.steam_client.on("connected", () => {
    util.log("connected to steam.");
    util.log("logging on to steam...");
    client.steam_user.logOn(config.steam_config);
});

client.steam_client.on("logOnResponse", () => {
    client.steam_friends.setPersonaState(Steam.EPersonaState.Online);
});

client.steam_client.once("logOnResponse", () => {
    util.log("logged on to steam.");
    util.log("connecting to discord...");
    client.connect();
});

client.steam_friends.on("friend", (id, relationship) => {
    if (relationship == 3) {
        client.steam_friends.sendMessage(id, "Please send me the code you recieved on Discord!");
    }
});

client.steam_friends.on("friendMsg", (steam_id, message) => {
    let q = `register:${steam_id}`;
    client.redis.get(q, (err, reply) => {
        if (err) util.log(err);
        reply = reply.split(":");
        let code = reply[0];
        let discord_id = reply[1];
        if (code == message.trim()) {
            let dota_id = new bignumber(steam_id).minus("76561197960265728");

            client.pg.query({
                "text": "SELECT * FROM public.users WHERE id = $1;",
                "values": [discord_id]
            }).then(res => {
                if (res.rowCount == 0) {
                    client.pg.query({
                        "text": "INSERT INTO public.users (id, steamid, dotaid) VALUES ($1, $2, $3);",
                        "values": [discord_id, parseInt(steam_id), parseInt(dota_id)]
                    }).then(() => {
                        util.log(`  inserted dota id ${dota_id}`);
                        client.steam_friends.removeFriend(steam_id);
                        client.users.get(discord_id).getDMChannel().then(dm_channel => {
                            dm_channel.createMessage(`All set! Dota ID ${dota_id} associated with Discord ID ${discord_id} (<@${discord_id}>).`);
                        });
                    }).catch(err => {
                        util.log("  something went wrong inserting a user");
                        util.log(err);
                    });
                } else {
                    client.pg.query({
                        "text": "UPDATE public.users SET id = $1, steamid = $2, dotaid = $3 WHERE id = $1;",
                        "values": [discord_id, parseInt(steam_id), parseInt(dota_id)]
                    }).then(() => {
                        util.log(`  updated dota id ${res.rows[0].dotaid} -> ${dota_id}`);
                        client.steam_friends.removeFriend(steam_id);
                        client.users.get(discord_id).getDMChannel().then(dm_channel => {
                            dm_channel.createMessage(`All set! Dota ID ${dota_id} associated with Discord ID ${discord_id} (<@${discord_id}>).`);
                        });
                    }).catch(err => {
                        util.log("  something went wrong updating a user");
                        util.log(err);
                    });
                }
            }).catch(err => {
                util.log("  something went wrong selecting a user");
                util.log(err);
            });
        }
    });
});

// connect to everthing in order
client.redis.on("ready", () => {
    util.log("redis ready.");
    util.log("connecting to pg...");
    client.pg.connect((err) => {
        if (err) {
            util.log("err conencting to client");
            util.log(err);
            process.exit(1);
        }

        util.log("pg ready.");
        if (config.steam_enabled) {
            util.log("conncting to steam...");
            client.steam_client.connect();
        } else {
            util.log("conncting to discord...");
            client.connect();
        }
    });
});
<|endoftext|>