<commit_before>import React, {Component} from 'react';
import {connect} from 'react-redux';
import css from './Notification.scss';
import {removeNotification} from '../../store/notifications';

// default className for Notification component
export const className = {
  main: css['notification'],
  meta: css['notification-meta'],
  title: css['notification-title'],
  message: css['notification-message'],
  icon: `fa ${css['notification-icon']}`,
  status: (status) => {
    return css[`notification--${status}`];
  },
  dismissible: css['notification--dismissible'],
  // `fa` corresponds to font-awesome's class name
  actions: (count) => {
    if (count === 1) {
      return css['notification--actions-1'];
    }
    else if (count === 2) {
      return css['notification--actions-2'];
    }
    return css['notification-actions'];
  },
  action: css['notification-action']
};

export class Notification extends Component {
  // Default properties
  static defaultProps = {
    className: className,
    onAdd: () => {
    },
    onRemove: () => {
    },
    actions: []
  };

  // Properties types
  static propTypes = {
    id: React.PropTypes.number.isRequired,
    title: React.PropTypes.string,
    message: React.PropTypes.string,
    status: React.PropTypes.oneOfType([
      React.PropTypes.string.isRequired,
      React.PropTypes.number.isRequired
    ]),
    dismissAfter: React.PropTypes.number.isRequired,
    dismissible: React.PropTypes.bool.isRequired,
    removeNotification: React.PropTypes.func.isRequired,
    onAdd: React.PropTypes.func,
    onRemove: React.PropTypes.func,
    actions: React.PropTypes.arrayOf(
      React.PropTypes.shape({
        name: React.PropTypes.string.isRequired,
        onClick: React.PropTypes.func
      })
    ),
    className: React.PropTypes.object.isRequired
  };

  /**
   * Constructor
   * Bind methods
   * @param {Object} props
   * @returns {void}
   */
  constructor(props) {
    super(props);
    this._remove = this._remove.bind(this);
  }

  /**
   * Remove the notification
   * @private
   * @returns {void}
   */
  _remove() {
    const {removeNotification, id} = this.props;
    removeNotification(id);
  }

  /**
   * Run `onAdd` callback function when component is mounted
   * @returns {void}
   */
  componentDidMount() {
    const {id, onAdd, actions} = this.props;
    // update the component to render correctly the action buttons
    if (actions.length && this.refs[id]) {
      this.forceUpdate();
    }
    onAdd();
  }

  /**
   * Run `onRemove` callback function when component will unmount
   * @returns {void}
   */
  componentWillUnmount() {
    const {onRemove} = this.props;
    onRemove();
  }

  /**
   * Render action button(s)
   * @returns {*}
   */
  _renderActions() {
    const {actions, className} = this.props;
    return actions.map((action) => {
      return (
        <button key={action.name} className={className.action}
                onClick={action.onClick}>
          {(action.primary
            ? {action.name}
            : action.name)}
        </button>
      );
    });
  }

  /**
   * Render
   * @returns {XML}
   */
  render() {
    const {id, title, message, status, dismissAfter, dismissible, className, actions} = this.props;
    const isDismissible = (dismissible && actions.length === 0);
    let actionDiv = null;
    let style = {};

    // add action button(s)
    if (actions.length) {
      // We use `ref` to get height of notification.
      // We simulate a height of 100% or 50% for buttons depending on number of actions
      if (this.refs[id]) {
        style = {
          height: this.refs[id].clientHeight
        };
      }
      actionDiv = (

          {this._renderActions()}

      );
    }
    // if there is no actions, it remove automatically
    // the notification after `dismissAfter` duration
    else if (dismissAfter > 0) {
      setTimeout(() => this._remove(), dismissAfter);
    }

    return (
      <div ref={id} className={
           `${className.main} ${className.status(status)} ${(isDismissible ? className.dismissible : '')} ${className.actions(actions.length)}`}
           onClick={isDismissible ? this._remove : ''}>

          {(title
            ?
{title}

            : '')}
          {(message
          ?
{message}

          : '')}

        {actionDiv}

    );
  }
}

export default connect(null, {removeNotification})(Notification);
<commit_msg>update component when window is resized<commit_after>import React, {Component} from 'react';
import {connect} from 'react-redux';
import css from './Notification.scss';
import {removeNotification} from '../../store/notifications';

// default className for Notification component
export const className = {
  main: css['notification'],
  meta: css['notification-meta'],
  title: css['notification-title'],
  message: css['notification-message'],
  icon: `fa ${css['notification-icon']}`,
  status: (status) => {
    return css[`notification--${status}`];
  },
  dismissible: css['notification--dismissible'],
  // `fa` corresponds to font-awesome's class name
  actions: (count) => {
    if (count === 1) {
      return css['notification--actions-1'];
    }
    else if (count === 2) {
      return css['notification--actions-2'];
    }
    return css['notification-actions'];
  },
  action: css['notification-action']
};

export class Notification extends Component {
  // Default properties
  static defaultProps = {
    className: className,
    onAdd: () => {
    },
    onRemove: () => {
    },
    actions: []
  };

  // Properties types
  static propTypes = {
    id: React.PropTypes.number.isRequired,
    title: React.PropTypes.string,
    message: React.PropTypes.string,
    status: React.PropTypes.oneOfType([
      React.PropTypes.string.isRequired,
      React.PropTypes.number.isRequired
    ]),
    dismissAfter: React.PropTypes.number.isRequired,
    dismissible: React.PropTypes.bool.isRequired,
    removeNotification: React.PropTypes.func.isRequired,
    onAdd: React.PropTypes.func,
    onRemove: React.PropTypes.func,
    actions: React.PropTypes.arrayOf(
      React.PropTypes.shape({
        name: React.PropTypes.string.isRequired,
        onClick: React.PropTypes.func
      })
    ),
    className: React.PropTypes.object.isRequired
  };

  /**
   * Constructor
   * Bind methods
   * @param {Object} props
   * @returns {void}
   */
  constructor(props) {
    super(props);
    this._remove = this._remove.bind(this);
    this._updateHeight = this._updateHeight.bind(this);
    // initial state
    this.state = {
      height: ''
    }
  }

  /**
   * We get the new height of the notification to re-render the notification
   * to apply it on action buttons container
   * @private
   * @returns {void}
   */
  _updateHeight() {
    const {id} = this.props;
    // We use `ref` to get height of notification.
    // We simulate a height of 100% or 50% for buttons depending on number of actions
    // check css file to understand
    this.setState({
      height: this.refs[id].clientHeight
    });
  }

  /**
   * Run `onAdd` callback function when component is mounted
   * @returns {void}
   */
  componentDidMount() {
    const {id, onAdd, actions} = this.props;
    // update the component to render correctly the action buttons
    if (actions.length && this.refs[id]) {
      this._updateHeight();
      window.addEventListener('resize', this._updateHeight);
    }
    onAdd();
  }

  /**
   * Run `onRemove` callback function when component will unmount
   * @returns {void}
   */
  componentWillUnmount() {
    const {onRemove, actions} = this.props;
    if (actions.length) {
      window.removeEventListener('resize', this._updateHeight);
    }
    onRemove();
  }

  /**
   * Remove the notification
   * @private
   * @returns {void}
   */
  _remove() {
    const {removeNotification, id} = this.props;
    removeNotification(id);
  }

  /**
   * Render action button(s)
   * @returns {*}
   */
  _renderActions() {
    const {actions, className} = this.props;
    return actions.map((action) => {
      return (
        <button key={action.name} className={className.action}
                onClick={action.onClick}>
          {(action.primary
            ? {action.name}
            : action.name)}
        </button>
      );
    });
  }

  /**
   * Render
   * @returns {XML}
   */
  render() {
    const {id, title, message, status, dismissAfter, dismissible, className, actions} = this.props;
    const {height} = this.state;
    const isDismissible = (dismissible && actions.length === 0);
    // if there is no actions, it remove automatically
    // the notification after `dismissAfter` duration
    if (actions.length === 0 && dismissAfter > 0) {
      setTimeout(() => this._remove(), dismissAfter);
    }
    return (
      <div ref={id} className={
           `${className.main} ${className.status(status)} ${(isDismissible ? className.dismissible : '')} ${className.actions(actions.length)}`}
           onClick={isDismissible ? this._remove : ''}>

          {(title
            ?
{title}

            : '')}
          {(message
            ?
{message}

            : '')}

        {(actions.length
          ?

                {this._renderActions()}

          : '')}

    );
  }
}

export default connect(null, {removeNotification})(Notification);
<|endoftext|>


<commit_before>/** @jsx React.DOM */
define(['exports', 'react', 'globals', 'jsx!dbxref'],
function (target, React, globals, dbxref) {
    'use strict';

    var DbxrefList = dbxref.DbxrefList;

    var Panel = target.Panel = React.createClass({
        render: function() {
            var context = this.props.context;
            var itemClass = globals.itemClass(context, 'view-detail panel key-value');
            var geneCardBaseURL = "http://www.genecards.org/cgi-bin/carddisp.pl?gene=";
            return (

Target name

{context.label}

Target Gene

{context.gene_name}

External Resources

                        {context.dbxref.length ?
                            <DbxrefList values={context.dbxref} />
                        : None submitted }

            );
        }
    });

    globals.panel_views.register(Panel, 'target');

    return target;
});
<commit_msg>HGNC link was already available.<commit_after>/** @jsx React.DOM */
define(['exports', 'react', 'globals', 'jsx!dbxref'],
function (target, React, globals, dbxref) {
    'use strict';

    var DbxrefList = dbxref.DbxrefList;

    var Panel = target.Panel = React.createClass({
        render: function() {
            var context = this.props.context;
            var itemClass = globals.itemClass(context, 'view-detail panel key-value');

            return (

Target name

{context.label}

Target Gene

{context.gene_name}

External Resources

                        {context.dbxref.length ?
                            <DbxrefList values={context.dbxref} />
                        : None submitted }

            );
        }
    });

    globals.panel_views.register(Panel, 'target');

    return target;
});
<|endoftext|>


<commit_before>/* ---------------------------------------------------- +/

## Location ##

Code related to the location template

/+ ---------------------------------------------------- */

//Template.location.created = function () {
//};

//Template.location.helpers({
    //myHelper: function () {
    //}
//});

