<commit_before>/*

Mutations have four steps:

1. Validation

If the mutation call is not trusted (i.e. it comes from a GraphQL mutation),
we'll run all validate steps:

- Check that the current user has permission to insert/edit each field.
- Validate document against collection schema.
- Add userId to document (insert only).
- Run validation callbacks.

2. Sync Callbacks

The second step is to run the mutation argument through all the sync callbacks.

3. Operation

We then perform the insert/update/remove operation.

4. Async Callbacks

Finally, *after* the operation is performed, we execute any async callbacks.
Being async, they won't hold up the mutation and slow down its response time
to the client.

*/

import { runCallbacks, runCallbacksAsync } from './callbacks.js';

export const newMutation = ({ collection, document, currentUser, validate, context }) => {

  console.log("// newMutation")
  console.log(collection._name)
  console.log(document)

  const collectionName = collection._name;
  const schema = collection.simpleSchema()._schema;

  // if document is not trusted, run validation steps
  if (validate) {

    // check that the current user has permission to insert each field
    _.keys(document).forEach(function (fieldName) {
      var field = schema[fieldName];
      if (!context.Users.canInsertField (currentUser, field)) {
        throw new Meteor.Error('disallowed_property', `disallowed_property_detected: ${fieldName}`);
      }
    });

    // validate document against schema
    collection.simpleSchema().namedContext(`${collectionName}.new`).validate(document);

    // run validation callbacks
    document = runCallbacks(`${collectionName}.new.validate`, document, currentUser);
  }

  // check if userId field is in the schema and add it to document if needed
  const userIdInSchema = Object.keys(schema).find(key => key === 'userId');
  if (!!userIdInSchema && !document.userId) document.userId = currentUser._id;

  // TODO: find that info in GraphQL mutations
  // if (Meteor.isServer && this.connection) {
  //   post.userIP = this.connection.clientAddress;
  //   post.userAgent = this.connection.httpHeaders["user-agent"];
  // }

  // run sync callbacks
  document = runCallbacks(`${collectionName}.new.sync`, document, currentUser);

  // add _id to document
  document._id = collection.insert({...document}); // use {...document} to "enable" Object.prototype (see https://cl.ly/1N1m2d0y3u1A)

  // get fresh copy of document from db
  const newDocument = collection.findOne(document._id);

  // run async callbacks
  // note: query for document to get fresh document with collection-hooks effects applied
  runCallbacksAsync(`${collectionName}.new.async`, newDocument, currentUser);

  console.log("// new mutation finished:")
  console.log(newDocument)
  return document;
}

export const editMutation = ({ collection, documentId, set, unset, currentUser, validate, context }) => {

  console.log("// editMutation")
  console.log(collection._name)
  console.log(documentId)
  console.log(set)
  console.log(unset)

  const collectionName = collection._name;
  const schema = collection.simpleSchema()._schema;

  // build mongo modifier from arguments
  let modifier = {$set: set, $unset: unset};

  // get original document from database
  let document = collection.findOne(documentId);

  // if document is not trusted, run validation steps
  if (validate) {

    // check that the current user has permission to edit each field
    const modifiedProperties = _.keys(set).concat(_.keys(unset));
    modifiedProperties.forEach(function (fieldName) {
      var field = schema[fieldName];
      console.log(field);
      if (!context.Users.canEditField(currentUser, field, document)) {
        throw new Meteor.Error('disallowed_property', `disallowed_property_detected: ${fieldName}`);
      }
    });

    console.log(modifier);

    // validate modifier against schema
    collection.simpleSchema().namedContext(`${collectionName}.edit`).validate(modifier, {modifier: true});

    // run validation callbacks
    modifier = runCallbacks(`${collectionName}.edit.validate`, modifier, document, currentUser);
  }

  // run sync callbacks (on mongo modifier)
  modifier = runCallbacks(`${collectionName}.edit.sync`, modifier, document, currentUser);

  // update document
  collection.update(documentId, modifier);

  // get fresh copy of document from db
  const newDocument = collection.findOne(documentId);

  // run async callbacks
  runCallbacksAsync(`${collectionName}.edit.async`, newDocument, document, currentUser);

  console.log("// edit mutation finished")
  console.log(newDocument)

  return newDocument;
}

export const removeMutation = ({ collection, documentId, currentUser, validate, context }) => {

  console.log("// removeMutation")
  console.log(collection._name)
  console.log(documentId)

  const collectionName = collection._name;

  let document = collection.findOne(documentId);

  // if document is not trusted, run validation callbacks
  if (validate) {
    document = runCallbacks(`${collectionName}.remove.validate`, document, currentUser);
  }

  runCallbacks(`${collectionName}.remove.sync`, document, currentUser);

  collection.remove(documentId);

  runCallbacksAsync(`${collectionName}.remove.async`, document, currentUser);

  return document;
}
<commit_msg>add note link to issue in collection2 on mutation insert, remove debug console logs on mutation edit<commit_after>/*

Mutations have four steps:

1. Validation

If the mutation call is not trusted (i.e. it comes from a GraphQL mutation),
we'll run all validate steps:

- Check that the current user has permission to insert/edit each field.
- Validate document against collection schema.
- Add userId to document (insert only).
- Run validation callbacks.

2. Sync Callbacks

The second step is to run the mutation argument through all the sync callbacks.

3. Operation

We then perform the insert/update/remove operation.

4. Async Callbacks

Finally, *after* the operation is performed, we execute any async callbacks.
Being async, they won't hold up the mutation and slow down its response time
to the client.

*/

import { runCallbacks, runCallbacksAsync } from './callbacks.js';

export const newMutation = ({ collection, document, currentUser, validate, context }) => {

  console.log("// newMutation")
  console.log(collection._name)
  console.log(document)

  const collectionName = collection._name;
  const schema = collection.simpleSchema()._schema;

  // if document is not trusted, run validation steps
  if (validate) {

    // check that the current user has permission to insert each field
    _.keys(document).forEach(function (fieldName) {
      var field = schema[fieldName];
      if (!context.Users.canInsertField (currentUser, field)) {
        throw new Meteor.Error('disallowed_property', `disallowed_property_detected: ${fieldName}`);
      }
    });

    // validate document against schema
    collection.simpleSchema().namedContext(`${collectionName}.new`).validate(document);

    // run validation callbacks
    document = runCallbacks(`${collectionName}.new.validate`, document, currentUser);
  }

  // check if userId field is in the schema and add it to document if needed
  const userIdInSchema = Object.keys(schema).find(key => key === 'userId');
  if (!!userIdInSchema && !document.userId) document.userId = currentUser._id;

  // TODO: find that info in GraphQL mutations
  // if (Meteor.isServer && this.connection) {
  //   post.userIP = this.connection.clientAddress;
  //   post.userAgent = this.connection.httpHeaders["user-agent"];
  // }

  // run sync callbacks
  document = runCallbacks(`${collectionName}.new.sync`, document, currentUser);

  // add _id to document
  // note: use {...document} to "enable" Object.prototype (see https://cl.ly/1N1m2d0y3u1A)
  // see https://github.com/aldeed/meteor-collection2-core/issues/8
  document._id = collection.insert({...document});

  // get fresh copy of document from db
  const newDocument = collection.findOne(document._id);

  // run async callbacks
  // note: query for document to get fresh document with collection-hooks effects applied
  runCallbacksAsync(`${collectionName}.new.async`, newDocument, currentUser);

  console.log("// new mutation finished:")
  console.log(newDocument)
  return document;
}

export const editMutation = ({ collection, documentId, set, unset, currentUser, validate, context }) => {

  console.log("// editMutation")
  console.log(collection._name)
  console.log(documentId)
  console.log(set)
  console.log(unset)

  const collectionName = collection._name;
  const schema = collection.simpleSchema()._schema;

  // build mongo modifier from arguments
  let modifier = {$set: set, $unset: unset};

  // get original document from database
  let document = collection.findOne(documentId);

  // if document is not trusted, run validation steps
  if (validate) {

    // check that the current user has permission to edit each field
    const modifiedProperties = _.keys(set).concat(_.keys(unset));
    modifiedProperties.forEach(function (fieldName) {
      const field = schema[fieldName];
      if (!context.Users.canEditField(currentUser, field, document)) {
        throw new Meteor.Error('disallowed_property', `disallowed_property_detected: ${fieldName}`);
      }
    });

    // validate modifier against schema
    collection.simpleSchema().namedContext(`${collectionName}.edit`).validate(modifier, {modifier: true});

    // run validation callbacks
    modifier = runCallbacks(`${collectionName}.edit.validate`, modifier, document, currentUser);
  }

  // run sync callbacks (on mongo modifier)
  modifier = runCallbacks(`${collectionName}.edit.sync`, modifier, document, currentUser);

  // update document
  collection.update(documentId, modifier);

  // get fresh copy of document from db
  const newDocument = collection.findOne(documentId);

  // run async callbacks
  runCallbacksAsync(`${collectionName}.edit.async`, newDocument, document, currentUser);

  console.log("// edit mutation finished")
  console.log(newDocument)

  return newDocument;
}

export const removeMutation = ({ collection, documentId, currentUser, validate, context }) => {

  console.log("// removeMutation")
  console.log(collection._name)
  console.log(documentId)

  const collectionName = collection._name;

  let document = collection.findOne(documentId);

  // if document is not trusted, run validation callbacks
  if (validate) {
    document = runCallbacks(`${collectionName}.remove.validate`, document, currentUser);
  }

  runCallbacks(`${collectionName}.remove.sync`, document, currentUser);

  collection.remove(documentId);

  runCallbacksAsync(`${collectionName}.remove.async`, document, currentUser);

  return document;
}
<|endoftext|>


<commit_before>const path = require('path');

const resolveFrom = require('resolve-from');
const parentModule = require('parent-module');

const resolve = (moduleId) => {
    try {
        return resolveFrom(path.dirname(parentModule(__filename)), moduleId);
    } catch (_) {
        /**/
    }
};

const clearModule = (moduleId) => {
    if (typeof moduleId !== 'string') {
        throw new TypeError(`Expected a \`string\`, got \`${typeof moduleId}\``);
    }

    const filePath = resolve(moduleId);

    if (!filePath) {
        return;
    }

    // Delete itself from module parent
    // Module parent should always be the adapter file that requires components
    if (require.cache[filePath] && require.cache[filePath].parent) {
        let i = require.cache[filePath].parent.children.length;

        while (i--) {
            const parentChild = require.cache[filePath].parent.children[i];
            if (parentChild.id === filePath) {
                require.cache[filePath].parent.children.splice(i, 1);
            } else if (isModuleDependentOnPath(parentChild, filePath)) {
                require.cache[filePath].parent.children.splice(i, 1);

                // Delete parent children that are dependent on filePath from cache
                delete require.cache[parentChild.id];
            }
        }
    }

    // Delete module from cache
    if (require.cache[filePath]) {
        delete require.cache[filePath];
    }
};