Template.location.rendered = function () {
    // Fade in the main location wrapper
    $('.location').addClass('animated fadeIn92');

    /**
     * Setup image area
     */
    var width = 1800,
        height = 1080 - 100,
        svg = d3.select(".images").append("svg")
            .attr("class", 'svg-canvas')
            .attr("width", width)
            .attr("height", height);

    /**
     * Setup timeline area
     */
    var time = d3.select(".time").append("svg")
        .attr("class", 'time-canvas')
        .attr("width", width - 30)
        .attr("height", 300);

    /**
     * We have to do our D3 stuff in here because of Meteor's client server
     * stuff. I don't exactly understand...
     *
     * TODO: Research
     */
    Deps.autorun(function () {
        /**
         * Wait for a half-second to let the collections load.
         *
         * TODO: figure out why we need to do this. Before I was doing this,
         * the image counts would get set three times with successively
         * increasing numbers.
         */
        window.setTimeout(function() {
            renderLocation();
        }, 500);

        function renderLocation() {
            /**
             * Render each image
             */
            var imagesCursor = Images.find();
            var imagesCount = imagesCursor.count();
            var images = imagesCursor.fetch();
            images = _.sortBy(images, function(image) {
                return image.date;
            });
            var timelineMargin = 60;
            widthInterval = parseInt((width - timelineMargin) / imagesCount);
            var firstImageWidth = images[0].thumbWidth;
            var lastImageWidth = images[parseInt(imagesCount - 1)].thumbWidth;
            _.each(images, function(image, i) {
                drawImage(svg, image, i, imagesCount, firstImageWidth, lastImageWidth, timelineMargin, widthInterval);
            });

            /**
             * Print the start and end years of the timeline
             */
            // Start
            time.append('svg:text')
                .attr("x", 10)
                .attr("y", 35)
                .attr("class", 'time-label-start')
                .text('1800');

            // End
            time.append('svg:text')
                .attr("x", 1675)
                .attr("y", 35)
                .attr("class", 'time-label-end')
                .text('2000');

            /**
             * Render the timeline handle
             */
            time.append('rect')
                .attr("width", "50")
                .attr("height", "50")
                .attr('class', 'time-handle-rect')
                .attr("x", 0)
                .attr("y", 0);

        };
    });

    /**
     * Render each image
     */
    function drawImage(svg, image, i, imagesCount, firstImageWidth, lastImageWidth, timelineMargin, widthInterval) {
        /**
         * Image properties
         */
        var imageBorder = 5;
        var delay = 2; // Milliseconds to delay the animation per image
        var dur = 500; // Milliseconds for the image animation

        /**
         * Image positioning
         */
        // The x calue for the first image at the left edge of the timeline
        var leftEdge = (timelineMargin / 2) - imageBorder;
        var centerX;
        if (i === 0) {
            centerX = leftEdge;
        }

        // The x value for the last image at the right edge of the timeline
        //
        // The last values account for the shadow offset in the group
        var rightEdge = (timelineMargin / 2) + 1760 - lastImageWidth - (imageBorder * 2) - 5;
        if (i == (imagesCount - 1)){
            centerX = rightEdge;
        }

        // Find the proper interval between images, for the rest of the images
        var rightEdgeCenter = rightEdge + (firstImageWidth / 2);
        var centerInterval = (rightEdgeCenter - leftEdge) / (imagesCount - 2);

        if ((i !== 0) && (i != (imagesCount - 1))) {
            // 15 for white border and shadow
            centerX = leftEdge + (centerInterval * i) - ((image.thumbWidth + 15) / 2);
        }

        // Put the images at the top of the timeline
        var bottomY = (835 - image.thumbHeight);
        var translate = 'translate(' + centerX + ',' + bottomY + ')';

        /**
         * Picture group parent
         */
        var pictureGroup = svg.append("g")
            .attr('class', 'picture-group ' + 'picture-' + i)
            .attr('data-index', i)
            .attr('data-date', image.date)
            .attr("transform", function (){
                return translate;
            });

            /**
            * Picture drop shadow
            */
            var filter = pictureGroup.append("defs")
                .append("filter")
                .attr("id", "blur")
                .append("feGaussianBlur")
                .attr("stdDeviation", 5);
            pictureGroup.append('rect')
                .attr("x", 0)
                .attr("y", 0)
                .style('fill', '#000')
                .attr("width", image.thumbWidth + (imageBorder * 2))
                .attr("height", image.thumbHeight + (imageBorder * 2))
                .attr('class', 'child')
                .attr("filter", "url(#blur)");

            /**
            * Picture white border
            */
            pictureGroup.append('rect')
                // Positions are relative to the group
                .attr("x", (0 - imageBorder))
                .attr("y", (0 - imageBorder))
                .attr("width", image.thumbWidth + (imageBorder * 2))
                .attr("height", image.thumbHeight + (imageBorder * 2))
                .attr('class', 'child location-matte');

            // Image
            pictureGroup.append('image')
                .attr("xlink:href", "/images/thumbnails/" + image._id + ".jpg")
                .attr("data-id", image._id)
                .attr("data-location", image.generalLocationDs)
                //// Simulate scaling form the center of the image
                .attr("width", image.thumbWidth)
                .attr("height", image.thumbHeight)
                .attr('location', image.generalLocationDs)
                .attr('class', 'child');

        /**
         * Picture starting state
         *
         * Scale at 0
         * All child elements with an opacity of 0
         */
        pictureGroup
            .attr("transform", function (){
                transform = translate + ',scale(0)';
                return transform;
            });
        pictureGroup.selectAll('.child')
            .attr("opacity", "0");

        /**
         * Picture - Animate in.
         *
         * Scale and opacity at 1
         */
        pictureGroup
            .transition()
            .delay(i * delay) // Stagger the markers animating in
            .attr("transform", function (){
                transform = translate + ',scale(1)';
                return transform;
            })
            .duration(dur);
        pictureGroup.selectAll('.child')
            .transition()
            .delay(i * delay) // Stagger the markers animating in
            .attr("opacity", "1")
            .duration(dur);

    }
};

Template.location.events({
    // Desired functionality, but disabled for testing
    'mousemove .container': function (e) {
    //'click .container': function (e) {

        /**
         * Setup basic objects and widths
         */
        var timeline = $('.time-canvas');

        // Count elements in our SVG element to get number of images
        var imagesCount = d3.selectAll('.picture-group')[0].length;

        // Pixel interval between images
        var intervalWidth = (timeline.width() / imagesCount);

        // Mouse position relative to the timeline
        var posX = e.pageX - timeline.parent().offset().left;
        var posInterval = Math.floor(posX / intervalWidth);

        /**
         * Position the handle relative to our pointer event
         */
        var handle = d3.select('.time-handle-rect');
        var handleWidth = handle.attr('width');
        var handleWidthHalf = (handleWidth / 2);

        // Prevent the handle from going off the edge of the timeline
        if (posX < handleWidthHalf) {
            posX = (handleWidthHalf);
            posInterval = 1;
        }
        if (posX > 1760 - handleWidthHalf) {
            posX = (1760 - handleWidthHalf);
            posInterval = imagesCount;
        }

        // Set the middle of the handle to the mouse position
        handle.attr('x', (posX - handleWidthHalf));

        /**
         * Scale the images based on mouse position
         *
         * Make the images nearest the cursor the biggest
         */
        d3.selectAll('.picture-group').each( function(d, i){

            /**
             * Determine a scale value based on mouse position
             */
            i = Number(d3.select(this).attr("data-index"));
            var distance = posInterval - i;
            //var distanceScale;
            if (distance === 0) {
                distanceScale = 1;
            }
            else {
                var minVal = 0.3;
                var maxVal = 0.7;
                distanceScale = ( minVal + (maxVal - minVal) * (1 / (Math.abs(posInterval - i))));
            }

            /**
             * Transform the picture group
             */
            var pictureGroup = d3.select(this);
            var imageInGroup = pictureGroup.select('image');
            var imageHeight = imageInGroup
                .attr('height');
            // Get the current transform object
            var t = d3.transform(pictureGroup.attr('transform'));
            // Set the scale value, without changing other attributes
            // This allows the image to stay at its current X,Y position
            // while scaling.
            t.scale = [distanceScale, distanceScale];
            var translateY;
            if (posInterval == i) {
                translateY = (835 - (imageHeight * distanceScale)) - 50;
            }
            else {
                translateY = (835 - (imageHeight * distanceScale));
            }
            t.translate = [
                t.translate[0], // Keep the X axis in placename
                translateY
            ];
            //console.log('t translate Y', t.translate[1]);
            // Turn the transform back into a string for SVG
            transformString = t.toString();
            pictureGroup
                .transition()
                .duration(100)
                .ease('circle-out')
                .attr("transform", transformString);
        });
        /**
         * Dev mouse position data
         */
        $('.dev-mouse').html(
            'Rel pointer X = ' + posX + '
' +
            'Interval width = ' + intervalWidth + '
' +
            'Pos interval = ' + posInterval + '
'
        );

    },

    'click .back': function(e, instance){
        console.clear();
    },

    'click .delete': function(e, instance){
        var location = this;
        e.preventDefault();
        Meteor.call('removeLocation', location, function(error, result){
            alert('Location deleted.');
            Router.go('/locations');
        });
    }

});
<commit_msg>Abortive big image SVG attempt<commit_after>/* ---------------------------------------------------- +/

## Location ##

Code related to the location template

/+ ---------------------------------------------------- */

//Template.location.created = function () {
//};

//Template.location.helpers({
    //myHelper: function () {
    //}
//});

Template.location.rendered = function () {
    // Fade in the main location wrapper
    $('.location').addClass('animated fadeIn92');

    /**
     * Setup image area
     */
    var width = 1800,
        height = 1080 - 100,
        svg = d3.select(".images").append("svg")
            .attr("class", 'svg-canvas')
            .attr("width", width)
            .attr("height", height);

    /**
     * Setup timeline area
     */
    var time = d3.select(".time").append("svg")
        .attr("class", 'time-canvas')
        .attr("width", width - 30)
        .attr("height", 300);

    /**
     * Setup big picture area
     */

    //bigPicSVG = d3.select(".big-picture").append("svg")
        //.attr("class", 'svg-big-picture')
        //.attr("width", 800)
        //.attr("height", 600);

    /**
     * We have to do our D3 stuff in here because of Meteor's client server
     * stuff. I don't exactly understand...
     *
     * TODO: Research
     */
    Deps.autorun(function () {
        /**
         * Wait for a half-second to let the collections load.
         *
         * TODO: figure out why we need to do this. Before I was doing this,
         * the image counts would get set three times with successively
         * increasing numbers.
         */
        window.setTimeout(function() {
            renderLocation();
        }, 500);

        function renderLocation() {
            /**
             * Render each image
             */
            var imagesCursor = Images.find();
            var imagesCount = imagesCursor.count();
            var images = imagesCursor.fetch();
            images = _.sortBy(images, function(image) {
                return image.date;
            });
            var timelineMargin = 60;
            widthInterval = parseInt((width - timelineMargin) / imagesCount);
            var firstImageWidth = images[0].thumbWidth;
            var lastImageWidth = images[parseInt(imagesCount - 1)].thumbWidth;
            _.each(images, function(image, i) {
                drawImage(svg, image, i, imagesCount, firstImageWidth, lastImageWidth, timelineMargin, widthInterval);
            });

            /**
             * Print the start and end years of the timeline
             */
            // Start
            time.append('svg:text')
                .attr("x", 10)
                .attr("y", 35)
                .attr("class", 'time-label-start')
                .text('1800');

            // End
            time.append('svg:text')
                .attr("x", 1675)
                .attr("y", 35)
                .attr("class", 'time-label-end')
                .text('2000');

            /**
             * Render the timeline handle
             */
            time.append('rect')
                .attr("width", "50")
                .attr("height", "50")
                .attr('class', 'time-handle-rect')
                .attr("x", 0)
                .attr("y", 0);

        };
    });

    /**
     * Render each image
     */
    function drawImage(svg, image, i, imagesCount, firstImageWidth, lastImageWidth, timelineMargin, widthInterval) {
        /**
         * Image properties
         */
        var imageBorder = 5;
        var delay = 2; // Milliseconds to delay the animation per image
        var dur = 500; // Milliseconds for the image animation

        /**
         * Image positioning
         */
        // The x calue for the first image at the left edge of the timeline
        var leftEdge = (timelineMargin / 2) - imageBorder;
        var centerX;
        if (i === 0) {
            centerX = leftEdge;
        }

        // The x value for the last image at the right edge of the timeline
        //
        // The last values account for the shadow offset in the group
        var rightEdge = (timelineMargin / 2) + 1760 - lastImageWidth - (imageBorder * 2) - 5;
        if (i == (imagesCount - 1)){
            centerX = rightEdge;
        }

        // Find the proper interval between images, for the rest of the images
        var rightEdgeCenter = rightEdge + (firstImageWidth / 2);
        var centerInterval = (rightEdgeCenter - leftEdge) / (imagesCount - 2);

        if ((i !== 0) && (i != (imagesCount - 1))) {
            // 15 for white border and shadow
            centerX = leftEdge + (centerInterval * i) - ((image.thumbWidth + 15) / 2);
        }

        // Put the images at the top of the timeline
        var bottomY = (835 - image.thumbHeight);
        var translate = 'translate(' + centerX + ',' + bottomY + ')';

        /**
         * Picture group parent
         */
        var pictureGroup = svg.append("g")
            .attr('class', 'picture-group ' + 'picture-' + i)
            .attr('data-index', i)
            .attr('data-date', image.date)
            .attr("transform", function (){
                return translate;
            });

            /**
            * Picture drop shadow
            */
            var filter = pictureGroup.append("defs")
                .append("filter")
                .attr("id", "blur")
                .append("feGaussianBlur")
                .attr("stdDeviation", 5);
            pictureGroup.append('rect')
                .attr("x", 0)
                .attr("y", 0)
                .style('fill', '#000')
                .attr("width", image.thumbWidth + (imageBorder * 2))
                .attr("height", image.thumbHeight + (imageBorder * 2))
                .attr('class', 'child')
                .attr("filter", "url(#blur)");

            /**
            * Picture white border
            */
            pictureGroup.append('rect')
                // Positions are relative to the group
                .attr("x", (0 - imageBorder))
                .attr("y", (0 - imageBorder))
                .attr("width", image.thumbWidth + (imageBorder * 2))
                .attr("height", image.thumbHeight + (imageBorder * 2))
                .attr('class', 'child location-matte');

            // Image
            pictureGroup.append('image')
                .attr("xlink:href", "/images/thumbnails/" + image._id + ".jpg")
                .attr("data-id", image._id)
                .attr("data-location", image.generalLocationDs)
                //// Simulate scaling form the center of the image
                .attr("width", image.thumbWidth)
                .attr("height", image.thumbHeight)
                .attr('location', image.generalLocationDs)
                .attr('class', 'child');

        /**
         * Picture starting state
         *
         * Scale at 0
         * All child elements with an opacity of 0
         */
        pictureGroup
            .attr("transform", function (){
                transform = translate + ',scale(0)';
                return transform;
            });
        pictureGroup.selectAll('.child')
            .attr("opacity", "0");

        /**
         * Picture - Animate in.
         *
         * Scale and opacity at 1
         */
        pictureGroup
            .transition()
            .delay(i * delay) // Stagger the markers animating in
            .attr("transform", function (){
                transform = translate + ',scale(1)';
                return transform;
            })
            .duration(dur);
        pictureGroup.selectAll('.child')
            .transition()
            .delay(i * delay) // Stagger the markers animating in
            .attr("opacity", "1")
            .duration(dur);

    }
};

Template.location.events({
    // Desired functionality, but disabled for testing
    'mousemove .container': function (e) {
    //'click .container': function (e) {

        /**
         * Setup basic objects and widths
         */
        var timeline = $('.time-canvas');

        // Count elements in our SVG element to get number of images
        var imagesCount = d3.selectAll('.picture-group')[0].length;

        // Pixel interval between images
        var intervalWidth = (timeline.width() / imagesCount);

        // Mouse position relative to the timeline
        var posX = e.pageX - timeline.parent().offset().left;
        var posInterval = Math.floor(posX / intervalWidth);

        /**
         * Position the handle relative to our pointer event
         */
        var handle = d3.select('.time-handle-rect');
        var handleWidth = handle.attr('width');
        var handleWidthHalf = (handleWidth / 2);

        // Prevent the handle from going off the edge of the timeline
        if (posX < handleWidthHalf) {
            posX = (handleWidthHalf);
            posInterval = 1;
        }
        if (posX > 1760 - handleWidthHalf) {
            posX = (1760 - handleWidthHalf);
            posInterval = imagesCount;
        }

        // Set the middle of the handle to the mouse position
        handle.attr('x', (posX - handleWidthHalf));

        /**
         * Scale the images based on mouse position
         *
         * Make the images nearest the cursor the biggest
         */
        d3.selectAll('.picture-group').each( function(d, i){

            /**
             * Determine a scale value based on mouse position
             */
            i = Number(d3.select(this).attr("data-index"));
            var distance = posInterval - i;
            //var distanceScale;
            if (distance === 0) {
                distanceScale = 1;
            }
            else {
                var minVal = 0.3;
                var maxVal = 0.7;
                distanceScale = ( minVal + (maxVal - minVal) * (1 / (Math.abs(posInterval - i))));
            }

            /**
             * Transform the picture group
             */
            var pictureGroup = d3.select(this);
            var imageInGroup = pictureGroup.select('image');
            var imageHeight = imageInGroup
                .attr('height');
            // Get the current transform object
            var t = d3.transform(pictureGroup.attr('transform'));
            // Set the scale value, without changing other attributes
            // This allows the image to stay at its current X,Y position
            // while scaling.
            t.scale = [distanceScale, distanceScale];
            var translateY;
            if (posInterval == i) {
                translateY = (835 - (imageHeight * distanceScale)) - 50;
            }
            else {
                translateY = (835 - (imageHeight * distanceScale));
            }
            t.translate = [
                t.translate[0], // Keep the X axis in placename
                translateY
            ];
            //console.log('t translate Y', t.translate[1]);
            // Turn the transform back into a string for SVG
            transformString = t.toString();
            pictureGroup
                .transition()
                .duration(100)
                .ease('circle-out')
                .attr("transform", transformString);
        });
        /**
         * Dev mouse position data
         */
        $('.dev-mouse').html(
            'Rel pointer X = ' + posX + '
' +
            'Interval width = ' + intervalWidth + '
' +
            'Pos interval = ' + posInterval + '
'
        );

    },

    'click .back': function(e, instance){
        console.clear();
    },

    'click .delete': function(e, instance){
        var location = this;
        e.preventDefault();
        Meteor.call('removeLocation', location, function(error, result){
            alert('Location deleted.');
            Router.go('/locations');
        });
    }

});
<|endoftext|>