const isModuleDependentOnPath = (module, filePath) => {
    let i = module.children.length;

    while (i--) {
        const parentChild = module.children[i];
        if (parentChild.id === filePath) {
            return true;
        } else if (parentChild.children.length && !parentChild.id.includes('node_modules')) {
            // Deep check if module is dependent on path
            return isModuleDependentOnPath(parentChild, filePath);
        }
    }

    return false;
};

module.exports = clearModule;
<commit_msg>fix(react): fix infinite loop when checking for module dependent paths (#1058)<commit_after>const path = require('path');

const resolveFrom = require('resolve-from');
const parentModule = require('parent-module');

const resolve = (moduleId) => {
    try {
        return resolveFrom(path.dirname(parentModule(__filename)), moduleId);
    } catch (_) {
        /**/
    }
};

const clearModule = (moduleId) => {
    if (typeof moduleId !== 'string') {
        throw new TypeError(`Expected a \`string\`, got \`${typeof moduleId}\``);
    }

    const filePath = resolve(moduleId);

    if (!filePath) {
        return;
    }

    // Delete itself from module parent
    // Module parent should always be the adapter file that requires components
    if (require.cache[filePath] && require.cache[filePath].parent) {
        let i = require.cache[filePath].parent.children.length;

        while (i--) {
            const parentChild = require.cache[filePath].parent.children[i];
            if (parentChild.id === filePath) {
                require.cache[filePath].parent.children.splice(i, 1);
            } else if (isModuleDependentOnPath(parentChild, filePath)) {
                require.cache[filePath].parent.children.splice(i, 1);

                // Delete parent children that are dependent on filePath from cache
                delete require.cache[parentChild.id];
            }
        }
    }

    // Delete module from cache
    if (require.cache[filePath]) {
        delete require.cache[filePath];
    }
};

const isModuleDependentOnPath = (module, filePath, checkedDependencies = []) => {
    checkedDependencies.push(module.id);
    let i = module.children.length;

    while (i--) {
        const parentChild = module.children[i];
        if (parentChild.id === filePath) {
            return true;
        } else if (
            parentChild.children.length &&
            !parentChild.id.includes('node_modules') &&
            !checkedDependencies.includes(parentChild.id)
        ) {
            // Deep check if module is dependent on path
            return isModuleDependentOnPath(parentChild, filePath, checkedDependencies);
        }
    }

    return false;
};

module.exports = clearModule;
<|endoftext|>


<commit_before>// Copyright 2019-2020, University of Colorado Boulder

/**
 * An ScrollingChartNode that includes a draggable cursor that allows the user to scrub or play back through the data.
 *
 * @author Jesse Greenberg
 */

import Utils from '../../dot/js/Utils.js';
import merge from '../../phet-core/js/merge.js';
import ArrowNode from '../../scenery-phet/js/ArrowNode.js';
import DragListener from '../../scenery/js/listeners/DragListener.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import Node from '../../scenery/js/nodes/Node.js';
import Rectangle from '../../scenery/js/nodes/Rectangle.js';
import Color from '../../scenery/js/util/Color.js';
import Tandem from '../../tandem/js/Tandem.js';
import griddle from './griddle.js';
import ScrollingChartNode from './ScrollingChartNode.js';

// constants
const WIDTH_PROPORTION = 0.013; // empirically determined
const CURSOR_FILL_COLOR = new Color( 50, 50, 200, 0.2 );
const CURSOR_STROKE_COLOR = Color.DARK_GRAY;
const ARROW_CUE_FILL_COLOR = new Color( 180, 180, 230 );
const ARROW_CUE_STROKE_COLOR = Color.DARK_GRAY;

class XYCursorPlot extends ScrollingChartNode {

  /**
   * @param {Object} [options]
   */
  constructor( options ) {
    options = merge( {

      // options passed on to the cursor, see ChartCursor
      cursorOptions: null,

      graphPanelOptions: {

        // so that the cursor is draggable
        pickable: true
      },

      // phet-io
      tandem: Tandem.OPTIONAL
    }, options );

    super( options );

    // @private {boolean|null} - if set with setCursorVisible, then this will indicate visibility of the cursor
    this._cursorVisibleOverride = null;

    // @private {number} - value for the cursor, determines cursor positioning
    this.cursorValue = 0;

    // @private - minimum and maximum recorded value, required by the cursor to limit dragging
    this.minRecordedXValue = 0;
    this.maxRecordedXValue = 0;

    // @private Map.<DynamicSeries,function> Keep track of the listener for each series, so the listener can be removed
    // when a series is removed.
    this.dynamicSeriesListenerMap = new Map();

    // @protected (read-only) {DynamicSeries[]}
    this.dynamicSeriesList = [];

    // @private {ChartCursor} - draggable Node that shows the cursor value
    this.chartCursor = new ChartCursor( this, this.modelViewTransformProperty, options.cursorOptions );
    this.graphPanel.addChild( this.chartCursor );

    // initialize position and visibility of the cursor
    this.updateCursor();
  }

  /**
   * Adds a DynamicSeries to the XYCursorPlot.
   * @override
   * @public
   *
   * @param {DynamicSeries} dynamicSeries
   */
  addDynamicSeries( dynamicSeries ) {
    super.addDynamicSeries( dynamicSeries );

    this.dynamicSeriesList.push( dynamicSeries );

    // when a point is added, update the min and max recorded values
    const dynamicSeriesListener = () => {

      // update the
      this.updateMinMaxRecordedValues();

      // if all data has been removed from the plot, update cursor visibility
      this.updateCursor();
    };

    // save to map so that listener can be found again for disposal
    this.dynamicSeriesListenerMap.set( dynamicSeries, dynamicSeriesListener );
    dynamicSeries.addDynamicSeriesListener( dynamicSeriesListener );
  }

  /**
   * Removes a DynamicSeries from the plot and disposes of its listener.
   * @override
   * @public
   *
   * @param {DynamicSeries} dynamicSeries - to remove
   */
  removeDynamicSeries( dynamicSeries ) {
    super.removeDynamicSeries( dynamicSeries );

    const seriesIndex = this.dynamicSeriesList.indexOf( dynamicSeries );
    this.dynamicSeriesList.splice( seriesIndex, 1 );

    dynamicSeries.removeDynamicSeriesListener( this.dynamicSeriesListenerMap.get( dynamicSeries ) );
    this.dynamicSeriesListenerMap.delete( dynamicSeries );
  }

  /**
   * Sets the cursor value. The value of the cursor is constrained to be within plot bounds.
   * @public
   *
   * @param {number} value
   */
  setCursorValue( value ) {
    const modelViewTransform = this.modelViewTransformProperty.get();
    const minX = modelViewTransform.viewToModelX( 0 );
    const maxX = modelViewTransform.viewToModelX( this.plotWidth + this.chartCursor.width / 2 );
    this.cursorValue = Utils.clamp( value, minX, maxX );
    this.updateCursor();
  }

  /**
   * Gets the value currently under the cursor.
   * @public
   *
   * @returns {number}
   */
  getCursorValue() {
    return this.cursorValue;
  }

  /**
   * Resets the cursor.
   * @public
   */
  resetCursor() {
    this.chartCursor.reset();
  }

  /**
   * Overrides the default behavior for setting cursor visibility. If set to null, cursor visibility will behave as
   * described in updateCursorVisibility. Otherwise, visibility will equal the boolean value set here.
   * @param {boolean|null} visible
   * @public
   */
  setCursorVisibleOverride( visible ) {
    assert && assert( typeof visible === 'boolean' || visible === null, 'visible must be boolean or null' );
    this._cursorVisibleOverride = visible;
    this.updateCursorVisibility();
  }

  /**
   * Updates the cursor visibility and position.
   * @private
   */
  updateCursor() {
    this.updateCursorVisibility();
    if ( this.chartCursor.isVisible() ) {
      this.updateCursorPos();
    }
  }

  /**
   * Updates the cursor position.
   * @private
   */
  updateCursorPos() {
    this.moveCursorToValue( this.cursorValue );
  }

  /**
   * Updates the cursor visibility. The cursor should be visible any time the cursor value is within
   * the recorded value range.
   * @private
   */
  updateCursorVisibility() {

    const wasVisible = this.chartCursor.visible;
    if ( typeof this._cursorVisibleOverride === 'boolean' ) {
      this.chartCursor.setVisible( this._cursorVisibleOverride );
    }
    else {

      const maxX = this.modelViewTransformProperty.get().viewToModelX( this.plotWidth + this.chartCursor.width / 2 );
      const minX = this.modelViewTransformProperty.get().viewToModelX( 0 );

      const isCurrentValueOnChart = ( this.cursorValue >= minX ) && ( this.cursorValue <= maxX );
      const dataExists = this.getDataExists();
      const chartCursorVisible = isCurrentValueOnChart && dataExists;

      this.chartCursor.setVisible( chartCursorVisible );
    }

    // if the cursor just became invisible, interrupt any active dragging
    if ( !this.chartCursor.visible && wasVisible ) {
      this.chartCursor.interruptDrag();
    }
  }

  /**
   * Returns true if any data is attached to this plot.
   * @returns {boolean}
   * @public
   */
  getDataExists() {
    let dataExists = false;
    this.dynamicSeriesListenerMap.forEach( ( listener, dataSeries, map ) => {
      dataExists = dataSeries.hasData();

      // break early
      if ( dataExists ) {
        return true;
      }
    } );

    return dataExists;
  }

  /**
   * Moves the cursor to the specified value.
   * @private
   *
   * @param {number} value
   */
  moveCursorToValue( value ) {
    const viewPosition = this.modelViewTransformProperty.get().modelToViewX( value );

    // keep the cursor within the grid bounds
    this.chartCursor.centerX = Utils.clamp( viewPosition, 0, this.plotWidth );
    this.chartCursor.centerY = this.gridNode.centerY;
  }

  /**
   * From the existing data, update the min and max recorded X values from all of the dynamicSeries of this plot,
   * so that the cursor can be limited to the recorded data.
   * @private
   */
  updateMinMaxRecordedValues() {
    let minValue = Number.POSITIVE_INFINITY;
    let maxValue = Number.NEGATIVE_INFINITY;
    this.dynamicSeriesList.forEach( dynamicSeries => {
      if ( dynamicSeries.getLength() > 0 ) {
        const seriesMinValue = dynamicSeries.getDataPoint( 0 ).x;
        const seriesMaxValue = dynamicSeries.getDataPoint( dynamicSeries.getLength() - 1 ).x;
        if ( seriesMinValue < minValue ) {
          minValue = seriesMinValue;
        }
        if ( seriesMaxValue > maxValue ) {
          maxValue = seriesMaxValue;
        }
      }
    } );

    this.minRecordedXValue = minValue;
    this.maxRecordedXValue = maxValue;
  }
}

/**
 * Rectangular cursor that indicates a current or selected value on the chart.
 */
class ChartCursor extends Rectangle {

  /**
   * @param {ScrollingChartNode} plot
   * @param {Property.<ModelViewTransform2>} modelViewTransformProperty
   * @param {Object} [options]
   */
  constructor( plot, modelViewTransformProperty, options ) {

    options = merge( {
      startDrag: () => {},
      endDrag: () => {},
      drag: () => {},

      // {boolean} - if true, a double headed arrow will be shown to indicate
      // that the cursor is draggable - becomes invisible after first
      // drag
      includeDragCue: false,

      // phet-io
      tandem: Tandem.OPTIONAL
    }, options );

    const width = plot.plotWidth * WIDTH_PROPORTION;
    const height = plot.plotHeight;

    // Set the shape. Origin is at the center top of the rectangle.
    super( 0, -height, width, height, 0, 0, {
      cursor: 'ew-resize',
      fill: CURSOR_FILL_COLOR,
      stroke: CURSOR_STROKE_COLOR,
      lineWidth: 0.4,
      lineDash: [ 4, 4 ]
    } );

    this.plot = plot;
    this.modelViewTransformProperty = modelViewTransformProperty;

    // Make it easier to grab this cursor by giving it expanded mouse and touch areas.
    this.mouseArea = this.localBounds.dilatedX( 12 );
    this.touchArea = this.localBounds.dilatedX( 12 );

    this.includeDragCue = options.includeDragCue;

    if ( this.includeDragCue ) {

      // @private - indicates to the user that the cursor is draggable, only created
      // and added if necessary
      this.dragCueArrowNode = new ArrowNode( -width * 2, 0, width * 2, 0, {
        doubleHead: true,
        headWidth: 12,
        headHeight: 10,
        fill: ARROW_CUE_FILL_COLOR,
        stroke: ARROW_CUE_STROKE_COLOR,
        center: this.center.plusXY( 0, height * 0.4 )
      } );

      this.addChild( this.dragCueArrowNode );
    }

    // Add the indentations that are intended to convey the idea of "gripability".
    const grippyNode = new Node();
    const indentSpacing = height * 0.05;
    for ( let i = 0; i < 3; i++ ) {
      const indentNode = new GrippyIndentNode( width / 2, CURSOR_FILL_COLOR );
      indentNode.top = i * ( indentNode.height + indentSpacing );
      grippyNode.addChild( indentNode );
    }
    grippyNode.center = this.center;
    this.addChild( grippyNode );

    // @private - so that we can interrupt the DragListener if necessary
    this.dragListener = new DragListener( {
      start: ( event, listener ) => {
        assert && assert( this.plot.getDataExists(), 'data should exist for the cursor to be draggable' );
        options.startDrag();
      },
      drag: ( event, listener ) => {
        const parentX = listener.parentPoint.x;
        let newValue = this.modelViewTransformProperty.get().viewToModelX( parentX );

        // limit cursor to the domain of recorded values
        newValue = Utils.clamp( newValue, this.plot.minRecordedXValue, this.plot.maxRecordedXValue );
        this.plot.setCursorValue( newValue );

        options.drag();
      },
      end: ( event, listener ) => {
        options.endDrag();

        // no need to show arrow after user successfully drags the cursor
        if ( this.includeDragCue ) {
          this.dragCueArrowNode.visible = false;
        }
      },
      tandem: options.tandem.createTandem( 'dragListener' )
    } );
    this.addInputListener( this.dragListener );
  }

  /**
   * Interrupt dragging of the cursor, useful when ChartCursor visibility changes.
   * @public
   */
  interruptDrag() {
    this.dragListener.interrupt();
  }

  /**
   * Reset the ChartCursor to its initial state. Note that this does not modify data
   * or the cursor position (cursorValue), only aspects of the view for the cursor itself.
   * @public
   */
  reset() {
    if ( this.includeDragCue ) {
      this.dragCueArrowNode.visible = true;
    }
  }
}

/**
 * This node is meant to portray a small round indentation on a surface.  This is a modern user interface paradigm that
 * is intended to convey the concept of "gripability" (sp?), i.e. something that the user can click on and subsequently
 * grab.  This is meant to look somewhat 3D, much like etched borders do.
 */
class GrippyIndentNode extends Circle {

  /**
   * @param {number} diameter
   * @param {Color} baseColor
   * @param {Object} [options]
   */
  constructor( diameter, baseColor, options ) {

    options = merge( {
      lineWidth: 0.5
    }, options );

    const baseDarkerColor = baseColor.darkerColor( 0.9 );
    const translucentDarkerColor = new Color( baseDarkerColor.getRed(), baseDarkerColor.getGreen(),
      baseDarkerColor.getBlue(), baseColor.getAlpha() );
    const baseLighterColor = baseColor.brighterColor( 0.9 );
    const translucentBrighterColor = new Color( baseLighterColor.getRed(), baseLighterColor.getGreen(),
      baseLighterColor.getBlue(), baseColor.getAlpha() );
    const radius = diameter / 2 - options.lineWidth;

    super( radius, {
      fill: translucentDarkerColor,
      stroke: translucentBrighterColor,
      lineWidth: options.lineWidth
    } );
  }
}

griddle.register( 'XYCursorPlot', XYCursorPlot );
export default XYCursorPlot;<commit_msg>rename updateCursorPosition, https://github.com/phetsims/griddle/issues/70<commit_after>// Copyright 2019-2020, University of Colorado Boulder

/**
 * An ScrollingChartNode that includes a draggable cursor that allows the user to scrub or play back through the data.
 *
 * @author Jesse Greenberg
 */

import Utils from '../../dot/js/Utils.js';
import merge from '../../phet-core/js/merge.js';
import ArrowNode from '../../scenery-phet/js/ArrowNode.js';
import DragListener from '../../scenery/js/listeners/DragListener.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import Node from '../../scenery/js/nodes/Node.js';
import Rectangle from '../../scenery/js/nodes/Rectangle.js';
import Color from '../../scenery/js/util/Color.js';
import Tandem from '../../tandem/js/Tandem.js';
import griddle from './griddle.js';
import ScrollingChartNode from './ScrollingChartNode.js';

// constants
const WIDTH_PROPORTION = 0.013; // empirically determined
const CURSOR_FILL_COLOR = new Color( 50, 50, 200, 0.2 );
const CURSOR_STROKE_COLOR = Color.DARK_GRAY;
const ARROW_CUE_FILL_COLOR = new Color( 180, 180, 230 );
const ARROW_CUE_STROKE_COLOR = Color.DARK_GRAY;

class XYCursorPlot extends ScrollingChartNode {

  /**
   * @param {Object} [options]
   */
  constructor( options ) {
    options = merge( {

      // options passed on to the cursor, see ChartCursor
      cursorOptions: null,

      graphPanelOptions: {

        // so that the cursor is draggable
        pickable: true
      },

      // phet-io
      tandem: Tandem.OPTIONAL
    }, options );

    super( options );

    // @private {boolean|null} - if set with setCursorVisible, then this will indicate visibility of the cursor
    this._cursorVisibleOverride = null;

    // @private {number} - value for the cursor, determines cursor positioning
    this.cursorValue = 0;

    // @private - minimum and maximum recorded value, required by the cursor to limit dragging
    this.minRecordedXValue = 0;
    this.maxRecordedXValue = 0;

    // @private Map.<DynamicSeries,function> Keep track of the listener for each series, so the listener can be removed
    // when a series is removed.
    this.dynamicSeriesListenerMap = new Map();

    // @protected (read-only) {DynamicSeries[]}
    this.dynamicSeriesList = [];

    // @private {ChartCursor} - draggable Node that shows the cursor value
    this.chartCursor = new ChartCursor( this, this.modelViewTransformProperty, options.cursorOptions );
    this.graphPanel.addChild( this.chartCursor );

    // initialize position and visibility of the cursor
    this.updateCursor();
  }

  /**
   * Adds a DynamicSeries to the XYCursorPlot.
   * @override
   * @public
   *
   * @param {DynamicSeries} dynamicSeries
   */
  addDynamicSeries( dynamicSeries ) {
    super.addDynamicSeries( dynamicSeries );

    this.dynamicSeriesList.push( dynamicSeries );

    // when a point is added, update the min and max recorded values
    const dynamicSeriesListener = () => {

      // update the
      this.updateMinMaxRecordedValues();

      // if all data has been removed from the plot, update cursor visibility
      this.updateCursor();
    };

    // save to map so that listener can be found again for disposal
    this.dynamicSeriesListenerMap.set( dynamicSeries, dynamicSeriesListener );
    dynamicSeries.addDynamicSeriesListener( dynamicSeriesListener );
  }

  /**
   * Removes a DynamicSeries from the plot and disposes of its listener.
   * @override
   * @public
   *
   * @param {DynamicSeries} dynamicSeries - to remove
   */
  removeDynamicSeries( dynamicSeries ) {
    super.removeDynamicSeries( dynamicSeries );

    const seriesIndex = this.dynamicSeriesList.indexOf( dynamicSeries );
    this.dynamicSeriesList.splice( seriesIndex, 1 );

    dynamicSeries.removeDynamicSeriesListener( this.dynamicSeriesListenerMap.get( dynamicSeries ) );
    this.dynamicSeriesListenerMap.delete( dynamicSeries );
  }

  /**
   * Sets the cursor value. The value of the cursor is constrained to be within plot bounds.
   * @public
   *
   * @param {number} value
   */
  setCursorValue( value ) {
    const modelViewTransform = this.modelViewTransformProperty.get();
    const minX = modelViewTransform.viewToModelX( 0 );
    const maxX = modelViewTransform.viewToModelX( this.plotWidth + this.chartCursor.width / 2 );
    this.cursorValue = Utils.clamp( value, minX, maxX );
    this.updateCursor();
  }

  /**
   * Gets the value currently under the cursor.
   * @public
   *
   * @returns {number}
   */
  getCursorValue() {
    return this.cursorValue;
  }

  /**
   * Resets the cursor.
   * @public
   */
  resetCursor() {
    this.chartCursor.reset();
  }

  /**
   * Overrides the default behavior for setting cursor visibility. If set to null, cursor visibility will behave as
   * described in updateCursorVisibility. Otherwise, visibility will equal the boolean value set here.
   * @param {boolean|null} visible
   * @public
   */
  setCursorVisibleOverride( visible ) {
    assert && assert( typeof visible === 'boolean' || visible === null, 'visible must be boolean or null' );
    this._cursorVisibleOverride = visible;
    this.updateCursorVisibility();
  }

  /**
   * Updates the cursor visibility and position.
   * @private
   */
  updateCursor() {
    this.updateCursorVisibility();
    if ( this.chartCursor.isVisible() ) {
      this.updateCursorPosition();
    }
  }

  /**
   * Updates the cursor position.
   * @private
   */
  updateCursorPosition() {
    this.moveCursorToValue( this.cursorValue );
  }

  /**
   * Updates the cursor visibility. The cursor should be visible any time the cursor value is within
   * the recorded value range.
   * @private
   */
  updateCursorVisibility() {

    const wasVisible = this.chartCursor.visible;
    if ( typeof this._cursorVisibleOverride === 'boolean' ) {
      this.chartCursor.setVisible( this._cursorVisibleOverride );
    }
    else {

      const maxX = this.modelViewTransformProperty.get().viewToModelX( this.plotWidth + this.chartCursor.width / 2 );
      const minX = this.modelViewTransformProperty.get().viewToModelX( 0 );

      const isCurrentValueOnChart = ( this.cursorValue >= minX ) && ( this.cursorValue <= maxX );
      const dataExists = this.getDataExists();
      const chartCursorVisible = isCurrentValueOnChart && dataExists;

      this.chartCursor.setVisible( chartCursorVisible );
    }

    // if the cursor just became invisible, interrupt any active dragging
    if ( !this.chartCursor.visible && wasVisible ) {
      this.chartCursor.interruptDrag();
    }
  }

  /**
   * Returns true if any data is attached to this plot.
   * @returns {boolean}
   * @public
   */
  getDataExists() {
    let dataExists = false;
    this.dynamicSeriesListenerMap.forEach( ( listener, dataSeries, map ) => {
      dataExists = dataSeries.hasData();

      // break early
      if ( dataExists ) {
        return true;
      }
    } );

    return dataExists;
  }

  /**
   * Moves the cursor to the specified value.
   * @private
   *
   * @param {number} value
   */
  moveCursorToValue( value ) {
    const viewPosition = this.modelViewTransformProperty.get().modelToViewX( value );

    // keep the cursor within the grid bounds
    this.chartCursor.centerX = Utils.clamp( viewPosition, 0, this.plotWidth );
    this.chartCursor.centerY = this.gridNode.centerY;
  }

  /**
   * From the existing data, update the min and max recorded X values from all of the dynamicSeries of this plot,
   * so that the cursor can be limited to the recorded data.
   * @private
   */
  updateMinMaxRecordedValues() {
    let minValue = Number.POSITIVE_INFINITY;
    let maxValue = Number.NEGATIVE_INFINITY;
    this.dynamicSeriesList.forEach( dynamicSeries => {
      if ( dynamicSeries.getLength() > 0 ) {
        const seriesMinValue = dynamicSeries.getDataPoint( 0 ).x;
        const seriesMaxValue = dynamicSeries.getDataPoint( dynamicSeries.getLength() - 1 ).x;
        if ( seriesMinValue < minValue ) {
          minValue = seriesMinValue;
        }
        if ( seriesMaxValue > maxValue ) {
          maxValue = seriesMaxValue;
        }
      }
    } );

    this.minRecordedXValue = minValue;
    this.maxRecordedXValue = maxValue;
  }
}

/**
 * Rectangular cursor that indicates a current or selected value on the chart.
 */
class ChartCursor extends Rectangle {

  /**
   * @param {ScrollingChartNode} plot
   * @param {Property.<ModelViewTransform2>} modelViewTransformProperty
   * @param {Object} [options]
   */
  constructor( plot, modelViewTransformProperty, options ) {

    options = merge( {
      startDrag: () => {},
      endDrag: () => {},
      drag: () => {},

      // {boolean} - if true, a double headed arrow will be shown to indicate
      // that the cursor is draggable - becomes invisible after first
      // drag
      includeDragCue: false,

      // phet-io
      tandem: Tandem.OPTIONAL
    }, options );

    const width = plot.plotWidth * WIDTH_PROPORTION;
    const height = plot.plotHeight;

    // Set the shape. Origin is at the center top of the rectangle.
    super( 0, -height, width, height, 0, 0, {
      cursor: 'ew-resize',
      fill: CURSOR_FILL_COLOR,
      stroke: CURSOR_STROKE_COLOR,
      lineWidth: 0.4,
      lineDash: [ 4, 4 ]
    } );

    this.plot = plot;
    this.modelViewTransformProperty = modelViewTransformProperty;

    // Make it easier to grab this cursor by giving it expanded mouse and touch areas.
    this.mouseArea = this.localBounds.dilatedX( 12 );
    this.touchArea = this.localBounds.dilatedX( 12 );

    this.includeDragCue = options.includeDragCue;

    if ( this.includeDragCue ) {

      // @private - indicates to the user that the cursor is draggable, only created
      // and added if necessary
      this.dragCueArrowNode = new ArrowNode( -width * 2, 0, width * 2, 0, {
        doubleHead: true,
        headWidth: 12,
        headHeight: 10,
        fill: ARROW_CUE_FILL_COLOR,
        stroke: ARROW_CUE_STROKE_COLOR,
        center: this.center.plusXY( 0, height * 0.4 )
      } );

      this.addChild( this.dragCueArrowNode );
    }

    // Add the indentations that are intended to convey the idea of "gripability".
    const grippyNode = new Node();
    const indentSpacing = height * 0.05;
    for ( let i = 0; i < 3; i++ ) {
      const indentNode = new GrippyIndentNode( width / 2, CURSOR_FILL_COLOR );
      indentNode.top = i * ( indentNode.height + indentSpacing );
      grippyNode.addChild( indentNode );
    }
    grippyNode.center = this.center;
    this.addChild( grippyNode );

    // @private - so that we can interrupt the DragListener if necessary
    this.dragListener = new DragListener( {
      start: ( event, listener ) => {
        assert && assert( this.plot.getDataExists(), 'data should exist for the cursor to be draggable' );
        options.startDrag();
      },
      drag: ( event, listener ) => {
        const parentX = listener.parentPoint.x;
        let newValue = this.modelViewTransformProperty.get().viewToModelX( parentX );

        // limit cursor to the domain of recorded values
        newValue = Utils.clamp( newValue, this.plot.minRecordedXValue, this.plot.maxRecordedXValue );
        this.plot.setCursorValue( newValue );

        options.drag();
      },
      end: ( event, listener ) => {
        options.endDrag();

        // no need to show arrow after user successfully drags the cursor
        if ( this.includeDragCue ) {
          this.dragCueArrowNode.visible = false;
        }
      },
      tandem: options.tandem.createTandem( 'dragListener' )
    } );
    this.addInputListener( this.dragListener );
  }

  /**
   * Interrupt dragging of the cursor, useful when ChartCursor visibility changes.
   * @public
   */
  interruptDrag() {
    this.dragListener.interrupt();
  }

  /**
   * Reset the ChartCursor to its initial state. Note that this does not modify data
   * or the cursor position (cursorValue), only aspects of the view for the cursor itself.
   * @public
   */
  reset() {
    if ( this.includeDragCue ) {
      this.dragCueArrowNode.visible = true;
    }
  }
}

/**
 * This node is meant to portray a small round indentation on a surface.  This is a modern user interface paradigm that
 * is intended to convey the concept of "gripability" (sp?), i.e. something that the user can click on and subsequently
 * grab.  This is meant to look somewhat 3D, much like etched borders do.
 */
class GrippyIndentNode extends Circle {

  /**
   * @param {number} diameter
   * @param {Color} baseColor
   * @param {Object} [options]
   */
  constructor( diameter, baseColor, options ) {

    options = merge( {
      lineWidth: 0.5
    }, options );

    const baseDarkerColor = baseColor.darkerColor( 0.9 );
    const translucentDarkerColor = new Color( baseDarkerColor.getRed(), baseDarkerColor.getGreen(),
      baseDarkerColor.getBlue(), baseColor.getAlpha() );
    const baseLighterColor = baseColor.brighterColor( 0.9 );
    const translucentBrighterColor = new Color( baseLighterColor.getRed(), baseLighterColor.getGreen(),
      baseLighterColor.getBlue(), baseColor.getAlpha() );
    const radius = diameter / 2 - options.lineWidth;

    super( radius, {
      fill: translucentDarkerColor,
      stroke: translucentBrighterColor,
      lineWidth: options.lineWidth
    } );
  }
}

griddle.register( 'XYCursorPlot', XYCursorPlot );
export default XYCursorPlot;<|endoftext|>


<commit_before>/*
 * Create a Rekit project. Boilerplates are provided by Rekit plugins.
 *
 * To create a Rekit project:
 *  - If options.source is a local folder, then copy content from it (excepts node_modules and .git folders),
 *    if it's a git url, clone it.
 *  - Otherwise, looks for project type registry from https://github.com/supnate/rekit-registry/appTypes.json,
 *    clone the repo to the project folder.
 *  - Execute postCreate.js
 *
*/

const path = require('path');
const https = require('https');
const fs = require('fs-extra');
const download = require('download-git-repo');

function create(options) {
  console.log('Creating app: ', options);

  if (!options.status)
    options.status = (code, msg) => {
      console.log(msg);
    };
  if (!options.type) options.type = 'rekit-react';

  const prjDir = path.join(process.cwd(), options.name);
  return new Promise(async (resolve, reject) => {
    try {
      if (fs.existsSync(prjDir)) {
        reject('FOLDER_EXISTS');
        return;
      }
      fs.mkdirSync(prjDir);
      let gitRepo;
      if (options.source) {
        if (/^https?:/.test(options.source)) {
          // It's a git repo
          gitRepo = options.source;
        } else {
          // It's a local folder
          const srcDir = path.isAbsolute(options.source) ? options.source : path.join(process.cwd(), options.source);
          options.status('CREATE_APP_COPY_FILES', `Copy files from ${srcDir}...`);
          await fs.copy(srcDir, prjDir, {
            filter: src => !/\/(\.git|node_modules)\//.test(src),
          });
        }
      } else if (options.type) {
        // Get gitRepo
        options.status('QUERY_APP_TYPES_GIT_REPO', `Looking for the git repo for app type ${options.type}...`);
        const appTypes = await getAppTypes();
        if (!appTypes[options.type]) reject('APP_TYPE_NOT_SUPPORTED');
        gitRepo = appTypes[options.type].repo;
      } else {
        await fs.remove(prjDir);
        reject('NO_SOURCE_OR_APP_TYPE');
      }

      if (gitRepo) {
        options.status('CLONE_PROJECT', `Cloning project from ${gitRepo}...`);
        await cloneRepo(gitRepo, prjDir);
      }

      postCreate(prjDir, options);
      console.log('App creation success.');
    } catch (err) {
      reject(err);
    }
  });
}

function getAppTypes() {
  return new Promise((resolve, reject) => {
    https
      .get('https://raw.githubusercontent.com/supnate/rekit-registry/master/appTypes.json', resp => {
        let data = '';

        // A chunk of data has been recieved.
        resp.on('data', chunk => {
          data += chunk;
        });

        // The whole response has been received. Print out the result.
        resp.on('end', () => {
          const appTypes = JSON.parse(data);
          resolve(appTypes);
        });
      })
      .on('error', err => {
        console.log('Failed to get app types: ', err);
        reject('GET_APP_TYPES_FAILED');
      });
  });
}

function cloneRepo(gitRepo, prjDir) {
  return new Promise((resolve, reject) => {
    const isDirect = /^https?:/.test(gitRepo);
    download(isDirect ? `direct:${gitRepo}` : gitRepo, prjDir, { clone: isDirect }, err => {
      if (err) {
        console.log('Failed to download the boilerplate. The project was not created. Please check and retry.');
        console.log(err);
        reject('CLONE_REPO_FAILED');
        return;
      }
      resolve();
    });
  });
}

function postCreate(prjDir, options) {
  options.status('POST_CREATE', 'Executing post create script...');
  const postCreateScript = path.join(prjDir, 'postCreate.js');
  if (fs.existsSync(postCreateScript)) {
    options.status('EXEC_POST_CREATE', 'Post creation...');
    require(postCreateScript)(options);
    fs.remove(postCreateScript);
  }
}

module.exports = create;
<commit_msg>Allow custom position for new project.<commit_after>/*
 * Create a Rekit project. Boilerplates are provided by Rekit plugins.
 *
 * To create a Rekit project:
 *  - If options.source is a local folder, then copy content from it (excepts node_modules and .git folders),
 *    if it's a git url, clone it.
 *  - Otherwise, looks for project type registry from https://github.com/supnate/rekit-registry/appTypes.json,
 *    clone the repo to the project folder.
 *  - Execute postCreate.js
 *
*/

const path = require('path');
const https = require('https');
const fs = require('fs-extra');
const download = require('download-git-repo');

function create(options) {
  console.log('Creating app: ', options);

  if (!options.status)
    options.status = (code, msg) => {
      console.log(msg);
    };
  if (!options.type) options.type = 'rekit-react';

  const prjDir = path.join(options.position || process.cwd(), options.name);
  return new Promise(async (resolve, reject) => {
    try {
      if (fs.existsSync(prjDir)) {
        reject('FOLDER_EXISTS');
        return;
      }
      fs.mkdirSync(prjDir);
      let gitRepo;
      if (options.source) {
        if (/^https?:/.test(options.source)) {
          // It's a git repo
          gitRepo = options.source;
        } else {
          // It's a local folder
          const srcDir = path.isAbsolute(options.source) ? options.source : path.join(process.cwd(), options.source);
          options.status('CREATE_APP_COPY_FILES', `Copy files from ${srcDir}...`);
          await fs.copy(srcDir, prjDir, {
            filter: src => !/\/(\.git|node_modules)\//.test(src),
          });
        }
      } else if (options.type) {
        // Get gitRepo
        options.status('QUERY_APP_TYPES_GIT_REPO', `Looking for the git repo for app type ${options.type}...`);
        const appTypes = await getAppTypes();
        if (!appTypes[options.type]) reject('APP_TYPE_NOT_SUPPORTED');
        gitRepo = appTypes[options.type].repo;
      } else {
        await fs.remove(prjDir);
        reject('NO_SOURCE_OR_APP_TYPE');
      }

      if (gitRepo) {
        options.status('CLONE_PROJECT', `Cloning project from ${gitRepo}...`);
        await cloneRepo(gitRepo, prjDir);
      }

      postCreate(prjDir, options);
      console.log('App creation success.');
    } catch (err) {
      reject(err);
    }
  });
}

function getAppTypes() {
  return new Promise((resolve, reject) => {
    https
      .get('https://raw.githubusercontent.com/supnate/rekit-registry/master/appTypes.json', resp => {
        let data = '';

        // A chunk of data has been recieved.
        resp.on('data', chunk => {
          data += chunk;
        });

        // The whole response has been received. Print out the result.
        resp.on('end', () => {
          const appTypes = JSON.parse(data);
          resolve(appTypes);
        });
      })
      .on('error', err => {
        console.log('Failed to get app types: ', err);
        reject('GET_APP_TYPES_FAILED');
      });
  });
}

function cloneRepo(gitRepo, prjDir) {
  return new Promise((resolve, reject) => {
    const isDirect = /^https?:/.test(gitRepo);
    download(isDirect ? `direct:${gitRepo}` : gitRepo, prjDir, { clone: isDirect }, err => {
      if (err) {
        console.log('Failed to download the boilerplate. The project was not created. Please check and retry.');
        console.log(err);
        reject('CLONE_REPO_FAILED');
        return;
      }
      resolve();
    });
  });
}

function postCreate(prjDir, options) {
  options.status('POST_CREATE', 'Executing post create script...');
  const postCreateScript = path.join(prjDir, 'postCreate.js');
  if (fs.existsSync(postCreateScript)) {
    options.status('EXEC_POST_CREATE', 'Post creation...');
    require(postCreateScript)(options);
    fs.removeSync(postCreateScript);
  }
}

module.exports = create;
<|endoftext|>


<commit_before>function Entities(myda) {
  this.myda = myda;
}

Entities.prototype.request = function(eventName, data) {
  var myda = this.myda;
  return new Promise(function(resolve, reject) {
    myda.request('entities.create', {
      root: myda.root,
      path: path,
      fields: fields
    }, resolve, reject);
  });
};

Entities.prototype.create = function(entity, fields) {
  return this.request('entities.create', {
    root: this.myda.root,
    path: path,
    fields: fields
  });
};

Entities.prototype.get = function(path, options) {
  var data = {
    root: this.myda.root,
    path: path
  };
  return this.request('entities.data', data);
};

Entities.prototype.delete = function(path) {
  return this.request('entities.delete', {
    root: this.myda.root,
    path: path
  });
};

Entities.prototype.change = function(path, fields) {
  return this.request('entities.change', {
    root: this.myda.root,
    path: path,
    fields: fields
  });
};

Entities.prototype.subscribe = function(filter, events) {
  return this.request('entities.unsubscribe', {
    root: this.myda.root,
    path: filter,
    events: events
  });
};

Entities.prototype.unsubscribe = function(filter) {
  return this.request('entities.unsubscribe', {
    root: this.myda.root,
    path: filter
  });
};

Entities.prototype.on = function(eventName, callback) {
  this.myda.on('entities.' + eventName + '.res', callback);
};
<commit_msg>fix: Entities.request<commit_after>function Entities(myda) {
  this.myda = myda;
}

Entities.prototype.request = function(eventName, data) {
  return new Promise(function(resolve, reject) {
    myda.request(eventName, data, resolve, reject);
  });
};

Entities.prototype.create = function(entity, fields) {
  return this.request('entities.create', {
    root: this.myda.root,
    path: path,
    fields: fields
  });
};

Entities.prototype.get = function(path, options) {
  var data = {
    root: this.myda.root,
    path: path
  };
  return this.request('entities.data', data);
};

Entities.prototype.delete = function(path) {
  return this.request('entities.delete', {
    root: this.myda.root,
    path: path
  });
};

Entities.prototype.change = function(path, fields) {
  return this.request('entities.change', {
    root: this.myda.root,
    path: path,
    fields: fields
  });
};

Entities.prototype.subscribe = function(filter, events) {
  return this.request('entities.unsubscribe', {
    root: this.myda.root,
    path: filter,
    events: events
  });
};

Entities.prototype.unsubscribe = function(filter) {
  return this.request('entities.unsubscribe', {
    root: this.myda.root,
    path: filter
  });
};

Entities.prototype.on = function(eventName, callback) {
  this.myda.on('entities.' + eventName + '.res', callback);
};
<|endoftext|>


<commit_before>export const queryDomains = () => ({
    url: 'domains',
    method: 'get'
});

export const getDomain = (domainID) => ({
    url: `domains/${domainID}`,
    method: 'get'
});

export const queryDomainAddresses = (domainID) => ({
    url: `domains/${domainID}/addresses`,
    method: 'get'
});

export const queryAvailableDomains = () => ({
    url: 'domains/available',
    method: 'get'
});

export const queryPremiumDomains = () => ({
    url: 'domains/premium',
    method: 'get'
});

export const addDomain = (Name) => ({
    url: 'domains',
    method: 'post',
    data: { Name }
});

export const updateCatchAll = (domainID, AddressID) => ({
    url: `domains/${domainID}/catchall`,
    method: 'put',
    data: { AddressID }
});

export const deleteDomain = (domainID) => ({
    url: `domains/${domainID}`,
    method: 'delete'
});
<commit_msg>New route to generate DKIM key<commit_after>export const queryDomains = () => ({
    url: 'domains',
    method: 'get'
});

export const getDomain = (domainID) => ({
    url: `domains/${domainID}`,
    method: 'get'
});

export const queryDomainAddresses = (domainID) => ({
    url: `domains/${domainID}/addresses`,
    method: 'get'
});

export const queryAvailableDomains = () => ({
    url: 'domains/available',
    method: 'get'
});

export const queryPremiumDomains = () => ({
    url: 'domains/premium',
    method: 'get'
});

export const addDomain = (Name) => ({
    url: 'domains',
    method: 'post',
    data: { Name }
});

export const updateCatchAll = (domainID, AddressID) => ({
    url: `domains/${domainID}/catchall`,
    method: 'put',
    data: { AddressID }
});

export const deleteDomain = (domainID) => ({
    url: `domains/${domainID}`,
    method: 'delete'
});

export const generateKey = (domainID, Algorithm) => ({
    url: `domains/${domainID}/keys`,
    method: 'post',
    data: { Algorithm }
});
<|endoftext|>


<commit_before>'use strict';

const os = require('os'),
  platform = os.platform(),
  Promise = require('bluebird'),
  child_process = Promise.promisifyAll(require('child_process')),
  fs = Promise.promisifyAll(require('fs')),
  mime = Promise.promisifyAll(require('mime')),
  path = require('path'),
  isXml = require('is-xml'),
  firstline = require('firstline');

const sisypheXml = {};

sisypheXml.doTheJob = function (data, next) {

  if(data.mimetype){
    return next(null,data)
  }

  // Permet d'identifier les fichiers .nxml (BMJ) et .Meta (Springer) comme mimetype XML
  mime.define({
    'application/xml': ["nxml", "meta", "xlink_v03", "prime_v03", "plusxml_v02", "plusprime_v02", "info_V03", "citation_v03", "aux_v03"]
  });

  var datapath = data.path;
  let mimetype = mime.lookup(data.path);

  firstline(datapath).then(line1=>{
    if(isXml(line1)){
      //console.log(data.name, 'firstline', 'application/xml')
      return 'application/xml'
    }
    //console.log(data.name, 'firstline', 'application/octet-stream');
    return 'application/octet-stream'
  }).catch(err=>{
    // Err loading first line
    //console.log(data.name, 'CatchFirstline', 'application/octet-stream');
    return 'application/octet-stream'
  })
  // This is in test building ...
  .then(mimeResult=>{
    //will detect wia filesystem Info
    if(mimeResult != 'application/xml'){
      switch(platform){
        //Windows
        case 'win32':
          break;
        //OSX & Linux
        case 'darwin':
        case 'linux':
        default :
          return child_process.execAsync(`file -b --mime-type "${datapath}"`)
          .then((stdout, stderr)=>{
            if(stderr){
              // add corrupt message here to data
              return mimeResult;
            }
            //console.log(stdout.replace(/\n|\r/g, ''));
            return stdout.replace(/\n|\r/g, '');
          }).catch(err=>{
            console.log('errFile',err)
            return mimeResult
          })
          break;
      }
    }
    return mimeResult;
  })
  .then(mimeResult=>{
    // Could now check all data of file
    if(mimeResult === 'text/plain'){
      return fs.readFileAsync(datapath,'utf8').then(dataFile=>{
        let nbOfXmlBalises = (dataFile.match(/<[\s\S]*?>/g) || []).length,
            seemsXml = isXml(dataFile);
        if(seemsXml || nbOfXmlBalises > 10){
          return 'application/xml'
        }
        //console.log(data.name, 'fulldata', mimeResult);
        return mimeResult
      }).catch(err=>{
        console.log(data.name, 'CatchFulldata', mimeResult, err);
        // Err loading full data
        return mimeResult;
      })
    }
    return mimeResult;
  })
  .then(filetype=>{
    console.log('filetype',data.name, filetype);
    data.mimetype = filetype;
    return next(null,data)
  })
};

module.exports = sisypheXml;<commit_msg>Deleting some console.log()<commit_after>'use strict';

const os = require('os'),
  platform = os.platform(),
  Promise = require('bluebird'),
  child_process = Promise.promisifyAll(require('child_process')),
  fs = Promise.promisifyAll(require('fs')),
  mime = Promise.promisifyAll(require('mime')),
  path = require('path'),
  isXml = require('is-xml'),
  firstline = require('firstline');

const sisypheXml = {};

sisypheXml.doTheJob = function (data, next) {

  if (data.mimetype) {
    return next(null, data)
  }

  // Permet d'identifier les fichiers .nxml (BMJ) et .Meta (Springer) comme mimetype XML
  mime.define({
    'application/xml': ["nxml", "meta", "xlink_v03", "prime_v03", "plusxml_v02", "plusprime_v02", "info_V03", "citation_v03", "aux_v03"]
  });

  const datapath = data.path;
  let mimetype = mime.lookup(data.path);

  firstline(datapath).then((line1) => {
    if (isXml(line1)) {
      //console.log(data.name, 'firstline', 'application/xml')
      return 'application/xml'
    }
    //console.log(data.name, 'firstline', 'application/octet-stream');
    return 'application/octet-stream'
  }).catch(() => {
    // Err loading first line
    //console.log(data.name, 'CatchFirstline', 'application/octet-stream');
    return 'application/octet-stream'
  })
  // This is in test building ...
    .then(mimeResult => {
      //will detect wia filesystem Info
      if (mimeResult != 'application/xml') {
        switch (platform) {
          //Windows
          case 'win32':
            break;
          //OSX & Linux
          case 'darwin':
          case 'linux':
          default :
            return child_process.execAsync(`file -b --mime-type "${datapath}"`)
              .then((stdout, stderr) => {
                if (stderr) {
                  // add corrupt message here to data
                  return mimeResult;
                }
                //console.log(stdout.replace(/\n|\r/g, ''));
                return stdout.replace(/\n|\r/g, '');
              }).catch(() => {
                return mimeResult
              });
            break;
        }
      }
      return mimeResult;
    })
    .then(mimeResult => {
      // Could now check all data of file
      if (mimeResult === 'text/plain') {
        return fs.readFileAsync(datapath, 'utf8').then(dataFile => {
          let nbOfXmlBalises = (dataFile.match(/<[\s\S]*?>/g) || []).length,
            seemsXml = isXml(dataFile);
          if (seemsXml || nbOfXmlBalises > 10) {
            return 'application/xml'
          }
          //console.log(data.name, 'fulldata', mimeResult);
          return mimeResult
        }).catch(err => {
          console.log(data.name, 'CatchFulldata', mimeResult, err);
          // Err loading full data
          return mimeResult;
        })
      }
      return mimeResult;
    })
    .then(filetype => {
      data.mimetype = filetype;
      return next(null, data)
    })
};

module.exports = sisypheXml;<|endoftext|>


<commit_before>////
//// Setup
////

// dependency for the count of tests running/passed/failed, etc. drives
// the navbar and the like.
var countDep = new Deps.Dependency;
// things that change on countDep
var running = true;
var totalCount = 0;
var passedCount = 0;
var failedCount = 0;
var failedTests = [];

// Dependency for when a new top level group is added. Each group and
// each test have their own dependency objects.
var topLevelGroupsDep = new Deps.Dependency;

// An array of top-level groups.
//
// Each group is an object with:
// - name: string
// - path: array of strings (names of parent groups)
// - parent: parent group object (back reference)
// - dep: Deps.Dependency object for this group. fires when new tests added.
// - groups: list of sub-groups
// - tests: list of tests in this group
//
// Each test is an object with:
// - name: string
// - parent: parent group object (back reference)
// - server: boolean
// - fullName: string
// - dep: Deps.Dependency object for this test. fires when the test completes.
var resultTree = [];

Session.setDefault("groupPath", ["tinytest"]);
Session.set("rerunScheduled", false);

Meteor.startup(function () {
  Deps.flush();
  Meteor._runTestsEverywhere(reportResults, function () {
    running = false;
    Meteor.onTestsComplete && Meteor.onTestsComplete();
    countDep.changed();
    Deps.flush();

    Meteor.default_connection._unsubscribeAll();
  }, Session.get("groupPath"));

});

////
//// Take incoming results and drive resultsTree
////

// report a series of events in a single test, or just the existence of
// that test if no events. this is the entry point for test results to
// this module.
var reportResults = function(results) {
  var test = _findTestForResults(results);

  if (_.isArray(results.events)) {
    // append events, if present
    Array.prototype.push.apply((test.events || (test.events = [])),
                               results.events);
    // sort and de-duplicate, based on sequence number
    test.events.sort(function (a, b) {
      return a.sequence - b.sequence;
    });
    var out = [];
    _.each(test.events, function (e) {
      if (out.length === 0 || out[out.length - 1].sequence !== e.sequence)
        out.push(e);
    });
    test.events = out;
  }
  var status = _testStatus(test);
  if (status === "failed") {
    failedCount++;
    // Expand a failed test (but only set this if the user hasn't clicked on the
    // test name yet).
    if (test.expanded === undefined)
      test.expanded = true;
    if (!_.contains(failedTests, test.fullName))
      failedTests.push(test.fullName);

    countDep.changed();
    test.dep.changed();
  } else if (status === "succeeded") {
    passedCount++;
    countDep.changed();
    test.dep.changed();
  } else if (test.expanded) {
    // re-render the test if new results come in and the test is
    // currently expanded.
    test.dep.changed();
  }
};

// forget all of the events for a particular test
var forgetEvents = function (results) {
  var test = _findTestForResults(results);
  var status = _testStatus(test);
  if (status === "failed") {
    failedCount--;
    countDep.changed();
  } else if (status === "succeeded") {
    passedCount--;
    countDep.changed();
  }
  delete test.events;
  test.dep.changed();
};

// given a 'results' as delivered via reportResults, find the
// corresponding leaf object in resultTree, creating one if it doesn't
// exist. it will be an object with attributes 'name', 'parent', and
// possibly 'events'.
var _findTestForResults = function (results) {
  var groupPath = results.groupPath; // array
  if ((! _.isArray(groupPath)) || (groupPath.length < 1)) {
    throw new Error("Test must be part of a group");
  }

  var group;
  var i = 0;
  _.each(groupPath, function(gname) {
    var array = (group ? (group.groups || (group.groups = []))
                 : resultTree);
    var newGroup = _.find(array, function(g) { return g.name === gname; });
    if (! newGroup) {
      newGroup = {
        name: gname,
        parent: (group || null),
        path: groupPath.slice(0, i+1),
        dep: new Deps.Dependency
      }; // create group
      array.push(newGroup);

      if (group)
        group.dep.changed();
      else
        topLevelGroupsDep.changed();
    }
    group = newGroup;
    i++;
  });

  var testName = results.test;
  var server = !!results.server;
  var test = _.find(group.tests || (group.tests = []),
                    function(t) { return t.name === testName &&
                                  t.server === server; });
  if (! test) {
    // create test
    var nameParts = _.clone(groupPath);
    nameParts.push(testName);
    var fullName = nameParts.join(' - ');
    test = {
      name: testName,
      parent: group,
      server: server,
      fullName: fullName,
      dep: new Deps.Dependency
    };
    group.tests.push(test);
    group.dep.changed();
    totalCount++;
    countDep.changed();
  }

  return test;
};

////
//// Helpers on test objects
////

var _testTime = function(t) {
  if (t.events && t.events.length > 0) {
    var lastEvent = _.last(t.events);
    if (lastEvent.type === "finish") {
      if ((typeof lastEvent.timeMs) === "number") {
        return lastEvent.timeMs;
      }
    }
  }
  return null;
};

var _testStatus = function(t) {
  var events = t.events || [];
  if (_.find(events, function(x) { return x.type === "exception"; })) {
    // "exception" should be last event, except race conditions on the
    // server can make this not the case.  Technically we can't tell
    // if the test is still running at this point, but it can only
    // result in FAIL.
    return "failed";
  } else if (events.length == 0 || (_.last(events).type != "finish")) {
    return "running";
  } else if (_.any(events, function(e) {
    return e.type == "fail" || e.type == "exception"; })) {
    return "failed";
  } else {
    return "succeeded";
  }
};

////
//// Templates
////

//// Template - navBars

Template.navBars({
  running: function() {
    countDep.depend();
    return running;
  },
  passed: function() {
    countDep.depend();
    return failedCount === 0;
  },
  total_test_time: function() {
    countDep.depend();

    // walk whole tree to get all tests
    var walk = function (groups) {
      var total = 0;

      _.each(groups || [], function (group) {
        _.each(group.tests || [], function (t) {
          total += _testTime(t);
        });

        total += walk(group.groups);
      });

      return total;
    };

    return walk(resultTree);
  }
});

//// Template - progressBar

Template.progressBar({
  running: function () {
    countDep.depend();
    return running;
  },
  percentPass: function () {
    countDep.depend();
    if (totalCount === 0)
      return 0;
    return 100*passedCount/totalCount;
  },
  totalCount: function () {
    countDep.depend();
    return totalCount;
  },
  passedCount: function () {
    countDep.depend();
    return passedCount;
  },
  percentFail: function () {
    countDep.depend();
    if (totalCount === 0)
      return 0;
    return 100*failedCount/totalCount;
  },
  anyFail: function () {
    countDep.depend();
    return failedCount > 0;
  },
  barOuterClass: function () {
    return this.running() ? 'progress-striped' : '';
  },
  barInnerClass: function () {
    return (this.anyFail() ?
            'bar-warning' : 'bar-success');
  }
});

//// Template - groupNav

Template.groupNav({
  groupPaths: function () {
    var groupPath = Session.get("groupPath");
    var ret = [];
    for (var i = 1; i <= groupPath.length; i++) {
      ret.push({path: groupPath.slice(0,i), name: groupPath[i-1]});
    }
    return ret;
  },
  rerunScheduled: function () {
    return Session.get("rerunScheduled");
  },
  changeToPath: function (path) {
    Session.set("groupPath", path);
    Session.set("rerunScheduled", true);
    // pretend there's just been a hot code push
    // so we run the tests completely fresh.
    Meteor._reload.reload();
  }
});

// XXXX EVENTS
/*Template.groupNav.events({
  "click .group": function () {
    changeToPath(this.path);
  },
  "click .rerun": function () {
    Session.set("rerunScheduled", true);
    Meteor._reload.reload();
  }
});*/

//// Template - failedTests

Template.failedTests({
  failedTests: function() {
    countDep.depend();
    return failedTests;
  }
});

//// Template - testTable

Template.testTable({
  data: function() {
    topLevelGroupsDep.depend();
    return resultTree;
  }
});

//// Template - test_group

Template.test_group({
  groupDep: function () {
    // this template just establishes a dependency. It doesn't actually
    // render anything.
    this.dep.depend();
    return "";
  }
});

// XXXX EVENTS
/*
Template.test_group.events({
  "click .groupname": function () {
    changeToPath(this.path);
  }
});
*/

//// Template - test

Template.test({
  testDep: function () {
    // this template just establishes a dependency. It doesn't actually
    // render anything.
    this.dep.depend();
    return "";
  },

  test_status_display: function() {
    var status = _testStatus(this);
    if (status == "failed") {
      return "FAIL";
    } else if (status == "succeeded") {
      return "PASS";
    } else {
      return "waiting...";
    }
  },

  test_time_display: function() {
    var time = _testTime(this);
    return (typeof time === "number") ? time + " ms" : "";
  },

  test_class: function() {
    var events = this.events || [];
    var classes = [_testStatus(this)];

    if (this.expanded) {
      classes.push("expanded");
    } else {
      classes.push("collapsed");
    }

    return classes.join(' ');
  },

  eventsArray: function() {
    var events = _.filter(this.events, function(e) {
      return e.type != "finish";
    });

    var partitionBy = function(seq, func) {
      var result = [];
      var lastValue = {};
      _.each(seq, function(x) {
        var newValue = func(x);
        if (newValue === lastValue) {
          result[result.length-1].push(x);
        } else {
          lastValue = newValue;
          result.push([x]);
        }
      });
      return result;
    };

    var dupLists = partitionBy(
      _.map(events, function(e) {
        // XXX XXX We need something better than stringify!
        // stringify([undefined]) === "[null]"
        e = _.clone(e);
        delete e.sequence;
        return {obj: e, str: JSON.stringify(e)};
      }), function(x) { return x.str; });

    return _.map(dupLists, function(L) {
      var obj = L[0].obj;
      return (L.length > 1) ? _.extend({times: L.length}, obj) : obj;
    });
  }
});

// XXXX EVENTS
/*Template.test.events({
  'click .testname': function() {
    this.expanded = ! this.expanded;
    this.dep.changed();
  }
});*/

//// Template - event

// XXXX EVENTS
/*
Template.event.events({
  'click .debug': function () {
    // the way we manage groupPath, shortName, cookies, etc, is really
    // messy. needs to be aggressively refactored.
    forgetEvents({groupPath: this.cookie.groupPath,
                  test: this.cookie.shortName});
    Meteor._debugTest(this.cookie, reportResults);
  }
});*/

Template.event({
  get_details: function() {

    var prepare = function(details) {
      return _.compact(_.map(details, function(val, key) {

        // You can end up with a an undefined value, e.g. using
        // isNull without providing a message attribute: isNull(1).
        // No need to display those.
        if (!_.isUndefined(val)) {
          return {
            key: key,
            val: val
          };
        } else {
          return undefined;
        }
      }));
    };

    var details = this.details;

    if (! details) {
      return null;
    } else {

      var type = details.type;
      var stack = details.stack;

      details = _.clone(details);
      delete details.type;
      delete details.stack;

      return {
        type: type,
        stack: stack,
        details: prepare(details)
      };
    }
  },

  is_debuggable: function() {
    return !!this.cookie;
  }
});
<commit_msg>start porting driver.js<commit_after>////
//// Setup
////

// dependency for the count of tests running/passed/failed, etc. drives
// the navbar and the like.
var countDep = new Deps.Dependency;
// things that change on countDep
var running = true;
var totalCount = 0;
var passedCount = 0;
var failedCount = 0;
var failedTests = [];

// Dependency for when a new top level group is added. Each group and
// each test have their own dependency objects.
var topLevelGroupsDep = new Deps.Dependency;

// An array of top-level groups.
//
// Each group is an object with:
// - name: string
// - path: array of strings (names of parent groups)
// - parent: parent group object (back reference)
// - dep: Deps.Dependency object for this group. fires when new tests added.
// - groups: list of sub-groups
// - tests: list of tests in this group
//
// Each test is an object with:
// - name: string
// - parent: parent group object (back reference)
// - server: boolean
// - fullName: string
// - dep: Deps.Dependency object for this test. fires when the test completes.
var resultTree = [];

Session.setDefault("groupPath", ["tinytest"]);
Session.set("rerunScheduled", false);

Meteor.startup(function () {
  Deps.flush();
  Meteor._runTestsEverywhere(reportResults, function () {
    running = false;
    Meteor.onTestsComplete && Meteor.onTestsComplete();
    countDep.changed();
    Deps.flush();

    Meteor.default_connection._unsubscribeAll();
  }, Session.get("groupPath"));

});

////
//// Take incoming results and drive resultsTree
////

// report a series of events in a single test, or just the existence of
// that test if no events. this is the entry point for test results to
// this module.
var reportResults = function(results) {
  var test = _findTestForResults(results);

  if (_.isArray(results.events)) {
    // append events, if present
    Array.prototype.push.apply((test.events || (test.events = [])),
                               results.events);
    // sort and de-duplicate, based on sequence number
    test.events.sort(function (a, b) {
      return a.sequence - b.sequence;
    });
    var out = [];
    _.each(test.events, function (e) {
      if (out.length === 0 || out[out.length - 1].sequence !== e.sequence)
        out.push(e);
    });
    test.events = out;
  }
  var status = _testStatus(test);
  if (status === "failed") {
    failedCount++;
    // Expand a failed test (but only set this if the user hasn't clicked on the
    // test name yet).
    if (test.expanded === undefined)
      test.expanded = true;
    if (!_.contains(failedTests, test.fullName))
      failedTests.push(test.fullName);

    countDep.changed();
    test.dep.changed();
  } else if (status === "succeeded") {
    passedCount++;
    countDep.changed();
    test.dep.changed();
  } else if (test.expanded) {
    // re-render the test if new results come in and the test is
    // currently expanded.
    test.dep.changed();
  }
};

// forget all of the events for a particular test
var forgetEvents = function (results) {
  var test = _findTestForResults(results);
  var status = _testStatus(test);
  if (status === "failed") {
    failedCount--;
    countDep.changed();
  } else if (status === "succeeded") {
    passedCount--;
    countDep.changed();
  }
  delete test.events;
  test.dep.changed();
};

// given a 'results' as delivered via reportResults, find the
// corresponding leaf object in resultTree, creating one if it doesn't
// exist. it will be an object with attributes 'name', 'parent', and
// possibly 'events'.
var _findTestForResults = function (results) {
  var groupPath = results.groupPath; // array
  if ((! _.isArray(groupPath)) || (groupPath.length < 1)) {
    throw new Error("Test must be part of a group");
  }

  var group;
  var i = 0;
  _.each(groupPath, function(gname) {
    var array = (group ? (group.groups || (group.groups = []))
                 : resultTree);
    var newGroup = _.find(array, function(g) { return g.name === gname; });
    if (! newGroup) {
      newGroup = {
        name: gname,
        parent: (group || null),
        path: groupPath.slice(0, i+1),
        dep: new Deps.Dependency
      }; // create group
      array.push(newGroup);

      if (group)
        group.dep.changed();
      else
        topLevelGroupsDep.changed();
    }
    group = newGroup;
    i++;
  });

  var testName = results.test;
  var server = !!results.server;
  var test = _.find(group.tests || (group.tests = []),
                    function(t) { return t.name === testName &&
                                  t.server === server; });
  if (! test) {
    // create test
    var nameParts = _.clone(groupPath);
    nameParts.push(testName);
    var fullName = nameParts.join(' - ');
    test = {
      name: testName,
      parent: group,
      server: server,
      fullName: fullName,
      dep: new Deps.Dependency
    };
    group.tests.push(test);
    group.dep.changed();
    totalCount++;
    countDep.changed();
  }

  return test;
};

////
//// Helpers on test objects
////

var _testTime = function(t) {
  if (t.events && t.events.length > 0) {
    var lastEvent = _.last(t.events);
    if (lastEvent.type === "finish") {
      if ((typeof lastEvent.timeMs) === "number") {
        return lastEvent.timeMs;
      }
    }
  }
  return null;
};

var _testStatus = function(t) {
  var events = t.events || [];
  if (_.find(events, function(x) { return x.type === "exception"; })) {
    // "exception" should be last event, except race conditions on the
    // server can make this not the case.  Technically we can't tell
    // if the test is still running at this point, but it can only
    // result in FAIL.
    return "failed";
  } else if (events.length == 0 || (_.last(events).type != "finish")) {
    return "running";
  } else if (_.any(events, function(e) {
    return e.type == "fail" || e.type == "exception"; })) {
    return "failed";
  } else {
    return "succeeded";
  }
};

////
//// Templates
////

//// Template - navBars

Template.navBars.include({
  running: function() {
    countDep.depend();
    return running;
  },
  passed: function() {
    countDep.depend();
    return failedCount === 0;
  },
  total_test_time: function() {
    countDep.depend();

    // walk whole tree to get all tests
    var walk = function (groups) {
      var total = 0;

      _.each(groups || [], function (group) {
        _.each(group.tests || [], function (t) {
          total += _testTime(t);
        });

        total += walk(group.groups);
      });

      return total;
    };

    return walk(resultTree);
  }
});

//// Template - progressBar

Template.progressBar.include({
  running: function () {
    countDep.depend();
    return running;
  },
  percentPass: function () {
    countDep.depend();
    if (totalCount === 0)
      return 0;
    return 100*passedCount/totalCount;
  },
  totalCount: function () {
    countDep.depend();
    return totalCount;
  },
  passedCount: function () {
    countDep.depend();
    return passedCount;
  },
  percentFail: function () {
    countDep.depend();
    if (totalCount === 0)
      return 0;
    return 100*failedCount/totalCount;
  },
  anyFail: function () {
    countDep.depend();
    return failedCount > 0;
  },
  barOuterClass: function () {
    return this.running() ? 'progress-striped' : '';
  },
  barInnerClass: function () {
    return (this.anyFail() ?
            'bar-warning' : 'bar-success');
  }
});

//// Template - groupNav

Template.groupNav.include({
  groupPaths: function () {
    var groupPath = Session.get("groupPath");
    var ret = [];
    for (var i = 1; i <= groupPath.length; i++) {
      ret.push({path: groupPath.slice(0,i), name: groupPath[i-1]});
    }
    return ret;
  },
  rerunScheduled: function () {
    return Session.get("rerunScheduled");
  },
  changeToPath: function (path) {
    Session.set("groupPath", path);
    Session.set("rerunScheduled", true);
    // pretend there's just been a hot code push
    // so we run the tests completely fresh.
    Meteor._reload.reload();
  }
});

// XXXX EVENTS
/*Template.groupNav.events({
  "click .group": function () {
    changeToPath(this.path);
  },
  "click .rerun": function () {
    Session.set("rerunScheduled", true);
    Meteor._reload.reload();
  }
});*/

//// Template - failedTests

Template.failedTests.include({
  failedTests: function() {
    countDep.depend();
    return failedTests;
  }
});

//// Template - testTable

Template.testTable.include({
  data: function() {
    topLevelGroupsDep.depend();
    return resultTree;
  }
});

//// Template - test_group

Template.test_group.include({
  groupDep: function () {
    // this template just establishes a dependency. It doesn't actually
    // render anything.
    this.dep.depend();
    return "";
  }
});

// XXXX EVENTS
/*
Template.test_group.events({
  "click .groupname": function () {
    changeToPath(this.path);
  }
});
*/

//// Template - test

Template.test.include({
  testDep: function () {
    // this template just establishes a dependency. It doesn't actually
    // render anything.
    this.dep.depend();
    return "";
  },

  test_status_display: function() {
    var status = _testStatus(this);
    if (status == "failed") {
      return "FAIL";
    } else if (status == "succeeded") {
      return "PASS";
    } else {
      return "waiting...";
    }
  },

  test_time_display: function() {
    var time = _testTime(this);
    return (typeof time === "number") ? time + " ms" : "";
  },

  test_class: function() {
    var events = this.events || [];
    var classes = [_testStatus(this)];

    if (this.expanded) {
      classes.push("expanded");
    } else {
      classes.push("collapsed");
    }

    return classes.join(' ');
  },

  eventsArray: function() {
    var events = _.filter(this.events, function(e) {
      return e.type != "finish";
    });

    var partitionBy = function(seq, func) {
      var result = [];
      var lastValue = {};
      _.each(seq, function(x) {
        var newValue = func(x);
        if (newValue === lastValue) {
          result[result.length-1].push(x);
        } else {
          lastValue = newValue;
          result.push([x]);
        }
      });
      return result;
    };

    var dupLists = partitionBy(
      _.map(events, function(e) {
        // XXX XXX We need something better than stringify!
        // stringify([undefined]) === "[null]"
        e = _.clone(e);
        delete e.sequence;
        return {obj: e, str: JSON.stringify(e)};
      }), function(x) { return x.str; });

    return _.map(dupLists, function(L) {
      var obj = L[0].obj;
      return (L.length > 1) ? _.extend({times: L.length}, obj) : obj;
    });
  }
});

// XXXX EVENTS
/*Template.test.events({
  'click .testname': function() {
    this.expanded = ! this.expanded;
    this.dep.changed();
  }
});*/

//// Template - event

// XXXX EVENTS
/*
Template.event.events({
  'click .debug': function () {
    // the way we manage groupPath, shortName, cookies, etc, is really
    // messy. needs to be aggressively refactored.
    forgetEvents({groupPath: this.cookie.groupPath,
                  test: this.cookie.shortName});
    Meteor._debugTest(this.cookie, reportResults);
  }
});*/

Template.event.include({
  get_details: function() {

    var prepare = function(details) {
      return _.compact(_.map(details, function(val, key) {

        // You can end up with a an undefined value, e.g. using
        // isNull without providing a message attribute: isNull(1).
        // No need to display those.
        if (!_.isUndefined(val)) {
          return {
            key: key,
            val: val
          };
        } else {
          return undefined;
        }
      }));
    };

    var details = this.details;

    if (! details) {
      return null;
    } else {

      var type = details.type;
      var stack = details.stack;

      details = _.clone(details);
      delete details.type;
      delete details.stack;

      return {
        type: type,
        stack: stack,
        details: prepare(details)
      };
    }
  },

  is_debuggable: function() {
    return !!this.cookie;
  }
});
<|endoftext|>


<commit_before>(function donutChart(){

  widget.ChartFactory.registerChart( 'donut', createDonut );

  function createDonut( factory, options ) {

    var chartId = factory.createChartNode( options, 'donut', 'svg' );
    var domSelector = '#'+chartId;

    var height = options.height || 350;
    var width = options.width || 350;

    var chartPromise = $.Deferred();
    nv.addGraph( function() {

      var dataProjection = [];

      $.each( options.data, function( key, dataHolder ) {
        var dataSet = dataHolder();
        var datum = {
          key: key,
          y: dataSet[ dataSet.length-1 ].y
        };

        dataHolder.subscribe( function( oldValue ) {
          datum.y = dataSet[ dataSet.length-1 ].y;
          d3.select( domSelector ).call( factory.charts[ chartId ] );
        });

        dataProjection.push( datum );
      });

      var donutChart = nv.models.pieChart();
      donutChart.x( function(d) {
        return d.key;
      });
      donutChart.y( function(d) {
        return d.y;
      });
      donutChart.donut(true);
      donutChart.width(width);
      donutChart.height(height);
      donutChart.padAngle( options.padAngle || 0.08 );
      donutChart.cornerRadius( options.cornerRadius || 5 );
      donutChart.id( 'donut-chart' );

      if( options.title )
        donutChart.title( options.title );

      donutChart.pie.donutLabelsOutside(true);

      d3.select( domSelector )
        .style( {width:width+'px', height:height+'px'} )
        .datum( dataProjection )
        .transition().duration( options.transtitionDuration||600 )
        .call( donutChart );

      factory.charts[ chartId ] = donutChart;
      donutChart.domSelector = domSelector;
      chartPromise.resolve( donutChart );
    });

    return chartPromise;
  }

})();<commit_msg>Fix nvd3 upgrade warning in donut chart<commit_after>(function donutChart(){

  widget.ChartFactory.registerChart( 'donut', createDonut );

  function createDonut( factory, options ) {

    var chartId = factory.createChartNode( options, 'donut', 'svg' );
    var domSelector = '#'+chartId;

    var height = options.height || 350;
    var width = options.width || 350;

    var chartPromise = $.Deferred();
    nv.addGraph( function() {

      var dataProjection = [];

      $.each( options.data, function( key, dataHolder ) {
        var dataSet = dataHolder();
        var datum = {
          key: key,
          y: dataSet[ dataSet.length-1 ].y
        };

        dataHolder.subscribe( function( oldValue ) {
          datum.y = dataSet[ dataSet.length-1 ].y;
          d3.select( domSelector ).call( factory.charts[ chartId ] );
        });

        dataProjection.push( datum );
      });

      var donutChart = nv.models.pieChart();
      donutChart.x( function(d) {
        return d.key;
      });
      donutChart.y( function(d) {
        return d.y;
      });
      donutChart.donut(true);
      donutChart.width(width);
      donutChart.height(height);
      donutChart.padAngle( options.padAngle || 0.08 );
      donutChart.cornerRadius( options.cornerRadius || 5 );
      donutChart.id( 'donut-chart' );

      if( options.title )
        donutChart.title( options.title );

      donutChart.pie.labelsOutside(true);

      d3.select( domSelector )
        .style( {width:width+'px', height:height+'px'} )
        .datum( dataProjection )
        .transition().duration( options.transtitionDuration||600 )
        .call( donutChart );

      factory.charts[ chartId ] = donutChart;
      donutChart.domSelector = domSelector;
      chartPromise.resolve( donutChart );
    });

    return chartPromise;
  }

})();<|endoftext|>


<commit_before>var fs = require("fs-extra");

function setDefaults(config) {
  config = config || {};

  var hooks = config.hooks || {};

  return {
    ignore: config.ignore || [],
    commands: config.commands || {
      "compile": "truffle compile",
      "migrate": "truffle migrate",
      "test": "truffle test"
    },
    hooks: {
    }
  };
}

function read(path) {
  return fs.readFile(path)
    .catch(function() { return "{}"; })
    .then(JSON.parse)
    .then(setDefaults);
}

module.exports = {
  read: read
}
<commit_msg>Re-add defaults for hooks (accidentally removed)<commit_after>var fs = require("fs-extra");

function setDefaults(config) {
  config = config || {};

  var hooks = config.hooks || {};

  return {
    ignore: config.ignore || [],
    commands: config.commands || {
      "compile": "truffle compile",
      "migrate": "truffle migrate",
      "test": "truffle test"
    },
    hooks: {
      "post-unpack": hooks["post-unpack"] || ""
    }
  };
}

function read(path) {
  return fs.readFile(path)
    .catch(function() { return "{}"; })
    .then(JSON.parse)
    .then(setDefaults);
}

module.exports = {
  read: read
}
<|endoftext|>