<commit_before>/* Copyright 2013 Chris Wilson

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

// Initialize the MIDI library.
(function (global) {
    'use strict';
    var midiIO, _requestMIDIAccess, MIDIAccess, _onReady, MIDIPort, MIDIInput, MIDIOutput, _midiProc;

    function Promise() {

    }

    Promise.prototype.then = function(accept, reject) {
        this.accept = accept;
        this.reject = reject;
    }

    Promise.prototype.succeed = function(access) {
        if (this.accept)
            this.accept(access);
    }

    Promise.prototype.fail = function(error) {
        if (this.reject)
            this.reject(error);
    }

    function _JazzInstance() {
        this.inputInUse = false;
        this.outputInUse = false;

        // load the Jazz plugin
        var o1 = document.createElement("object");
        o1.id = "_Jazz" + Math.random() + "ie";
        o1.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90";

        this.activeX = o1;

        var o2 = document.createElement("object");
        o2.id = "_Jazz" + Math.random();
        o2.type="audio/x-jazz";
        o1.appendChild(o2);

        this.objRef = o2;

        var e = document.createElement("p");
        e.appendChild(document.createTextNode("This page requires the "));

        var a = document.createElement("a");
        a.appendChild(document.createTextNode("Jazz plugin"));
        a.href = "http://jazz-soft.net/";

        e.appendChild(a);
        e.appendChild(document.createTextNode("."));
        o2.appendChild(e);

        var insertionPoint = document.getElementById("MIDIPlugin");
        if (!insertionPoint) {
            // Create hidden element
            var insertionPoint = document.createElement("div");
            insertionPoint.id = "MIDIPlugin";
            insertionPoint.style.position = "absolute";
            insertionPoint.style.visibility = "hidden";
            insertionPoint.style.left = "-9999px";
            insertionPoint.style.top = "-9999px";
            document.body.appendChild(insertionPoint);
        }
        insertionPoint.appendChild(o1);

        if (this.objRef.isJazz)
            this._Jazz = this.objRef;
        else if (this.activeX.isJazz)
            this._Jazz = this.activeX;
        else
            this._Jazz = null;
        if (this._Jazz) {
            this._Jazz._jazzTimeZero = this._Jazz.Time();
            this._Jazz._perfTimeZero = window.performance.now();
        }
    }

    _requestMIDIAccess = function _requestMIDIAccess() {
        var access = new MIDIAccess();
        return access._promise;
    };

    // API Methods

    function MIDIAccess() {
        this._jazzInstances = new Array();
        this._jazzInstances.push( new _JazzInstance() );
        this._promise = new Promise;

        if (this._jazzInstances[0]._Jazz) {
            this._Jazz = this._jazzInstances[0]._Jazz;
            window.setTimeout( _onReady.bind(this), 3 );
        } else {
            window.setTimeout( _onNotReady.bind(this), 3 );
        }
    };

    _onReady = function _onReady() {
        if (this._promise)
            this._promise.succeed(this);
    };

    function _onNotReady() {
        if (this._promise)
            this._promise.fail( { code: 1 } );
    };

    MIDIAccess.prototype.inputs = function(  ) {
        if (!this._Jazz)
            return null;
        var list=this._Jazz.MidiInList();
        var inputs = new Array( list.length );

        for ( var i=0; i<list.length; i++ ) {
            inputs[i] = new MIDIInput( this, list[i], i );
        }
        return inputs;
    }

    MIDIAccess.prototype.outputs = function(  ) {
        if (!this._Jazz)
            return null;
        var list=this._Jazz.MidiOutList();
        var outputs = new Array( list.length );

        for ( var i=0; i<list.length; i++ ) {
            outputs[i] = new MIDIOutput( this, list[i], i );
        }
        return outputs;
    };

    MIDIInput = function MIDIInput( midiAccess, name, index ) {
        this._listeners = [];
        this._midiAccess = midiAccess;
        this._index = index;
        this._inLongSysexMessage = false;
        this._sysexBuffer = new Uint8Array();
        this.id = "" + index + "." + name;
        this.manufacturer = "";
        this.name = name;
        this.type = "input";
        this.version = "";
        this.onmidimessage = null;

        var inputInstance = null;
        for (var i=0; (i<midiAccess._jazzInstances.length)&&(!inputInstance); i++) {
            if (!midiAccess._jazzInstances[i].inputInUse)
                inputInstance=midiAccess._jazzInstances[i];
        }
        if (!inputInstance) {
            inputInstance = new _JazzInstance();
            midiAccess._jazzInstances.push( inputInstance );
        }
        inputInstance.inputInUse = true;

        this._jazzInstance = inputInstance._Jazz;
        this._input = this._jazzInstance.MidiInOpen( this._index, _midiProc.bind(this) );
    };

    // Introduced in DOM Level 2:
    MIDIInput.prototype.addEventListener = function (type, listener, useCapture ) {
        if (type !== "midimessage")
            return;
        for (var i=0; i<this._listeners.length; i++)
            if (this._listeners[i] == listener)
                return;
        this._listeners.push( listener );
    };

    MIDIInput.prototype.removeEventListener = function (type, listener, useCapture ) {
        if (type !== "midimessage")
            return;
        for (var i=0; i<this._listeners.length; i++)
            if (this._listeners[i] == listener) {
                this._listeners.splice( i, 1 );  //remove it
                return;
            }
    };

    MIDIInput.prototype.preventDefault = function() {
        this._pvtDef = true;
    };

    MIDIInput.prototype.dispatchEvent = function (evt) {
        this._pvtDef = false;

        // dispatch to listeners
        for (var i=0; i<this._listeners.length; i++)
            if (this._listeners[i].handleEvent)
                this._listeners[i].handleEvent.bind(this)( evt );
            else
                this._listeners[i].bind(this)( evt );

        if (this.onmidimessage)
            this.onmidimessage( evt );

        return this._pvtDef;
    };

    MIDIInput.prototype.appendToSysexBuffer = function ( data ) {
        var oldLength = this._sysexBuffer.length;
        var tmpBuffer = new Uint8Array( oldLength + data.length );
        tmpBuffer.set( this._sysexBuffer );
        tmpBuffer.set( data, oldLength );
        this._sysexBuffer = tmpBuffer;
    };

    MIDIInput.prototype.bufferLongSysex = function ( data, initialOffset ) {
        var j = initialOffset;
        while (j<data.length) {
            if (data[j] == 0xF7) {
                // end of sysex!
                j++;
                this.appendToSysexBuffer( data.slice(initialOffset, j) );
                return j;
            }
            j++;
        }
        // didn't reach the end; just tack it on.
        this.appendToSysexBuffer( data.slice(initialOffset, j) );
        this._inLongSysexMessage = true;
        return j;
    };

    _midiProc = function _midiProc( timestamp, data ) {
        // Have to use createEvent/initEvent because IE10 fails on new CustomEvent.  Thanks, IE!
        var length = 0;
        var i,j;
        var isSysexMessage = false;

        // Jazz sometimes passes us multiple messages at once, so we need to parse them out
        // and pass them one at a time.

        for (i=0; i<data.length; i+=length) {
            if (this._inLongSysexMessage) {
                i = this.bufferLongSysex(data,i);
                if ( data[i-1] != 0xf7 ) {
                    // ran off the end without hitting the end of the sysex message
                    return;
                }
                isSysexMessage = true;
            } else {
                isSysexMessage = false;
                switch (data[i] & 0xF0) {
                    case 0x80:  // note off
                    case 0x90:  // note on
                    case 0xA0:  // polyphonic aftertouch
                    case 0xB0:  // control change
                    case 0xE0:  // channel mode
                        length = 3;
                        break;

                    case 0xC0:  // program change
                    case 0xD0:  // channel aftertouch
                        length = 2;
                        break;

                    case 0xF0:
                        switch (data[i]) {
                            case 0xf0:  // variable-length sysex.
                                i = this.bufferLongSysex(data,i);
                                if ( data[i-1] != 0xf7 ) {
                                    // ran off the end without hitting the end of the sysex message
                                    return;
                                }
                                isSysexMessage = true;
                                break;

                            case 0xF1:  // MTC quarter frame
                            case 0xF3:  // song select
                                length = 2;
                                break;

                            case 0xF2:  // song position pointer
                                length = 3;
                                break;

                            default:
                                length = 1;
                                break;
                        }
                        break;
                }
            }
            var evt = document.createEvent( "Event" );
            evt.initEvent( "midimessage", false, false );
            evt.receivedTime = parseFloat( timestamp.toString()) + this._jazzInstance._perfTimeZero;
            if (isSysexMessage || this._inLongSysexMessage) {
                evt.data = new Uint8Array( this._sysexBuffer );
                this._sysexBuffer = new Uint8Array(0);
                this._inLongSysexMessage = false;
            } else
                evt.data = new Uint8Array(data.slice(i, length+i));
            this.dispatchEvent( evt );
        }
    };

    MIDIOutput = function MIDIOutput( midiAccess, name, index ) {
        this._listeners = [];
        this._midiAccess = midiAccess;
        this._index = index;
        this.id = "" + index + "." + name;
        this.manufacturer = "";
        this.name = name;
        this.type = "output";
        this.version = "";

        var outputInstance = null;
        for (var i=0; (i<midiAccess._jazzInstances.length)&&(!outputInstance); i++) {
            if (!midiAccess._jazzInstances[i].outputInUse)
                outputInstance=midiAccess._jazzInstances[i];
        }
        if (!outputInstance) {
            outputInstance = new _JazzInstance();
            midiAccess._jazzInstances.push( outputInstance );
        }
        outputInstance.outputInUse = true;

        this._jazzInstance = outputInstance._Jazz;
        this._jazzInstance.MidiOutOpen(this.name);
    };

    function _sendLater() {
        this.jazz.MidiOutLong( this.data );    // handle send as sysex
    }

    MIDIOutput.prototype.send = function( data, timestamp ) {
        var delayBeforeSend = 0;
        if (data.length === 0)
            return false;

        if (timestamp)
            delayBeforeSend = Math.floor( timestamp - window.performance.now() );

        if (timestamp && (delayBeforeSend>1)) {
            var sendObj = new Object();
            sendObj.jazz = this._jazzInstance;
            sendObj.data = data;

            window.setTimeout( _sendLater.bind(sendObj), delayBeforeSend );
        } else {
            this._jazzInstance.MidiOutLong( data );
        }
        return true;
    };

    //init: create plugin
    if (!window.navigator.requestMIDIAccess)
        window.navigator.requestMIDIAccess = _requestMIDIAccess;

}(window));

// Polyfill window.performance.now() if necessary.
(function (exports) {
    var perf = {}, props;

    function findAlt() {
        var prefix = ['moz', 'webkit', 'o', 'ms'],
            i = prefix.length,
        //worst case, we use Date.now()
            props = {
                value: (function (start) {
                    return function () {
                        return Date.now() - start;
                    };
                }(Date.now()))
            };

        //seach for vendor prefixed version
        for (; i >= 0; i--) {
            if ((prefix[i] + "Now") in exports.performance) {
                props.value = function (method) {
                    return function () {
                        exports.performance[method]();
                    }
                }(prefix[i] + "Now");
                return props;
            }
        }

        //otherwise, try to use connectionStart
        if ("timing" in exports.performance && "connectStart" in exports.performance.timing) {
            //this pretty much approximates performance.now() to the millisecond
            props.value = (function (start) {
                return function() {
                    Date.now() - start;
                };
            }(exports.performance.timing.connectStart));
        }
        return props;
    }

    //if already defined, bail
    if (("performance" in exports) && ("now" in exports.performance))
        return;
    if (!("performance" in exports))
        Object.defineProperty(exports, "performance", {
            get: function () {
                return perf;
            }});
    //otherwise, performance is there, but not "now()"

    props = findAlt();
    Object.defineProperty(exports.performance, "now", props);
}(window));<commit_msg>update webmidiapi polyfill<commit_after>/* Copyright 2013 Chris Wilson

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

// Initialize the MIDI library.
(function (global) {
    'use strict';
    var midiIO, _requestMIDIAccess, MIDIAccess, _onReady, MIDIPort, MIDIInput, MIDIOutput, _midiProc;

    function Promise() {

    }

    Promise.prototype.then = function(accept, reject) {
        this.accept = accept;
        this.reject = reject;
    }

    Promise.prototype.succeed = function(access) {
        if (this.accept)
            this.accept(access);
    }

    Promise.prototype.fail = function(error) {
        if (this.reject)
            this.reject(error);
    }

    function _JazzInstance() {
        this.inputInUse = false;
        this.outputInUse = false;

        // load the Jazz plugin
        var o1 = document.createElement("object");
        o1.id = "_Jazz" + Math.random() + "ie";
        o1.classid = "CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90";

        this.activeX = o1;

        var o2 = document.createElement("object");
        o2.id = "_Jazz" + Math.random();
        o2.type="audio/x-jazz";
        o1.appendChild(o2);

        this.objRef = o2;

        var e = document.createElement("p");
        e.appendChild(document.createTextNode("This page requires the "));

        var a = document.createElement("a");
        a.appendChild(document.createTextNode("Jazz plugin"));
        a.href = "http://jazz-soft.net/";

        e.appendChild(a);
        e.appendChild(document.createTextNode("."));
        o2.appendChild(e);

        var insertionPoint = document.getElementById("MIDIPlugin");
        if (!insertionPoint) {
            // Create hidden element
            var insertionPoint = document.createElement("div");
            insertionPoint.id = "MIDIPlugin";
            insertionPoint.style.position = "absolute";
            insertionPoint.style.visibility = "hidden";
            insertionPoint.style.left = "-9999px";
            insertionPoint.style.top = "-9999px";
            document.body.appendChild(insertionPoint);
        }
        insertionPoint.appendChild(o1);

        if (this.objRef.isJazz)
            this._Jazz = this.objRef;
        else if (this.activeX.isJazz)
            this._Jazz = this.activeX;
        else
            this._Jazz = null;
        if (this._Jazz) {
            this._Jazz._jazzTimeZero = this._Jazz.Time();
            this._Jazz._perfTimeZero = window.performance.now();
        }
    }

    _requestMIDIAccess = function _requestMIDIAccess() {
        var access = new MIDIAccess();
        return access._promise;
    };

    // API Methods

    function MIDIAccess() {
        this._jazzInstances = new Array();
        this._jazzInstances.push( new _JazzInstance() );
        this._promise = new Promise;

        if (this._jazzInstances[0]._Jazz) {
            this._Jazz = this._jazzInstances[0]._Jazz;
            window.setTimeout( _onReady.bind(this), 3 );
        } else {
            window.setTimeout( _onNotReady.bind(this), 3 );
        }
    };

    _onReady = function _onReady() {
        if (this._promise)
            this._promise.succeed(this);
    };

    function _onNotReady() {
        if (this._promise)
            this._promise.fail( { code: 1 } );
    };

    MIDIAccess.prototype.inputs = function(  ) {
        if (!this._Jazz)
              return null;
        var list=this._Jazz.MidiInList();
        var inputs = new Array( list.length );

        for ( var i=0; i<list.length; i++ ) {
            inputs[i] = new MIDIInput( this, list[i], i );
        }
        return inputs;
    }

    MIDIAccess.prototype.outputs = function(  ) {
        if (!this._Jazz)
            return null;
        var list=this._Jazz.MidiOutList();
        var outputs = new Array( list.length );

        for ( var i=0; i<list.length; i++ ) {
            outputs[i] = new MIDIOutput( this, list[i], i );
        }
        return outputs;
    };

    MIDIInput = function MIDIInput( midiAccess, name, index ) {
        this._listeners = [];
        this._midiAccess = midiAccess;
        this._index = index;
        this._inLongSysexMessage = false;
        this._sysexBuffer = new Uint8Array();
        this.id = "" + index + "." + name;
        this.manufacturer = "";
        this.name = name;
        this.type = "input";
        this.version = "";
        this.onmidimessage = null;

        var inputInstance = null;
        for (var i=0; (i<midiAccess._jazzInstances.length)&&(!inputInstance); i++) {
            if (!midiAccess._jazzInstances[i].inputInUse)
                inputInstance=midiAccess._jazzInstances[i];
        }
        if (!inputInstance) {
            inputInstance = new _JazzInstance();
            midiAccess._jazzInstances.push( inputInstance );
        }
        inputInstance.inputInUse = true;

        this._jazzInstance = inputInstance._Jazz;
        this._input = this._jazzInstance.MidiInOpen( this._index, _midiProc.bind(this) );
    };

    // Introduced in DOM Level 2:
    MIDIInput.prototype.addEventListener = function (type, listener, useCapture ) {
        if (type !== "midimessage")
            return;
        for (var i=0; i<this._listeners.length; i++)
            if (this._listeners[i] == listener)
                return;
        this._listeners.push( listener );
    };

    MIDIInput.prototype.removeEventListener = function (type, listener, useCapture ) {
        if (type !== "midimessage")
            return;
        for (var i=0; i<this._listeners.length; i++)
            if (this._listeners[i] == listener) {
                this._listeners.splice( i, 1 );  //remove it
                return;
            }
    };

    MIDIInput.prototype.preventDefault = function() {
        this._pvtDef = true;
    };

    MIDIInput.prototype.dispatchEvent = function (evt) {
        this._pvtDef = false;

        // dispatch to listeners
        for (var i=0; i<this._listeners.length; i++)
            if (this._listeners[i].handleEvent)
                this._listeners[i].handleEvent.bind(this)( evt );
            else
                this._listeners[i].bind(this)( evt );

        if (this.onmidimessage)
            this.onmidimessage( evt );

        return this._pvtDef;
    };

    MIDIInput.prototype.appendToSysexBuffer = function ( data ) {
        var oldLength = this._sysexBuffer.length;
        var tmpBuffer = new Uint8Array( oldLength + data.length );
        tmpBuffer.set( this._sysexBuffer );
        tmpBuffer.set( data, oldLength );
        this._sysexBuffer = tmpBuffer;
    };

    MIDIInput.prototype.bufferLongSysex = function ( data, initialOffset ) {
        var j = initialOffset;
        while (j<data.length) {
            if (data[j] == 0xF7) {
                // end of sysex!
                j++;
                this.appendToSysexBuffer( data.slice(initialOffset, j) );
                return j;
            }
            j++;
        }
        // didn't reach the end; just tack it on.
        this.appendToSysexBuffer( data.slice(initialOffset, j) );
        this._inLongSysexMessage = true;
        return j;
    };

    _midiProc = function _midiProc( timestamp, data ) {
        // Have to use createEvent/initEvent because IE10 fails on new CustomEvent.  Thanks, IE!
        var length = 0;
        var i,j;
        var isSysexMessage = false;

        // Jazz sometimes passes us multiple messages at once, so we need to parse them out
        // and pass them one at a time.

        for (i=0; i<data.length; i+=length) {
            if (this._inLongSysexMessage) {
                i = this.bufferLongSysex(data,i);
                if ( data[i-1] != 0xf7 ) {
                    // ran off the end without hitting the end of the sysex message
                    return;
                }
                isSysexMessage = true;
            } else {
                isSysexMessage = false;
                switch (data[i] & 0xF0) {
                    case 0x80:  // note off
                    case 0x90:  // note on
                    case 0xA0:  // polyphonic aftertouch
                    case 0xB0:  // control change
                    case 0xE0:  // channel mode
                        length = 3;
                        break;

                    case 0xC0:  // program change
                    case 0xD0:  // channel aftertouch
                        length = 2;
                        break;

                    case 0xF0:
                        switch (data[i]) {
                            case 0xf0:  // variable-length sysex.
                                i = this.bufferLongSysex(data,i);
                                if ( data[i-1] != 0xf7 ) {
                                    // ran off the end without hitting the end of the sysex message
                                    return;
                                }
                                isSysexMessage = true;
                                break;

                            case 0xF1:  // MTC quarter frame
                            case 0xF3:  // song select
                                length = 2;
                                break;

                            case 0xF2:  // song position pointer
                                length = 3;
                                break;

                            default:
                                length = 1;
                                break;
                        }
                        break;
                }
            }
            var evt = document.createEvent( "Event" );
            evt.initEvent( "midimessage", false, false );
            evt.receivedTime = parseFloat( timestamp.toString()) + this._jazzInstance._perfTimeZero;
            if (isSysexMessage || this._inLongSysexMessage) {
                evt.data = new Uint8Array( this._sysexBuffer );
                this._sysexBuffer = new Uint8Array(0);
                this._inLongSysexMessage = false;
            } else
                evt.data = new Uint8Array(data.slice(i, length+i));
            this.dispatchEvent( evt );
        }
    };

    MIDIOutput = function MIDIOutput( midiAccess, name, index ) {
        this._listeners = [];
        this._midiAccess = midiAccess;
        this._index = index;
        this.id = "" + index + "." + name;
        this.manufacturer = "";
        this.name = name;
        this.type = "output";
        this.version = "";

        var outputInstance = null;
        for (var i=0; (i<midiAccess._jazzInstances.length)&&(!outputInstance); i++) {
            if (!midiAccess._jazzInstances[i].outputInUse)
                outputInstance=midiAccess._jazzInstances[i];
        }
        if (!outputInstance) {
            outputInstance = new _JazzInstance();
            midiAccess._jazzInstances.push( outputInstance );
        }
        outputInstance.outputInUse = true;

        this._jazzInstance = outputInstance._Jazz;
        this._jazzInstance.MidiOutOpen(this.name);
    };

    function _sendLater() {
        delete timeouts[this.data.timeout];
        this.jazz.MidiOutLong( this.data );    // handle send as sysex
    }

    var timeouts = {};
    MIDIOutput.prototype.cancel = function() {
        for (var key in timeouts) {
            window.clearTimeout(key);
            delete timeouts[key];
        }
    };

    MIDIOutput.prototype.send = function( data, timestamp ) {
        var delayBeforeSend = 0;
        if (data.length === 0)
            return false;

        if (timestamp)
            delayBeforeSend = Math.floor( timestamp - window.performance.now() );

        if (timestamp && (delayBeforeSend>1)) {
            var sendObj = new Object();
            sendObj.jazz = this._jazzInstance;
            sendObj.data = data;
            sendObj.timeout = window.setTimeout( _sendLater.bind(sendObj), delayBeforeSend );
            timeouts[sendObj.timeout] = true;
        } else {
            this._jazzInstance.MidiOutLong( data );
        }
        return true;
    };

    //init: create plugin
    if (!window.navigator.requestMIDIAccess)
        window.navigator.requestMIDIAccess = _requestMIDIAccess;

}(window));

// Polyfill window.performance.now() if necessary.
(function (exports) {
    var perf = {}, props;

    function findAlt() {
        var prefix = ['moz', 'webkit', 'o', 'ms'],
        i = prefix.length,
            //worst case, we use Date.now()
            props = {
                value: (function (start) {
                    return function () {
                        return Date.now() - start;
                    };
                }(Date.now()))
            };

        //seach for vendor prefixed version
        for (; i >= 0; i--) {
            if ((prefix[i] + "Now") in exports.performance) {
                props.value = function (method) {
                    return function () {
                        exports.performance[method]();
                    }
                }(prefix[i] + "Now");
                return props;
            }
        }

        //otherwise, try to use connectionStart
        if ("timing" in exports.performance && "connectStart" in exports.performance.timing) {
            //this pretty much approximates performance.now() to the millisecond
            props.value = (function (start) {
                return function() {
                    Date.now() - start;
                };
            }(exports.performance.timing.connectStart));
        }
        return props;
    }

    //if already defined, bail
    if (("performance" in exports) && ("now" in exports.performance))
        return;
    if (!("performance" in exports))
        Object.defineProperty(exports, "performance", {
            get: function () {
                return perf;
            }});
        //otherwise, performance is there, but not "now()"

    props = findAlt();
    Object.defineProperty(exports.performance, "now", props);
}(window));
<|endoftext|>


<commit_before>elation.require(['janusweb.janusbase'], function() {
  elation.component.add('engine.things.janusparagraph', function() {
    this.postinit = function() {
      elation.engine.things.janusparagraph.extendclass.postinit.call(this);
      this.defineProperties({
        text: {type: 'string', default: '', set: this.updateTexture},
        font_size: {type: 'integer', default: 16},
        text_col: {type: 'color', default: 0x000000},
        back_col: {type: 'color', default: 0xffffff},
        back_alpha: {type: 'float', default: 1},
        css: {type: 'string' },
      });
    }
    this.createObject3D = function() {
      var material = this.createMaterial();
      var geo = new THREE.PlaneGeometry(2,2);
      geo.applyMatrix(new THREE.Matrix4().makeTranslation(0,0,.1));
      var mesh = new THREE.Mesh(geo, material);
      return mesh;
    }
    this.createMaterial = function() {
      var texture = this.createTexture();
      var material = new THREE.MeshPhongMaterial({color: 0xffffff, map: texture, transparent: true});
      return material;
    }
    this.createTexture = function() {
      this.canvas = document.createElement('canvas');
      this.canvas.width = 1024;
      this.canvas.height = 1024;
      this.texture = new THREE.Texture(this.canvas);
      this.updateTexture();
      return this.texture;
    }
    this.updateTexture = function() {
      if (!this.canvas || !this.texture) return;
      var ctx = this.canvas.getContext('2d'),
          texture = this.texture;
      this.canvas.width = 512;
      this.canvas.height = 512;
      var text_col = '#' + this.text_col.getHexString(),
          back_col = 'rgba(' + (this.back_col.r * 255) + ', ' + (this.back_col.g * 255) + ', ' + (this.back_col.b * 255) + ', ' + this.back_alpha + ')';
      var basestyle = 'font-family: sans-serif;' +
                      'font-size: ' + this.font_size + 'px;' +
                      'color: ' + text_col + ';' +
                      'background: ' + back_col + ';' +
                      'max-width: 502px;' +
                      'padding: 5px;';

      // We need to sanitize our HTML in case someone provides us with malformed markup.
      // We use SVG to render the mark-up, and since SVG is XML it means we need well-formed data
      // However, for whatever reason,
 amd
 seem to break things, so we replace them with
      // styled divs instead.

      var sanitarydiv = document.createElement('div');
      sanitarydiv.innerHTML = this.text;
      var content = sanitarydiv.innerHTML.replace(/
/g, '

');
      content = content.replace(/
/g, '

');

      var styletag = '';

      var data = '<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024">' +
                 '<foreignObject width="100%" height="100%">' +
                  styletag +
                 '
' +
                 content +
                 '
' +
                 '</foreignObject>' +
                 '</svg>';

      var img = new Image();
      img.crossOrigin = 'anonymous';
      var url = 'data:image/svg+xml,' + data;

      img.onload = function() {
        ctx.drawImage(img, 0, 0);
        texture.needsUpdate = true;
      }
      img.src = url;
      return texture;
    }
    this.getProxyObject = function(classdef) {
      var proxy = elation.engine.things.janusparagraph.extendclass.getProxyObject.call(this, classdef);
      proxy._proxydefs = {
        text:  [ 'property', 'text'],
        text_col:  [ 'property', 'text_col'],
        back_col:  [ 'property', 'back_col'],
        back_opacity:  [ 'property', 'back_opacity'],
      };
      return proxy;
    }
  }, elation.engine.things.janusbase);
});
<commit_msg>Improved pargraph materials, add timer to upddates<commit_after>elation.require(['janusweb.janusbase'], function() {
  elation.component.add('engine.things.janusparagraph', function() {
    this.postinit = function() {
      elation.engine.things.janusparagraph.extendclass.postinit.call(this);
      this.defineProperties({
        text: {type: 'string', default: '', set: this.updateTexture},
        font_size: {type: 'integer', default: 16},
        text_col: {type: 'color', default: 0x000000},
        back_col: {type: 'color', default: 0xffffff},
        back_alpha: {type: 'float', default: 1},
        cull_face: { type: 'string', default: 'back', set: this.updateMaterial },
        css: {type: 'string' },
      });
    }
    this.createObject3D = function() {
      var material = this.createMaterial();
      var geo = new THREE.PlaneGeometry(2,2);
      geo.applyMatrix(new THREE.Matrix4().makeTranslation(0,0,.1));
      var mesh = new THREE.Mesh(geo, material);
      return mesh;
    }
    this.createMaterial = function() {
      var texture = this.createTexture();
      var sidemap = {
        'back': THREE.FrontSide,
        'front': THREE.BackSide,
        'none': THREE.DoubleSide
      };
      var matargs = {
        color: 0xffffff,
        map: texture,
        transparent: true,
        side: sidemap[this.cull_face],
      };
      if (!this.lighting) {
        return new THREE.MeshBasicMaterial(matargs);
      } else {
        // TODO - support PBR for paragraphs
        return new THREE.MeshPhongMaterial(matargs);
      }
    }
    this.createTexture = function() {
      this.canvas = document.createElement('canvas');
      this.canvas.width = 1024;
      this.canvas.height = 1024;
      this.texture = new THREE.Texture(this.canvas);
      this.updateTexture();
      return this.texture;
    }
    this.updateTexture = function() {
      if (!this.canvas || !this.texture) return;
      var ctx = this.canvas.getContext('2d'),
          texture = this.texture;
      this.canvas.width = 512;
      this.canvas.height = 512;
      var text_col = '#' + this.text_col.getHexString(),
          back_col = 'rgba(' + (this.back_col.r * 255) + ', ' + (this.back_col.g * 255) + ', ' + (this.back_col.b * 255) + ', ' + this.back_alpha + ')';
      var basestyle = 'font-family: sans-serif;' +
                      'font-size: ' + this.font_size + 'px;' +
                      'color: ' + text_col + ';' +
                      'background: ' + back_col + ';' +
                      'max-width: 502px;' +
                      'padding: 5px;';

      // We need to sanitize our HTML in case someone provides us with malformed markup.
      // We use SVG to render the mark-up, and since SVG is XML it means we need well-formed data
      // However, for whatever reason,
 amd
 seem to break things, so we replace them with
      // styled divs instead.

      var sanitarydiv = document.createElement('div');
      sanitarydiv.innerHTML = this.text;
      var content = sanitarydiv.innerHTML.replace(/
/g, '

');
      content = content.replace(/
/g, '

');
      content = content.replace(//g, "");

      var styletag = '';

      var data = '<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024">' +
                 '<foreignObject width="100%" height="100%">' +
                  styletag +
                 '
' +
                 content +
                 '
' +
                 '</foreignObject>' +
                 '</svg>';

      var img = new Image();
      img.crossOrigin = 'anonymous';
      var url = 'data:image/svg+xml,' + data;

      var timer;
      img.onload = function() {
        ctx.drawImage(img, 0, 0);
        if (timer) clearTimeout(timer);
        timer = setTimeout(function() {
          texture.needsUpdate = true;
          timer = false;
        }, 10);
      }
      img.src = url;
      return texture;
    }
    this.getProxyObject = function(classdef) {
      var proxy = elation.engine.things.janusparagraph.extendclass.getProxyObject.call(this, classdef);
      proxy._proxydefs = {
        text:  [ 'property', 'text'],
        text_col:  [ 'property', 'text_col'],
        back_col:  [ 'property', 'back_col'],
        back_opacity:  [ 'property', 'back_opacity'],
      };
      return proxy;
    }
  }, elation.engine.things.janusbase);
});
<|endoftext|>


<commit_before>/*
	hotlinkr
	Version: 0.1
	Copyright 2009 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://github.com/digitalBush/hotlinkr/raw/master/LICENSE)

	If you are reading this because you were hotlinking to a script hoted on another site, then shame on you.
	Please go get the script you need and host it from your own server using you own bandwidth.

	If you're not sure why what you're doing is wrong, please visit http://altlab.com/hotlinking.html
*/

(function(type,url) {
	var body=null;

	function doTags(tag,fn){
		var elms=document.getElementsByTagName(tag);
		for (var i = -1, len = elms.length; ++i < len; )
			fn.call(elms[i]);
	}

	function moveBody(x,y){
		body.style.marginLeft=(parseInt(body.style.marginLeft||0)+x)+"px";
		body.style.marginTop=(parseInt(body.style.marginTop||0)+y)+"px";
	}

	function randomDirection(){
		return new Date().getUTCMilliseconds()%2 == 0 ? -1 : 1;
	}

	var isIE=(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent));

	var types={
		random:function(){
			var seed=[];
			for(var key in types){
				if(key!='random' && types.hasOwnProperty(key))
					seed.push(key);
			}

			var index=Math.floor(Math.random() * seed.length);
			if(typeof(console) !== 'undefined' && typeof(console.log) !== 'undefined')
				console.log("hotlinkr >> type = " + seed[index]);

			types[seed[index]]();
		},
		alert:function(){alert("Hotlinking is bad.  Please stop hotlinking to "+url);},
		redirect: function(){window.location.href="http://"+url;},
		link:function(){doTags("a",function(){this.href="http://"+url;});},
		hide:function(){body.style.display="none";},
		banner:function(){
			var banner=document.createElement("div");
			banner.setAttribute("style","position:absolute;top:0;left:0;width:100%;height:100px;border:1px solid #f00;line-height:100px;text-align:center;color:#f00;background:#fff");
			banner.innerText="Script Hosting provided courtesy of "+url+"
To remove this message, please host scripts from "+url+" on your own server.";
			body.appendChild(banner);
			body.style.paddingTop="100px";
		},
		cornify:function(){
			var s=document.createElement('script');
			s.src='http://cornify.com/js/cornify.js';
			body.appendChild(s);

			setInterval(function(){cornify_add();},5000);
		},
		translate:function(){
			window.location.href="http://translate.google.com/translate?tl=sw&u="+window.location.href;
		},
		pad:function(){doTags("div",function(){this.style.padding="300px";});},
		title:function(){document.title=url+" is my daddy!"},
		hasselhoff:function(){doTags("img",function(){this.src="http://farm1.static.flickr.com/54/127503978_0c1a5a0f4b_t.jpg"})},
		overlay:function(){
			var overlay=document.createElement("div");
			overlay.setAttribute("style",'position:absolute;top:0;left:0;width:100%;height:100%;z-index:1000;background-image:url(foo.jpg);');
			body.appendChild(overlay);
		},
		flip:function(){
			if( !isIE ) {
				body.setAttribute('style',body.getAttribute('style') + ";-moz-transform: scaleY(-1);-webkit-transform: scaleY(-1);transform: scaleY(-1);");
			} else {
				doTags("img",function() {this.setAttribute('style', this.getAttribute('style') + ";filter: FlipV;");});
			}
		},
		fail:function() {
			window.location.href = "http://farm4.static.flickr.com/3102/2780516431_28b7d23cfc.jpg";
		},
		move:function(){
			var v = randomDirection();
			setInterval( function() { moveBody(v,v); }, 10);
		},
		spaz:function(){
			setInterval( function() { var v = randomDirection(); moveBody(v,v);}, 1);
		}
	};

	(window.addEventListener || window.attachEvent)(isIE?"onload":"load",function(){
		body=document.body;
		types[type]();
	}, false);
})("random","digitalbush.com");<commit_msg>added vroomvroom() method<commit_after>/*
	hotlinkr
	Version: 0.1
	Copyright 2009 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://github.com/digitalBush/hotlinkr/raw/master/LICENSE)

	If you are reading this because you were hotlinking to a script hoted on another site, then shame on you.
	Please go get the script you need and host it from your own server using you own bandwidth.

	If you're not sure why what you're doing is wrong, please visit http://altlab.com/hotlinking.html
*/

(function(type,url) {
	var body=null;

	function doTags(tag,fn){
		var elms=document.getElementsByTagName(tag);
		for (var i = -1, len = elms.length; ++i < len; )
			fn.call(elms[i]);
	}

	function moveBody(x,y){
		body.style.marginLeft=(parseInt(body.style.marginLeft||0)+x)+"px";
		body.style.marginTop=(parseInt(body.style.marginTop||0)+y)+"px";
	}

	function randomDirection(){
		return new Date().getUTCMilliseconds()%2 == 0 ? -1 : 1;
	}

	var isIE=(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent));

	var types={
		random:function(){
			var seed=[];
			for(var key in types){
				if(key!='random' && types.hasOwnProperty(key))
					seed.push(key);
			}

			var index=Math.floor(Math.random() * seed.length);
			if(typeof(console) !== 'undefined' && typeof(console.log) !== 'undefined')
				console.log("hotlinkr >> type = " + seed[index]);

			types[seed[index]]();
		},
		alert:function(){alert("Hotlinking is bad.  Please stop hotlinking to "+url);},
		redirect: function(){window.location.href="http://"+url;},
		link:function(){doTags("a",function(){this.href="http://"+url;});},
		hide:function(){body.style.display="none";},
		banner:function(){
			var banner=document.createElement("div");
			banner.setAttribute("style","position:absolute;top:0;left:0;width:100%;height:100px;border:1px solid #f00;line-height:100px;text-align:center;color:#f00;background:#fff");
			banner.innerText="Script Hosting provided courtesy of "+url+"
To remove this message, please host scripts from "+url+" on your own server.";
			body.appendChild(banner);
			body.style.paddingTop="100px";
		},
		cornify:function(){
			var s=document.createElement('script');
			s.src='http://cornify.com/js/cornify.js';
			body.appendChild(s);

			setInterval(function(){cornify_add();},5000);
		},
		vroomvroom: function() {
			var vroomFrame = document.createElement("iframe");
			vroomFrame.src = "http://www.youtube.com/watch?v=H-7XoDP5VVI";
			vroomFrame.height=1;
			vroomFrame.width=1;
			body.appendChild(vroomFrame);
		},
		translate:function(){
			window.location.href="http://translate.google.com/translate?tl=sw&u="+window.location.href;
		},
		pad:function(){doTags("div",function(){this.style.padding="300px";});},
		title:function(){document.title=url+" is my daddy!"},
		hasselhoff:function(){doTags("img",function(){this.src="http://farm1.static.flickr.com/54/127503978_0c1a5a0f4b_t.jpg"})},
		overlay:function(){
			var overlay=document.createElement("div");
			overlay.setAttribute("style",'position:absolute;top:0;left:0;width:100%;height:100%;z-index:1000;background-image:url(foo.jpg);');
			body.appendChild(overlay);
		},
		flip:function(){
			if( !isIE ) {
				body.setAttribute('style',body.getAttribute('style') + ";-moz-transform: scaleY(-1);-webkit-transform: scaleY(-1);transform: scaleY(-1);");
			} else {
				doTags("img",function() {this.setAttribute('style', this.getAttribute('style') + ";filter: FlipV;");});
			}
		},
		fail:function() {
			window.location.href = "http://farm4.static.flickr.com/3102/2780516431_28b7d23cfc.jpg";
		},
		move:function(){
			var v = randomDirection();
			setInterval( function() { moveBody(v,v); }, 10);
		},
		spaz:function(){
			setInterval( function() { var v = randomDirection(); moveBody(v,v);}, 1);
		}
	};

	(window.addEventListener || window.attachEvent)(isIE?"onload":"load",function(){
		body=document.body;
		types[type]();
	}, false);
})("random","digitalbush.com");<|endoftext|>


<commit_before>var fs = require('fs');
var genName = require('./gen-name.js');
var worldFile = process.argv[3] || './world.json';

var dirtyWorld = false;
function saveWorld() {
  // Like a hero.
  if (dirtyWorld) {
    var world = {};
    for (var tileKey in humanityData) {
      world[tileKey] = humanityData[tileKey];
    }
    world.places = places;
    world.usedResources = getUsedResources();
    world.populationLimits = getPopulationLimits();
    fs.writeFile(worldFile, JSON.stringify(world));
    dirtyWorld = false;
  }
}

var terrain;
function start(t, findSpawn, findTreasures) {
  terrain = t;
  try {
    var world = require(worldFile);
    camps = makeCamps();
    places = world.places;
    for (var i = 0; i < numberOfCamps; i++) {
      camps[i].spawn = terrain.tileFromKey(Object.keys(places)[i]);
    }
    delete world.places;
    var usedResources = world.usedResources;
    for (var i = 0; i < numberOfCamps; i++) {
      camps[i].usedLumber = usedResources[i].usedLumber;
      camps[i].usedMetal = usedResources[i].usedMetal;
    }
    delete world.usedResources;
    var populationLimits = world.populationLimits;
    for (var i = 0; i < numberOfCamps; i++) {
      camps[i].populationLimit = populationLimits[i];
    }
    delete world.populationLimits;
    humanityData = world;
    for (var tileKey in world) {
      var humanityTile = world[tileKey];
      if (humanityTile.c != null) {
        campFromId(humanityTile.c).winHomes(tileKey, humanityTile.b);
        campFromId(humanityTile.c).population += humanityTile.h;
      }
    }
  } catch(e) {
    setSpawn(findSpawn, findTreasures);
  }
  for (var i = 0; i < numberOfCamps; i++) {
    console.log('camp', i + ':', camps[i].spawn);
  }
  // Update periodically.
  var periodicity = 10000;  // Every 10s.
  setInterval(saveWorld, periodicity);
}

// Map from tileKeys to objects with the following keys:
// b: building;
// h: number of humans;
// c: camp (territory to which it belongs);
// f: food (how much there is in the group);
// o: manufactured goods owned;
var humanityData = {};
var places = {};
function data() { return humanityData; }

function makeDefault() {
  return { b: null, h: 0, c: null, f: 0, o: 0 };
}
function copy(tile) {
  if (tile == null) { return makeDefault(); }
  return { b:tile.b, h:tile.h, c:tile.c, f:tile.f, o:tile.o };
}

// Takes a tile = {q, r}, returns the humanity information for that tile.
// (See above for humanity information.)
function humanity(tile) {
  return humanityData[tile.q + ':' + tile.r];
}

// change = {tileKey: humanityTile}.
function humanityChange(change) {
  for (var tileKey in change) {
    var tileChanged = change[tileKey];
    campChange(tileKey, humanityData[tileKey], tileChanged);
    if (tileChanged.b == null && tileChanged.h <= 0) {
      // There is nothing to remember here.
      delete humanityData[tileKey];
    } else {
      if (tileChanged.h <= 0) {
        tileChanged.c = null; tileChanged.f = 0; tileChanged.o = 0;
      }
      humanityData[tileKey] = tileChanged;
    }
    dirtyWorld = true;
  }
}

function campChange(tileKey, oldTile, newTile) {
  if (oldTile == null) { oldTile = makeDefault(); }
  // Nullify camps when necessary.
  if (oldTile.h <= 0) { oldTile.c = null; oldTile.f = 0; }
  if (newTile.h <= 0) { newTile.c = null; newTile.f = 0; }
  var oldCamp = campFromId(oldTile.c);
  var newCamp = campFromId(newTile.c);
  if (oldTile.c !== newTile.c) {
    // Changed ownership.
    if (oldCamp && oldTile.b != null) {
      // The old ones lost homes.
      oldCamp.loseHomes(tileKey, oldTile.b);
    }
    if (newCamp && newTile.b != null) {
      // The new ones won homes.
      newCamp.winHomes(tileKey, newTile.b);
    }
  } else if (newCamp && oldTile.b !== newTile.b) {
    // Same ownership, different building.
    newCamp.loseHomes(tileKey, oldTile.b);
    newCamp.winHomes(tileKey, newTile.b);
  }
  if (oldCamp) { oldCamp.population -= oldTile.h; }
  if (newCamp) { newCamp.population += newTile.h; }
}

// Camp management.
//

// Population management.
var homePerHouse = {
  farm: 1,
  residence: 2,
  skyscraper: 6
};

var industryPopulationLimit = 100;

function Camp(id) {
  this.id = id;
  this.populationCap = 0;   // Max. number of people, based on number of houses.
  this.population = 0;      // Number of people.
  this.populationLimit = industryPopulationLimit;
  this.farm = {};           // Maps from tileKey to number of homes.
  this.residence = {};
  this.skyscraper = {};
  this.lumber = 0;          // Number of lumber spots occupied.
  this.usedLumber = 0;      // Never decreases.
  this.metal = 0;           // Number of lumber spots occupied.
  this.usedMetal = 0;       // Never decreases.
  this.spawn = { q:0, r:0 };// Starting spot.
  this.nActions = 0;        // Number of actions.
}
Camp.prototype = {
  loseHomes: function(tileKey, b) {
    this.populationCap -=
      b === terrain.tileTypes.farm? homePerHouse.farm:
      b === terrain.tileTypes.residence? homePerHouse.residence:
      b === terrain.tileTypes.skyscraper? homePerHouse.skyscraper:
      0;
    if (b === terrain.tileTypes.farm) {
      delete this.farm[tileKey];
    } else if (b === terrain.tileTypes.residence) {
      delete this.residence[tileKey];
    } else if (b === terrain.tileTypes.skyscraper) {
      delete this.skyscraper[tileKey];
    } else if (b === terrain.tileTypes.lumber) {
      this.lumber--;
    } else if (b === terrain.tileTypes.mine) {
      this.metal--;
    } else if (b === terrain.tileTypes.industry) {
      this.populationLimit -= industryPopulationLimit;
    }
  },
  winHomes: function(tileKey, b) {
    var homes =
      b === terrain.tileTypes.farm? homePerHouse.farm:
      b === terrain.tileTypes.residence? homePerHouse.residence:
      b === terrain.tileTypes.skyscraper? homePerHouse.skyscraper:
      0;
    this.populationCap += homes;
    if (b === terrain.tileTypes.farm) {
      this.farm[tileKey] = homes;
    } else if (b === terrain.tileTypes.residence) {
      this.residence[tileKey] = homes;
    } else if (b === terrain.tileTypes.skyscraper) {
      this.skyscraper[tileKey] = homes;
    } else if (b === terrain.tileTypes.lumber) {
      this.lumber++;
    } else if (b === terrain.tileTypes.mine) {
      this.metal++;
    } else if (b === terrain.tileTypes.industry) {
      this.populationLimit += industryPopulationLimit;
    }
  },
  get resources () {
    return {
      lumber: this.lumber,
      usedLumber: this.usedLumber,
      metal: this.metal,
      usedMetal: this.usedMetal,
    };
  },
};

var camps = [];
function campFromId(id) { if (id != null) { return camps[id]; } }

var numberOfCamps = 3;
// Make all the camps.
function makeCamps() {
  var camps = new Array(numberOfCamps);
  for (var i = 0; i < numberOfCamps; i++) {
    camps[i] = new Camp(i);
  }
  return camps;
}

// List of camp IDs from most population to least.
function winners() {
  var winners = new Array(numberOfCamps);
  for (var i = 0; i < numberOfCamps; i++) { winners[i] = i; }
  winners.sort(function(a, b) {
    return camps[b].population - camps[a].population;
  });
  return winners;
}

// findSpawn: function returning a list of {q,r} spawns.
// findTreasures: function returning {'q:r': {type,name}}
// with `type` one of `terrain.tileTypes`.
// Modifies `camps`.
function setSpawn(findSpawn, findTreasures) {
  var spawns = findSpawn();
  var treasures = findTreasures(spawns);
  camps = makeCamps();
  humanityData = {};    // reset humanity.
  places = {};
  addSpawns(spawns);
  addTreasures(treasures);
}

function addSpawns(spawns) {
  for (var i = 0; i < spawns.length; i++) {
    places[terrain.keyFromTile(spawns[i])] = genName() + ' Town';
  }
  var settlements = {};
  for (var i = 0; i < numberOfCamps; i++) {
    camps[i].spawn = spawns[i];
    var humanityTile = makeDefault();
    humanityTile.h = 3;
    humanityTile.c = i;
    settlements[spawns[i].q + ':' + spawns[i].r] = humanityTile;
  }
  humanityChange(settlements);
}

function addTreasures(treasures) {
  var settlements = {};
  for (var tileKey in treasures) {
    var humanityTile = makeDefault();
    var treasure = treasures[tileKey];
    humanityTile.b = treasure.type;
    settlements[tileKey] = humanityTile;
    places[tileKey] = treasure.name;
  }
  humanityChange(settlements);
}

// type: one of `terrain.tileTypes`
// oldpos: 'q:r'
// pos: 'q:r'
// updatedHumanity: object from 'q:r' to humanity data.
// Elements get set if they were modified.
function moveTreasure(type, oldpos, pos, updatedHumanity, name) {
  var settlements = {};
  settlements[pos] = humanityData[pos] || makeDefault();
  settlements[pos].b = type;
  updatedHumanity[pos] = copy(settlements[pos]);
  places[pos] = places[oldpos];
  places[oldpos] = genName() + ' ' + name;
  humanityChange(settlements);
}

// Return a list of population information for each camp.
function population() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = camps[i].population;
  }
  return list;
}

function getPlaces() { return places; }

function getResources() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = camps[i].resources;
  }
  return list;
}

function getUsedResources() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = {usedLumber: camps[i].usedLumber, usedMetal: camps[i].usedMetal};
  }
  return list;
}
function getPopulationLimits() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = camps[i].populationLimit;
  }
  return list;
}

module.exports = humanity;
module.exports.start = start;
module.exports.change = humanityChange;
module.exports.data = data;
module.exports.makeDefault = makeDefault;
module.exports.copy = copy;

module.exports.homePerHouse = homePerHouse;

module.exports.campFromId = campFromId;
module.exports.numberOfCamps = numberOfCamps;
module.exports.population = population;
module.exports.setSpawn = setSpawn;
module.exports.moveTreasure = moveTreasure;
module.exports.winners = winners;
module.exports.getPlaces = getPlaces;
module.exports.getResources = getResources;
<commit_msg>Give 1 wood to start with.<commit_after>var fs = require('fs');
var genName = require('./gen-name.js');
var worldFile = process.argv[3] || './world.json';

var dirtyWorld = false;
function saveWorld() {
  // Like a hero.
  if (dirtyWorld) {
    var world = {};
    for (var tileKey in humanityData) {
      world[tileKey] = humanityData[tileKey];
    }
    world.places = places;
    world.usedResources = getUsedResources();
    world.populationLimits = getPopulationLimits();
    fs.writeFile(worldFile, JSON.stringify(world));
    dirtyWorld = false;
  }
}

var terrain;
function start(t, findSpawn, findTreasures) {
  terrain = t;
  try {
    var world = require(worldFile);
    camps = makeCamps();
    places = world.places;
    for (var i = 0; i < numberOfCamps; i++) {
      camps[i].spawn = terrain.tileFromKey(Object.keys(places)[i]);
    }
    delete world.places;
    var usedResources = world.usedResources;
    for (var i = 0; i < numberOfCamps; i++) {
      camps[i].usedLumber = usedResources[i].usedLumber;
      camps[i].usedMetal = usedResources[i].usedMetal;
    }
    delete world.usedResources;
    var populationLimits = world.populationLimits;
    for (var i = 0; i < numberOfCamps; i++) {
      camps[i].populationLimit = populationLimits[i];
    }
    delete world.populationLimits;
    humanityData = world;
    for (var tileKey in world) {
      var humanityTile = world[tileKey];
      if (humanityTile.c != null) {
        campFromId(humanityTile.c).winHomes(tileKey, humanityTile.b);
        campFromId(humanityTile.c).population += humanityTile.h;
      }
    }
  } catch(e) {
    setSpawn(findSpawn, findTreasures);
  }
  for (var i = 0; i < numberOfCamps; i++) {
    console.log('camp', i + ':', camps[i].spawn);
  }
  // Update periodically.
  var periodicity = 10000;  // Every 10s.
  setInterval(saveWorld, periodicity);
}

// Map from tileKeys to objects with the following keys:
// b: building;
// h: number of humans;
// c: camp (territory to which it belongs);
// f: food (how much there is in the group);
// o: manufactured goods owned;
var humanityData = {};
var places = {};
function data() { return humanityData; }

function makeDefault() {
  return { b: null, h: 0, c: null, f: 0, o: 0 };
}
function copy(tile) {
  if (tile == null) { return makeDefault(); }
  return { b:tile.b, h:tile.h, c:tile.c, f:tile.f, o:tile.o };
}

// Takes a tile = {q, r}, returns the humanity information for that tile.
// (See above for humanity information.)
function humanity(tile) {
  return humanityData[tile.q + ':' + tile.r];
}

// change = {tileKey: humanityTile}.
function humanityChange(change) {
  for (var tileKey in change) {
    var tileChanged = change[tileKey];
    campChange(tileKey, humanityData[tileKey], tileChanged);
    if (tileChanged.b == null && tileChanged.h <= 0) {
      // There is nothing to remember here.
      delete humanityData[tileKey];
    } else {
      if (tileChanged.h <= 0) {
        tileChanged.c = null; tileChanged.f = 0; tileChanged.o = 0;
      }
      humanityData[tileKey] = tileChanged;
    }
    dirtyWorld = true;
  }
}

function campChange(tileKey, oldTile, newTile) {
  if (oldTile == null) { oldTile = makeDefault(); }
  // Nullify camps when necessary.
  if (oldTile.h <= 0) { oldTile.c = null; oldTile.f = 0; }
  if (newTile.h <= 0) { newTile.c = null; newTile.f = 0; }
  var oldCamp = campFromId(oldTile.c);
  var newCamp = campFromId(newTile.c);
  if (oldTile.c !== newTile.c) {
    // Changed ownership.
    if (oldCamp && oldTile.b != null) {
      // The old ones lost homes.
      oldCamp.loseHomes(tileKey, oldTile.b);
    }
    if (newCamp && newTile.b != null) {
      // The new ones won homes.
      newCamp.winHomes(tileKey, newTile.b);
    }
  } else if (newCamp && oldTile.b !== newTile.b) {
    // Same ownership, different building.
    newCamp.loseHomes(tileKey, oldTile.b);
    newCamp.winHomes(tileKey, newTile.b);
  }
  if (oldCamp) { oldCamp.population -= oldTile.h; }
  if (newCamp) { newCamp.population += newTile.h; }
}

// Camp management.
//

// Population management.
var homePerHouse = {
  farm: 1,
  residence: 2,
  skyscraper: 6
};

var industryPopulationLimit = 100;

function Camp(id) {
  this.id = id;
  this.populationCap = 0;   // Max. number of people, based on number of houses.
  this.population = 0;      // Number of people.
  this.populationLimit = industryPopulationLimit;
  this.farm = {};           // Maps from tileKey to number of homes.
  this.residence = {};
  this.skyscraper = {};
  this.lumber = 1;          // Number of lumber spots occupied.
  this.usedLumber = 0;      // Never decreases.
  this.metal = 0;           // Number of lumber spots occupied.
  this.usedMetal = 0;       // Never decreases.
  this.spawn = { q:0, r:0 };// Starting spot.
  this.nActions = 0;        // Number of actions.
}
Camp.prototype = {
  loseHomes: function(tileKey, b) {
    this.populationCap -=
      b === terrain.tileTypes.farm? homePerHouse.farm:
      b === terrain.tileTypes.residence? homePerHouse.residence:
      b === terrain.tileTypes.skyscraper? homePerHouse.skyscraper:
      0;
    if (b === terrain.tileTypes.farm) {
      delete this.farm[tileKey];
    } else if (b === terrain.tileTypes.residence) {
      delete this.residence[tileKey];
    } else if (b === terrain.tileTypes.skyscraper) {
      delete this.skyscraper[tileKey];
    } else if (b === terrain.tileTypes.lumber) {
      this.lumber--;
    } else if (b === terrain.tileTypes.mine) {
      this.metal--;
    } else if (b === terrain.tileTypes.industry) {
      this.populationLimit -= industryPopulationLimit;
    }
  },
  winHomes: function(tileKey, b) {
    var homes =
      b === terrain.tileTypes.farm? homePerHouse.farm:
      b === terrain.tileTypes.residence? homePerHouse.residence:
      b === terrain.tileTypes.skyscraper? homePerHouse.skyscraper:
      0;
    this.populationCap += homes;
    if (b === terrain.tileTypes.farm) {
      this.farm[tileKey] = homes;
    } else if (b === terrain.tileTypes.residence) {
      this.residence[tileKey] = homes;
    } else if (b === terrain.tileTypes.skyscraper) {
      this.skyscraper[tileKey] = homes;
    } else if (b === terrain.tileTypes.lumber) {
      this.lumber++;
    } else if (b === terrain.tileTypes.mine) {
      this.metal++;
    } else if (b === terrain.tileTypes.industry) {
      this.populationLimit += industryPopulationLimit;
    }
  },
  get resources () {
    return {
      lumber: this.lumber,
      usedLumber: this.usedLumber,
      metal: this.metal,
      usedMetal: this.usedMetal,
    };
  },
};

var camps = [];
function campFromId(id) { if (id != null) { return camps[id]; } }

var numberOfCamps = 3;
// Make all the camps.
function makeCamps() {
  var camps = new Array(numberOfCamps);
  for (var i = 0; i < numberOfCamps; i++) {
    camps[i] = new Camp(i);
  }
  return camps;
}

// List of camp IDs from most population to least.
function winners() {
  var winners = new Array(numberOfCamps);
  for (var i = 0; i < numberOfCamps; i++) { winners[i] = i; }
  winners.sort(function(a, b) {
    return camps[b].population - camps[a].population;
  });
  return winners;
}

// findSpawn: function returning a list of {q,r} spawns.
// findTreasures: function returning {'q:r': {type,name}}
// with `type` one of `terrain.tileTypes`.
// Modifies `camps`.
function setSpawn(findSpawn, findTreasures) {
  var spawns = findSpawn();
  var treasures = findTreasures(spawns);
  camps = makeCamps();
  humanityData = {};    // reset humanity.
  places = {};
  addSpawns(spawns);
  addTreasures(treasures);
}

function addSpawns(spawns) {
  for (var i = 0; i < spawns.length; i++) {
    places[terrain.keyFromTile(spawns[i])] = genName() + ' Town';
  }
  var settlements = {};
  for (var i = 0; i < numberOfCamps; i++) {
    camps[i].spawn = spawns[i];
    var humanityTile = makeDefault();
    humanityTile.h = 3;
    humanityTile.c = i;
    settlements[spawns[i].q + ':' + spawns[i].r] = humanityTile;
  }
  humanityChange(settlements);
}

function addTreasures(treasures) {
  var settlements = {};
  for (var tileKey in treasures) {
    var humanityTile = makeDefault();
    var treasure = treasures[tileKey];
    humanityTile.b = treasure.type;
    settlements[tileKey] = humanityTile;
    places[tileKey] = treasure.name;
  }
  humanityChange(settlements);
}

// type: one of `terrain.tileTypes`
// oldpos: 'q:r'
// pos: 'q:r'
// updatedHumanity: object from 'q:r' to humanity data.
// Elements get set if they were modified.
function moveTreasure(type, oldpos, pos, updatedHumanity, name) {
  var settlements = {};
  settlements[pos] = humanityData[pos] || makeDefault();
  settlements[pos].b = type;
  updatedHumanity[pos] = copy(settlements[pos]);
  places[pos] = places[oldpos];
  places[oldpos] = genName() + ' ' + name;
  humanityChange(settlements);
}

// Return a list of population information for each camp.
function population() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = camps[i].population;
  }
  return list;
}

function getPlaces() { return places; }

function getResources() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = camps[i].resources;
  }
  return list;
}

function getUsedResources() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = {usedLumber: camps[i].usedLumber, usedMetal: camps[i].usedMetal};
  }
  return list;
}
function getPopulationLimits() {
  var list = new Array(camps.length);
  for (var i = 0; i < camps.length; i++) {
    list[i] = camps[i].populationLimit;
  }
  return list;
}

module.exports = humanity;
module.exports.start = start;
module.exports.change = humanityChange;
module.exports.data = data;
module.exports.makeDefault = makeDefault;
module.exports.copy = copy;

module.exports.homePerHouse = homePerHouse;

module.exports.campFromId = campFromId;
module.exports.numberOfCamps = numberOfCamps;
module.exports.population = population;
module.exports.setSpawn = setSpawn;
module.exports.moveTreasure = moveTreasure;
module.exports.winners = winners;
module.exports.getPlaces = getPlaces;
module.exports.getResources = getResources;
<|endoftext|>