<commit_before>/**
 * This Source Code is licensed under the MIT license. If a copy of the
 * MIT-license was not distributed with this file, You can obtain one at:
 * http://opensource.org/licenses/mit-license.html.
 *
 * @author: Hein Rutjes (IjzerenHein)
 * @license MIT
 * @copyright Gloey Apps, 2014
 */

/**
 * Internal LayoutNode class used by `LayoutController`.
 *
 * @module
 */
define(function (require, exports, module) {

    var Transitionable = require('famous/transitions/Transitionable');
    var Transform = require('famous/core/Transform');
    var LayoutUtility = require('./LayoutUtility');

    /**
     * @class
     * @param {Object} renderNode Render-node which this layout-node represents
     * @alias module:LayoutNode
     */
    function LayoutNode(renderNode, spec) {
        this.renderNode = renderNode;
        this._spec = spec ? LayoutUtility.cloneSpec(spec) : {};
        this._spec.renderNode = renderNode; // also store in spec
        this._specModified = true;
        this._invalidated = false;
        this._removing = false;
    }

    /**
     * Called to update the underlying render-node
     */
    LayoutNode.prototype.setRenderNode = function (renderNode) {
        this.renderNode = renderNode;
        this._spec.renderNode = renderNode;
    };

    /**
     * Called to update the options for the node
     */
    LayoutNode.prototype.setOptions = function (options) {
        // override to implement
    };

    /**
     * Called when the node is destroyed
     */
    LayoutNode.prototype.destroy = function () {
        this.renderNode = undefined;
        this._spec.renderNode = undefined;
        this._viewSequence = undefined;
    };

    /**
     * Reset the end-state. This function is called on all layout-nodes prior to
     * calling the layout-function. So that the layout-function starts with a clean slate.
     */
    LayoutNode.prototype.reset = function () {
        this._invalidated = false;
        this.trueSizeRequested = false;
    };

    /**
     * Set the spec of the node
     *
     * @param {Object} spec
     */
    LayoutNode.prototype.setSpec = function (spec) {
        this._specModified = true;
        if (spec.align) {
            if (!spec.align) {
                this._spec.align = [0, 0];
            }
            this._spec.align[0] = spec.align[0];
            this._spec.align[1] = spec.align[1];
        }
        else {
            this._spec.align = undefined;
        }
        if (spec.origin) {
            if (!spec.origin) {
                this._spec.origin = [0, 0];
            }
            this._spec.origin[0] = spec.origin[0];
            this._spec.origin[1] = spec.origin[1];
        }
        else {
            this._spec.origin = undefined;
        }
        if (spec.size) {
            if (!spec.size) {
                this._spec.size = [0, 0];
            }
            this._spec.size[0] = spec.size[0];
            this._spec.size[1] = spec.size[1];
        }
        else {
            this._spec.size = undefined;
        }

        this._spec.hide = spec.hide;

        if (spec.transform) {
            if (!spec.transform) {
                this._spec.transform = spec.transform.slice(0);
            }
            else {
                for (var i = 0; i < 16; i++) {
                    this._spec.transform[i] = spec.transform[i];
                }
            }
        }
        else {
            this._spec.transform = undefined;
        }
        this._spec.opacity = spec.opacity;
    };

    /**
     * Initiates a transition on the layout node
     * @param transition
     * @param {Function} onComplete callback when complete
     */
    LayoutNode.prototype.transition = function (transition, onComplete) {

        this._originalSet = this._transitionable ?
            /* If there's already something in progress, then interpolate the current starting position */
            this._interpolateSet() :
            /* Otherwise, it's the most recent set */
            this._lastSet;
        let transitionable = this._transitionable = new Transitionable(0);
        transitionable.set(1, transition, () => {
            /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
            * because we are finished */
            if (this._transitionable === transitionable) {
                delete this._transitionable;
                this._originalSet = this._targetSet;
                if(onComplete){
                    onComplete();
                }
            }
        })
    };

        /**
     * Set the content of the node
     *
     * @param {Object} set
     */
    LayoutNode.prototype.set = function (set, size) {

        var transition = set.transition;

        if (transition) {
            this._targetSet = set;
            if (!this._transitionable) {
                let transitionable = this._transitionable = new Transitionable(0);
                transitionable.set(1, transition, () => {
                    /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
                    * because we are finished */
                    if (this._transitionable === transitionable) {
                        delete this._transitionable;
                        this._originalSet = this._targetSet;
                    }
                })
            }
        }
        if (!this._transitionable) {
            this._originalSet = set;
        }

        this._lastSet = set;
        this._invalidated = true;
        this._specModified = true;
        this._removing = false;
        this._modifySpecFromSet(this._spec, set);
        this.scrollLength = set.scrollLength;
    };

    LayoutNode.prototype._modifySpecFromSet = function (spec, set) {
        spec.opacity = set.opacity;

        var propertiesDefaultingToZero = ['size', 'origin', 'align'];
        for (var i = 0; i < propertiesDefaultingToZero.length; i++) {
            var propertyName = propertiesDefaultingToZero[i];
            spec[propertyName] = set[propertyName] ? [set[propertyName][0] || 0, set[propertyName][1] || 0] : undefined;
        }

        spec.hide = set.hide;

        if (set.skew || set.rotate || set.scale) {
            spec.transform = Transform.build({
                translate: set.translate || [0, 0, 0],
                skew: set.skew || [0, 0, 0],
                scale: set.scale || [1, 1, 1],
                rotate: set.rotate || [0, 0, 0]
            });
        }
        else {
            spec.transform = set.translate ?
                Transform.translate(set.translate[0], set.translate[1], set.translate[2]) :
                undefined;
        }
    };

    /**
     * Creates the render-spec
     */
    LayoutNode.prototype.getSpec = function () {
        this._specModified = false;
        this._spec.removed = !this._invalidated;
        return this._getFinalSpec();
    };

    /**
     * Gets the calculated spec based on the time passed, or what the last setting was
     * @returns {*}
     * @private
     */
    LayoutNode.prototype._getFinalSpec = function () {
        if (this._transitionable && !this._spec.hide) {
            if (!this._transitionable.isActive()) {
                delete this._transitionable;
            }
            var spec = {renderNode: this._spec.renderNode};
            this._modifySpecFromSet(spec, this._interpolateSet());
            return spec;
        }

        return this._spec;
    };

    var propertiesWithDefaults = {
        size: [0, 0],
        origin: [0, 0],
        align: [0, 0],
        skew: [0, 0, 0],
        rotate: [0, 0, 0],
        scale: [1, 1, 1],
        translate: [0, 0, 0],
        opacity: [0]
    };

    /**
     * Interpolates an ongoing animation
     * @returns {Object|*}
     * @private
     */
    LayoutNode.prototype._interpolateSet = function () {
        var tweenValue = this._transitionable.get();
        if (!this._originalSet || tweenValue === 1) {
            return this._lastSet;
        }
        var resultingSet = {};
        var tweenToSet = this._lastSet;
        var tweenFromSet = this._originalSet;

        for(var property in propertiesWithDefaults){
            var defaultProperty = propertiesWithDefaults[property];
            var tweenFrom = tweenFromSet[property] !== undefined ? tweenFromSet[property] : defaultProperty;
            var tweenTo = tweenToSet[property] !== undefined ? tweenToSet[property] : tweenFrom;
            if(tweenTo === tweenFrom){
                resultingSet[property] = tweenTo;
                continue;
            }
            tweenTo = Array.isArray(tweenTo) ? tweenTo : [tweenTo];
            tweenFrom = Array.isArray(tweenFrom) ? tweenFrom: [tweenFrom];
            this._specModified = true;
            var numberOfDimensions = defaultProperty.length;
            /* Clone the property so that we can start from the default setting */
            let tweenedSet = resultingSet[property] = new Array(numberOfDimensions);
            for(var dimension = 0;dimension<numberOfDimensions;dimension++){
                tweenedSet[dimension] = tweenFrom[dimension] + (tweenTo[dimension] - tweenFrom[dimension]) * tweenValue;
            }
        }
        resultingSet.opacity = resultingSet.opacity[0];
        return resultingSet;
    };
    /**
     * Marks the node for removal
     */
    LayoutNode.prototype.remove = function (removeSpec) {
        this._removing = true;
    };

    module.exports = LayoutNode;
});
<commit_msg>Fixed the defualt size to work again<commit_after>/**
 * This Source Code is licensed under the MIT license. If a copy of the
 * MIT-license was not distributed with this file, You can obtain one at:
 * http://opensource.org/licenses/mit-license.html.
 *
 * @author: Hein Rutjes (IjzerenHein)
 * @license MIT
 * @copyright Gloey Apps, 2014
 */

/**
 * Internal LayoutNode class used by `LayoutController`.
 *
 * @module
 */
define(function (require, exports, module) {

    var Transitionable = require('famous/transitions/Transitionable');
    var Transform = require('famous/core/Transform');
    var LayoutUtility = require('./LayoutUtility');

    /**
     * @class
     * @param {Object} renderNode Render-node which this layout-node represents
     * @alias module:LayoutNode
     */
    function LayoutNode(renderNode, spec) {
        this.renderNode = renderNode;
        this._spec = spec ? LayoutUtility.cloneSpec(spec) : {};
        this._spec.renderNode = renderNode; // also store in spec
        this._specModified = true;
        this._invalidated = false;
        this._removing = false;
    }

    /**
     * Called to update the underlying render-node
     */
    LayoutNode.prototype.setRenderNode = function (renderNode) {
        this.renderNode = renderNode;
        this._spec.renderNode = renderNode;
    };

    /**
     * Called to update the options for the node
     */
    LayoutNode.prototype.setOptions = function (options) {
        // override to implement
    };

    /**
     * Called when the node is destroyed
     */
    LayoutNode.prototype.destroy = function () {
        this.renderNode = undefined;
        this._spec.renderNode = undefined;
        this._viewSequence = undefined;
    };

    /**
     * Reset the end-state. This function is called on all layout-nodes prior to
     * calling the layout-function. So that the layout-function starts with a clean slate.
     */
    LayoutNode.prototype.reset = function () {
        this._invalidated = false;
        this.trueSizeRequested = false;
    };

    /**
     * Set the spec of the node
     *
     * @param {Object} spec
     */
    LayoutNode.prototype.setSpec = function (spec) {
        this._specModified = true;
        if (spec.align) {
            if (!spec.align) {
                this._spec.align = [0, 0];
            }
            this._spec.align[0] = spec.align[0];
            this._spec.align[1] = spec.align[1];
        }
        else {
            this._spec.align = undefined;
        }
        if (spec.origin) {
            if (!spec.origin) {
                this._spec.origin = [0, 0];
            }
            this._spec.origin[0] = spec.origin[0];
            this._spec.origin[1] = spec.origin[1];
        }
        else {
            this._spec.origin = undefined;
        }
        if (spec.size) {
            if (!spec.size) {
                this._spec.size = [0, 0];
            }
            this._spec.size[0] = spec.size[0];
            this._spec.size[1] = spec.size[1];
        }
        else {
            this._spec.size = undefined;
        }

        this._spec.hide = spec.hide;

        if (spec.transform) {
            if (!spec.transform) {
                this._spec.transform = spec.transform.slice(0);
            }
            else {
                for (var i = 0; i < 16; i++) {
                    this._spec.transform[i] = spec.transform[i];
                }
            }
        }
        else {
            this._spec.transform = undefined;
        }
        this._spec.opacity = spec.opacity;
    };

    /**
     * Initiates a transition on the layout node
     * @param transition
     * @param {Function} onComplete callback when complete
     */
    LayoutNode.prototype.transition = function (transition, onComplete) {

        this._originalSet = this._transitionable ?
            /* If there's already something in progress, then interpolate the current starting position */
            this._interpolateSet() :
            /* Otherwise, it's the most recent set */
            this._lastSet;
        let transitionable = this._transitionable = new Transitionable(0);
        transitionable.set(1, transition, () => {
            /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
            * because we are finished */
            if (this._transitionable === transitionable) {
                delete this._transitionable;
                this._originalSet = this._targetSet;
                if(onComplete){
                    onComplete();
                }
            }
        })
    };

        /**
     * Set the content of the node
     *
     * @param {Object} set
     */
    LayoutNode.prototype.set = function (set, size) {

        var transition = set.transition;

        if (transition) {
            this._targetSet = set;
            if (!this._transitionable) {
                let transitionable = this._transitionable = new Transitionable(0);
                transitionable.set(1, transition, () => {
                    /* If the transitionable wasn't replaced by another transitionable, then delete the transitionable
                    * because we are finished */
                    if (this._transitionable === transitionable) {
                        delete this._transitionable;
                        this._originalSet = this._targetSet;
                    }
                })
            }
        }
        if (!this._transitionable) {
            this._originalSet = set;
        }

        this._lastSet = set;
        this._invalidated = true;
        this._specModified = true;
        this._removing = false;
        this._modifySpecFromSet(this._spec, set);
        this.scrollLength = set.scrollLength;
    };

    var propertiesWithDefaults = {
        size: [undefined, undefined],
        origin: [0, 0],
        align: [0, 0],
        skew: [0, 0, 0],
        rotate: [0, 0, 0],
        scale: [1, 1, 1],
        translate: [0, 0, 0],
        opacity: [0]
    };

    LayoutNode.prototype._modifySpecFromSet = function (spec, set) {
        spec.opacity = set.opacity;

        var propertiesWithEasyDefaults = ['size', 'origin', 'align'];
        for (var i = 0; i < propertiesWithEasyDefaults.length; i++) {
            var propertyName = propertiesWithEasyDefaults[i];
            var defaultProperty = propertiesWithDefaults[propertyName];
            spec[propertyName] = set[propertyName] ? [set[propertyName][0] || defaultProperty[0], set[propertyName][1] || defaultProperty[1]] : undefined;
        }

        spec.hide = set.hide;

        if (set.skew || set.rotate || set.scale) {
            spec.transform = Transform.build({
                translate: set.translate || [0, 0, 0],
                skew: set.skew || [0, 0, 0],
                scale: set.scale || [1, 1, 1],
                rotate: set.rotate || [0, 0, 0]
            });
        }
        else {
            spec.transform = set.translate ?
                Transform.translate(set.translate[0], set.translate[1], set.translate[2]) :
                undefined;
        }
    };

    /**
     * Creates the render-spec
     */
    LayoutNode.prototype.getSpec = function () {
        this._specModified = false;
        this._spec.removed = !this._invalidated;
        return this._getFinalSpec();
    };

    /**
     * Gets the calculated spec based on the time passed, or what the last setting was
     * @returns {*}
     * @private
     */
    LayoutNode.prototype._getFinalSpec = function () {
        if (this._transitionable && !this._spec.hide) {
            if (!this._transitionable.isActive()) {
                delete this._transitionable;
            }
            var spec = {renderNode: this._spec.renderNode};
            this._modifySpecFromSet(spec, this._interpolateSet());
            return spec;
        }

        return this._spec;

    };

    /**
     * Interpolates an ongoing animation
     * @returns {Object|*}
     * @private
     */
    LayoutNode.prototype._interpolateSet = function () {
        var tweenValue = this._transitionable.get();
        if (!this._originalSet || tweenValue === 1) {
            return this._lastSet;
        }
        var resultingSet = {};
        var tweenToSet = this._lastSet;
        var tweenFromSet = this._originalSet;

        for(var property in propertiesWithDefaults){
            var defaultProperty = propertiesWithDefaults[property];
            var tweenFrom = tweenFromSet[property] !== undefined ? tweenFromSet[property] : defaultProperty;
            var tweenTo = tweenToSet[property] !== undefined ? tweenToSet[property] : tweenFrom;
            if(tweenTo === tweenFrom){
                resultingSet[property] = tweenTo;
                continue;
            }
            tweenTo = Array.isArray(tweenTo) ? tweenTo : [tweenTo];
            tweenFrom = Array.isArray(tweenFrom) ? tweenFrom: [tweenFrom];
            this._specModified = true;
            var numberOfDimensions = defaultProperty.length;
            /* Clone the property so that we can start from the default setting */
            let tweenedSet = resultingSet[property] = new Array(numberOfDimensions);
            for(var dimension = 0;dimension<numberOfDimensions;dimension++){
                tweenedSet[dimension] = tweenFrom[dimension] + (tweenTo[dimension] - tweenFrom[dimension]) * tweenValue;
            }
        }
        resultingSet.opacity = resultingSet.opacity[0];
        return resultingSet;
    };
    /**
     * Marks the node for removal
     */
    LayoutNode.prototype.remove = function (removeSpec) {
        this._removing = true;
    };

    module.exports = LayoutNode;
});
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import LogMonitorEntry from './LogMonitorEntry';
import LogMonitorButton from './LogMonitorButton';
import shouldPureComponentUpdate from 'react-pure-render/function';
import * as themes from 'redux-devtools-themes';
import { ActionCreators } from 'redux-devtools';
import { updateScrollTop } from './actions';
import reducer from './reducers';

const { reset, rollback, commit, sweep, toggleAction } = ActionCreators;

const styles = {
  container: {
    fontFamily: 'monaco, Consolas, Lucida Console, monospace',
    position: 'relative',
    overflowY: 'hidden',
    width: '100%',
    height: '100%',
    minWidth: 300,
    direction: 'ltr'
  },
  buttonBar: {
    textAlign: 'center',
    borderBottomWidth: 1,
    borderBottomStyle: 'solid',
    borderColor: 'transparent',
    zIndex: 1,
    display: 'flex',
    flexDirection: 'row'
  },
  elements: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 38,
    bottom: 0,
    overflowX: 'hidden',
    overflowY: 'auto'
  }
};

export default class LogMonitor extends Component {
  static update = reducer;

  static propTypes = {
    dispatch: PropTypes.func,
    computedStates: PropTypes.array,
    actionsById: PropTypes.object,
    stagedActionIds: PropTypes.array,
    skippedActionIds: PropTypes.array,
    monitorState: PropTypes.shape({
      initialScrollTop: PropTypes.number
    }),

    preserveScrollTop: PropTypes.bool,
    select: PropTypes.func.isRequired,
    theme: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.string
    ]),
    expandActionRoot: PropTypes.bool,
    expandStateRoot: PropTypes.bool
  };

  static defaultProps = {
    select: (state) => state,
    theme: 'nicinabox',
    preserveScrollTop: true,
    expandActionRoot: true,
    expandStateRoot: true
  };

  shouldComponentUpdate = shouldPureComponentUpdate;

  constructor(props) {
    super(props);
    this.handleToggleAction = this.handleToggleAction.bind(this);
    this.handleReset = this.handleReset.bind(this);
    this.handleRollback = this.handleRollback.bind(this);
    this.handleSweep = this.handleSweep.bind(this);
    this.handleCommit = this.handleCommit.bind(this);
  }

  scroll() {
    const node = this.refs.container;
    if (!node) {
      return;
    }
    if (this.scrollDown) {
      const { offsetHeight, scrollHeight } = node;
      node.scrollTop = scrollHeight - offsetHeight;
      this.scrollDown = false;
    }
  }

  componentDidMount() {
    const node = this.refs.container;
    if (!node) {
      return;
    }

    if (this.props.preserveScrollTop) {
      node.scrollTop = this.props.monitorState.initialScrollTop;
      this.interval = setInterval(::this.updateScrollTop, 1000);
    } else {
      this.scrollDown = true;
      this.scroll();
    }
  }

  componentWillUnmount() {
    if (this.interval) {
      clearInterval(this.interval);
    }
  }

  updateScrollTop() {
    const node = this.refs.container;
    this.props.dispatch(updateScrollTop(node ? node.scrollTop : 0));
  }

  componentWillReceiveProps(nextProps) {
    const node = this.refs.container;
    if (!node) {
      this.scrollDown = true;
    } else if (
      this.props.stagedActionIds.length <
      nextProps.stagedActionIds.length
    ) {
      const { scrollTop, offsetHeight, scrollHeight } = node;

      this.scrollDown = Math.abs(
        scrollHeight - (scrollTop + offsetHeight)
      ) < 20;
    } else {
      this.scrollDown = false;
    }
  }

  componentDidUpdate() {
    this.scroll();
  }

  handleRollback() {
    this.props.dispatch(rollback());
  }

  handleSweep() {
    this.props.dispatch(sweep());
  }

  handleCommit() {
    this.props.dispatch(commit());
  }

  handleToggleAction(id) {
    this.props.dispatch(toggleAction(id));
  }

  handleReset() {
    this.props.dispatch(reset());
  }

  getTheme() {
    let { theme } = this.props;
    if (typeof theme !== 'string') {
      return theme;
    }

    if (typeof themes[theme] !== 'undefined') {
      return themes[theme];
    }

    console.warn('DevTools theme ' + theme + ' not found, defaulting to nicinabox');
    return themes.nicinabox;
  }

  render() {
    const elements = [];
    const theme = this.getTheme();
    const { actionsById, skippedActionIds, stagedActionIds, computedStates, select } = this.props;

    for (let i = 0; i < stagedActionIds.length; i++) {
      const actionId = stagedActionIds[i];
      const action = actionsById[actionId].action;
      const { state, error } = computedStates[i];
      let previousState;
      if (i > 0) {
        previousState = computedStates[i - 1].state;
      }
      elements.push(
        <LogMonitorEntry key={actionId}
                         theme={theme}
                         select={select}
                         action={action}
                         actionId={actionId}
                         state={state}
                         previousState={previousState}
                         collapsed={skippedActionIds.indexOf(actionId) > -1}
                         error={error}
                         expandActionRoot={this.props.expandActionRoot}
                         expandStateRoot={this.props.expandStateRoot}
                         onActionClick={this.handleToggleAction} />
      );
    }

    return (

          <LogMonitorButton
            theme={theme}
            onClick={this.handleReset}
            enabled>
            Reset
          </LogMonitorButton>
          <LogMonitorButton
            theme={theme}
            onClick={this.handleRollback}
            enabled={computedStates.length > 1}>
            Revert
          </LogMonitorButton>
          <LogMonitorButton
            theme={theme}
            onClick={this.handleSweep}
            enabled={skippedActionIds.length > 0}>
            Sweep
          </LogMonitorButton>
          <LogMonitorButton
            theme={theme}
            onClick={this.handleCommit}
            enabled={computedStates.length > 1}>
            Commit
          </LogMonitorButton>

          {elements}

    );
  }
}
<commit_msg>Warn but not throw when the monitor is rendered but not included in the store configuration<commit_after>import React, { PropTypes, Component } from 'react';
import LogMonitorEntry from './LogMonitorEntry';
import LogMonitorButton from './LogMonitorButton';
import shouldPureComponentUpdate from 'react-pure-render/function';
import * as themes from 'redux-devtools-themes';
import { ActionCreators } from 'redux-devtools';
import { updateScrollTop } from './actions';
import reducer from './reducers';

const { reset, rollback, commit, sweep, toggleAction } = ActionCreators;

const styles = {
  container: {
    fontFamily: 'monaco, Consolas, Lucida Console, monospace',
    position: 'relative',
    overflowY: 'hidden',
    width: '100%',
    height: '100%',
    minWidth: 300,
    direction: 'ltr'
  },
  buttonBar: {
    textAlign: 'center',
    borderBottomWidth: 1,
    borderBottomStyle: 'solid',
    borderColor: 'transparent',
    zIndex: 1,
    display: 'flex',
    flexDirection: 'row'
  },
  elements: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 38,
    bottom: 0,
    overflowX: 'hidden',
    overflowY: 'auto'
  }
};

export default class LogMonitor extends Component {
  static update = reducer;

  static propTypes = {
    dispatch: PropTypes.func,
    computedStates: PropTypes.array,
    actionsById: PropTypes.object,
    stagedActionIds: PropTypes.array,
    skippedActionIds: PropTypes.array,
    monitorState: PropTypes.shape({
      initialScrollTop: PropTypes.number
    }).isRequired,

    preserveScrollTop: PropTypes.bool,
    select: PropTypes.func.isRequired,
    theme: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.string
    ]),
    expandActionRoot: PropTypes.bool,
    expandStateRoot: PropTypes.bool
  };

  static defaultProps = {
    select: (state) => state,
    theme: 'nicinabox',
    preserveScrollTop: true,
    expandActionRoot: true,
    expandStateRoot: true
  };

  shouldComponentUpdate = shouldPureComponentUpdate;

  constructor(props) {
    super(props);
    this.handleToggleAction = this.handleToggleAction.bind(this);
    this.handleReset = this.handleReset.bind(this);
    this.handleRollback = this.handleRollback.bind(this);
    this.handleSweep = this.handleSweep.bind(this);
    this.handleCommit = this.handleCommit.bind(this);
  }

  scroll() {
    const node = this.refs.container;
    if (!node) {
      return;
    }
    if (this.scrollDown) {
      const { offsetHeight, scrollHeight } = node;
      node.scrollTop = scrollHeight - offsetHeight;
      this.scrollDown = false;
    }
  }

  componentDidMount() {
    const node = this.refs.container;
    if (!node || !this.props.monitorState) {
      return;
    }

    if (this.props.preserveScrollTop) {
      node.scrollTop = this.props.monitorState.initialScrollTop;
      this.interval = setInterval(::this.updateScrollTop, 1000);
    } else {
      this.scrollDown = true;
      this.scroll();
    }
  }

  componentWillUnmount() {
    if (this.interval) {
      clearInterval(this.interval);
    }
  }

  updateScrollTop() {
    const node = this.refs.container;
    this.props.dispatch(updateScrollTop(node ? node.scrollTop : 0));
  }

  componentWillReceiveProps(nextProps) {
    const node = this.refs.container;
    if (!node) {
      this.scrollDown = true;
    } else if (
      this.props.stagedActionIds.length <
      nextProps.stagedActionIds.length
    ) {
      const { scrollTop, offsetHeight, scrollHeight } = node;

      this.scrollDown = Math.abs(
        scrollHeight - (scrollTop + offsetHeight)
      ) < 20;
    } else {
      this.scrollDown = false;
    }
  }

  componentDidUpdate() {
    this.scroll();
  }

  handleRollback() {
    this.props.dispatch(rollback());
  }

  handleSweep() {
    this.props.dispatch(sweep());
  }

  handleCommit() {
    this.props.dispatch(commit());
  }

  handleToggleAction(id) {
    this.props.dispatch(toggleAction(id));
  }

  handleReset() {
    this.props.dispatch(reset());
  }

  getTheme() {
    let { theme } = this.props;
    if (typeof theme !== 'string') {
      return theme;
    }

    if (typeof themes[theme] !== 'undefined') {
      return themes[theme];
    }

    console.warn('DevTools theme ' + theme + ' not found, defaulting to nicinabox');
    return themes.nicinabox;
  }

  render() {
    const elements = [];
    const theme = this.getTheme();
    const { actionsById, skippedActionIds, stagedActionIds, computedStates, select } = this.props;

    for (let i = 0; i < stagedActionIds.length; i++) {
      const actionId = stagedActionIds[i];
      const action = actionsById[actionId].action;
      const { state, error } = computedStates[i];
      let previousState;
      if (i > 0) {
        previousState = computedStates[i - 1].state;
      }
      elements.push(
        <LogMonitorEntry key={actionId}
                         theme={theme}
                         select={select}
                         action={action}
                         actionId={actionId}
                         state={state}
                         previousState={previousState}
                         collapsed={skippedActionIds.indexOf(actionId) > -1}
                         error={error}
                         expandActionRoot={this.props.expandActionRoot}
                         expandStateRoot={this.props.expandStateRoot}
                         onActionClick={this.handleToggleAction} />
      );
    }

    return (

          <LogMonitorButton
            theme={theme}
            onClick={this.handleReset}
            enabled>
            Reset
          </LogMonitorButton>
          <LogMonitorButton
            theme={theme}
            onClick={this.handleRollback}
            enabled={computedStates.length > 1}>
            Revert
          </LogMonitorButton>
          <LogMonitorButton
            theme={theme}
            onClick={this.handleSweep}
            enabled={skippedActionIds.length > 0}>
            Sweep
          </LogMonitorButton>
          <LogMonitorButton
            theme={theme}
            onClick={this.handleCommit}
            enabled={computedStates.length > 1}>
            Commit
          </LogMonitorButton>

          {elements}

    );
  }
}
<|endoftext|>


<commit_before>import ons from 'onsenui';
export var ngm;

// function (EVENTTYPE type, object content)
var eventHdlr;
var wsconn;

class WebSocketConn {
    send(jsonm) {
        wsconn.send(jsonm);
    }

    sendObj(obj) {
        wsconn.send( JSON.stringify(obj) );
    }

    connect(messageHandler) {
        if (!("WebSocket" in window)) {
            ons.notification.alert("WebSocket NOT supported by your Browser!");
        }

        wsconn = new WebSocket("ws://localhost:8753/ws");

        wsconn.onerror = (err) => {
            console.log(err);
            ons.notification.alert("connection error");
        };

        wsconn.onopen = () => {
            console.log("open");
        };

        this.remainMes = "";
        wsconn.onmessage = function (m) {
            let ms = m.data.split("\n");
            ms[0] = this.remainMes + ms[0];
            this.remainMes = ms.pop();
            ms.forEach(messageHandler, this);
        }.bind(this);

        wsconn.onclose = () => {
            console.log("close");
        };
    }
}

class Ngmconn {
    constructor(eventHandler) {
        this.ws = new WebSocketConn();
        eventHdlr = eventHandler;
    }

    handleMessage(jsonm) {
        eventHdlr(JSON.parse(jsonm));
    }

    connectWs() {
        this.ws.connect(this.handleMessage);
    }

    broadConnect(uri) {
        this.ws.sendObj(
            {
                "domain": "nagome_query",
                "command": "Broad.Connect",
                "content": {
                    "broad_id": uri
                }
            }
        );
    }

    broadDisconnect() {
        this.ws.sendObj(
            {
                "domain": "nagome_query",
                "command": "Broad.Disconnect"
            }
        );
    }
}

export var NagomeInit = (eventHandler) => {
    ngm = new Ngmconn(eventHandler);
};
<commit_msg>relative WS URI<commit_after>import ons from 'onsenui';
export var ngm;

// function (EVENTTYPE type, object content)
var eventHdlr;
var wsconn;

class WebSocketConn {
    send(jsonm) {
        wsconn.send(jsonm);
    }

    sendObj(obj) {
        wsconn.send( JSON.stringify(obj) );
    }

    connect(messageHandler) {
        if (!("WebSocket" in window)) {
            ons.notification.alert("WebSocket NOT supported by your Browser!");
        }

        // make absolute ws uri
        let loc = window.location, prot;
        if (loc.protocol === "https:") {
            prot = "wss:";
        } else {
            prot = "ws:";
        }
        wsconn = new WebSocket(`${prot}//${loc.host}/ws`);

        wsconn.onerror = (err) => {
            console.log(err);
            ons.notification.alert("connection error");
        };

        wsconn.onopen = () => {
            console.log("open");
        };

        this.remainMes = "";
        wsconn.onmessage = function (m) {
            let ms = m.data.split("\n");
            ms[0] = this.remainMes + ms[0];
            this.remainMes = ms.pop();
            ms.forEach(messageHandler, this);
        }.bind(this);

        wsconn.onclose = () => {
            console.log("close");
        };
    }
}

class Ngmconn {
    constructor(eventHandler) {
        this.ws = new WebSocketConn();
        eventHdlr = eventHandler;
    }

    handleMessage(jsonm) {
        eventHdlr(JSON.parse(jsonm));
    }

    connectWs() {
        this.ws.connect(this.handleMessage);
    }

    broadConnect(uri) {
        this.ws.sendObj(
            {
                "domain": "nagome_query",
                "command": "Broad.Connect",
                "content": {
                    "broad_id": uri
                }
            }
        );
    }

    broadDisconnect() {
        this.ws.sendObj(
            {
                "domain": "nagome_query",
                "command": "Broad.Disconnect"
            }
        );
    }
}

export var NagomeInit = (eventHandler) => {
    ngm = new Ngmconn(eventHandler);
};
<|endoftext|>


<commit_before>/*
Copyright 2016 OpenMarket Ltd
Copyright 2017, 2018 New Vector Ltd

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.
*/

import React from 'react';
import MatrixClientPeg from './MatrixClientPeg';
import MultiInviter from './utils/MultiInviter';
import Modal from './Modal';
import { getAddressType } from './UserAddress';
import createRoom from './createRoom';
import sdk from './';
import dis from './dispatcher';
import DMRoomMap from './utils/DMRoomMap';
import { _t } from './languageHandler';

/**
 * Invites multiple addresses to a room
 * Simpler interface to utils/MultiInviter but with
 * no option to cancel.
 *
 * @param {string} roomId The ID of the room to invite to
 * @param {string[]} addrs Array of strings of addresses to invite. May be matrix IDs or 3pids.
 * @returns {Promise} Promise
 */
function inviteMultipleToRoom(roomId, addrs) {
    const inviter = new MultiInviter(roomId);
    return inviter.invite(addrs).then(addrs => Promise.resolve({addrs, inviter}));
}

export function showStartChatInviteDialog() {
    const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
    Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
        title: _t('Start a chat'),
        description: _t("Who would you like to communicate with?"),
        placeholder: _t("Email, name or matrix ID"),
        validAddressTypes: ['mx-user-id', 'email'],
        button: _t("Start Chat"),
        onFinished: _onStartChatFinished,
    });
}

export function showRoomInviteDialog(roomId) {
    const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
    Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
        title: _t('Invite new room members'),
        description: _t('Who would you like to add to this room?'),
        button: _t('Send Invites'),
        placeholder: _t("Email, name or matrix ID"),
        onFinished: (shouldInvite, addrs) => {
            _onRoomInviteFinished(roomId, shouldInvite, addrs);
        },
    });
}

function _onStartChatFinished(shouldInvite, addrs) {
    if (!shouldInvite) return;

    const addrTexts = addrs.map((addr) => addr.address);

    if (_isDmChat(addrTexts)) {
        const rooms = _getDirectMessageRooms(addrTexts[0]);
        if (rooms.length > 0) {
            // A Direct Message room already exists for this user, so select a
            // room from a list that is similar to the one in MemberInfo panel
            const ChatCreateOrReuseDialog = sdk.getComponent("views.dialogs.ChatCreateOrReuseDialog");
            const close = Modal.createTrackedDialog('Create or Reuse', '', ChatCreateOrReuseDialog, {
                userId: addrTexts[0],
                onNewDMClick: () => {
                    dis.dispatch({
                        action: 'start_chat',
                        user_id: addrTexts[0],
                    });
                    close(true);
                },
                onExistingRoomSelected: (roomId) => {
                    dis.dispatch({
                        action: 'view_room',
                        room_id: roomId,
                    });
                    close(true);
                },
            }).close;
        } else {
            // Start a new DM chat
            createRoom({dmUserId: addrTexts[0]}).catch((err) => {
                const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
                    title: _t("Failed to invite user"),
                    description: ((err && err.message) ? err.message : _t("Operation failed")),
                });
            });
        }
    } else if (addrTexts.length === 1) {
        // Start a new DM chat
        createRoom({dmUserId: addrTexts[0]}).catch((err) => {
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
                title: _t("Failed to invite user"),
                description: ((err && err.message) ? err.message : _t("Operation failed")),
            });
        });
    } else {
        // Start multi user chat
        let room;
        createRoom().then((roomId) => {
            room = MatrixClientPeg.get().getRoom(roomId);
            return inviteMultipleToRoom(roomId, addrTexts);
        }).then((result) => {
            return _showAnyInviteErrors(result.addrs, room, result.inviter);
        }).catch((err) => {
            console.error(err.stack);
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
                title: _t("Failed to invite"),
                description: ((err && err.message) ? err.message : _t("Operation failed")),
            });
        });
    }
}

function _onRoomInviteFinished(roomId, shouldInvite, addrs) {
    if (!shouldInvite) return;

    const addrTexts = addrs.map((addr) => addr.address);

    // Invite new users to a room
    inviteMultipleToRoom(roomId, addrTexts).then((result) => {
        const room = MatrixClientPeg.get().getRoom(roomId);
        return _showAnyInviteErrors(result.addrs, room, result.inviter);
    }).catch((err) => {
        console.error(err.stack);
        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
            title: _t("Failed to invite"),
            description: ((err && err.message) ? err.message : _t("Operation failed")),
        });
    });
}

function _isDmChat(addrTexts) {
    if (addrTexts.length === 1 && getAddressType(addrTexts[0]) === 'mx-user-id') {
        return true;
    } else {
        return false;
    }
}

function _showAnyInviteErrors(addrs, room, inviter) {
    // Show user any errors
    const failedUsers = Object.keys(addrs).filter(a => addrs[a] === 'error');
    if (failedUsers.length === 1 && inviter.fatal) {
        // Just get the first message because there was a fatal problem on the first
        // user. This usually means that no other users were attempted, making it
        // pointless for us to list who failed exactly.
        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, {
            title: _t("Failed to invite users to the room:", {roomName: room.name}),
            description: inviter.getErrorText(failedUsers[0]),
        });
    } else {
        const errorList = [];
        for (const addr of failedUsers) {
            if (addrs[addr] === "error") {
                const reason = inviter.getErrorText(addr);
                errorList.push(addr + ": " + reason);
            }
        }

        if (errorList.length > 0) {
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to invite the following users to the room', '', ErrorDialog, {
                title: _t("Failed to invite the following users to the %(roomName)s room:", {roomName: room.name}),
                description: errorList.join(
),
            });
        }
    }

    return addrs;
}

function _getDirectMessageRooms(addr) {
    const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
    const dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
    const rooms = dmRooms.filter((dmRoom) => {
        const room = MatrixClientPeg.get().getRoom(dmRoom);
        if (room) {
            return room.getMyMembership() === 'join';
        }
    });
    return rooms;
}

<commit_msg>Rename returned completion states to be something sensible<commit_after>/*
Copyright 2016 OpenMarket Ltd
Copyright 2017, 2018 New Vector Ltd

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.
*/

import React from 'react';
import MatrixClientPeg from './MatrixClientPeg';
import MultiInviter from './utils/MultiInviter';
import Modal from './Modal';
import { getAddressType } from './UserAddress';
import createRoom from './createRoom';
import sdk from './';
import dis from './dispatcher';
import DMRoomMap from './utils/DMRoomMap';
import { _t } from './languageHandler';

/**
 * Invites multiple addresses to a room
 * Simpler interface to utils/MultiInviter but with
 * no option to cancel.
 *
 * @param {string} roomId The ID of the room to invite to
 * @param {string[]} addrs Array of strings of addresses to invite. May be matrix IDs or 3pids.
 * @returns {Promise} Promise
 */
function inviteMultipleToRoom(roomId, addrs) {
    const inviter = new MultiInviter(roomId);
    return inviter.invite(addrs).then(states => Promise.resolve({states, inviter}));
}

export function showStartChatInviteDialog() {
    const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
    Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
        title: _t('Start a chat'),
        description: _t("Who would you like to communicate with?"),
        placeholder: _t("Email, name or matrix ID"),
        validAddressTypes: ['mx-user-id', 'email'],
        button: _t("Start Chat"),
        onFinished: _onStartChatFinished,
    });
}

export function showRoomInviteDialog(roomId) {
    const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
    Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
        title: _t('Invite new room members'),
        description: _t('Who would you like to add to this room?'),
        button: _t('Send Invites'),
        placeholder: _t("Email, name or matrix ID"),
        onFinished: (shouldInvite, addrs) => {
            _onRoomInviteFinished(roomId, shouldInvite, addrs);
        },
    });
}

function _onStartChatFinished(shouldInvite, addrs) {
    if (!shouldInvite) return;

    const addrTexts = addrs.map((addr) => addr.address);

    if (_isDmChat(addrTexts)) {
        const rooms = _getDirectMessageRooms(addrTexts[0]);
        if (rooms.length > 0) {
            // A Direct Message room already exists for this user, so select a
            // room from a list that is similar to the one in MemberInfo panel
            const ChatCreateOrReuseDialog = sdk.getComponent("views.dialogs.ChatCreateOrReuseDialog");
            const close = Modal.createTrackedDialog('Create or Reuse', '', ChatCreateOrReuseDialog, {
                userId: addrTexts[0],
                onNewDMClick: () => {
                    dis.dispatch({
                        action: 'start_chat',
                        user_id: addrTexts[0],
                    });
                    close(true);
                },
                onExistingRoomSelected: (roomId) => {
                    dis.dispatch({
                        action: 'view_room',
                        room_id: roomId,
                    });
                    close(true);
                },
            }).close;
        } else {
            // Start a new DM chat
            createRoom({dmUserId: addrTexts[0]}).catch((err) => {
                const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
                Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
                    title: _t("Failed to invite user"),
                    description: ((err && err.message) ? err.message : _t("Operation failed")),
                });
            });
        }
    } else if (addrTexts.length === 1) {
        // Start a new DM chat
        createRoom({dmUserId: addrTexts[0]}).catch((err) => {
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
                title: _t("Failed to invite user"),
                description: ((err && err.message) ? err.message : _t("Operation failed")),
            });
        });
    } else {
        // Start multi user chat
        let room;
        createRoom().then((roomId) => {
            room = MatrixClientPeg.get().getRoom(roomId);
            return inviteMultipleToRoom(roomId, addrTexts);
        }).then((result) => {
            return _showAnyInviteErrors(result.states, room, result.inviter);
        }).catch((err) => {
            console.error(err.stack);
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
                title: _t("Failed to invite"),
                description: ((err && err.message) ? err.message : _t("Operation failed")),
            });
        });
    }
}

function _onRoomInviteFinished(roomId, shouldInvite, addrs) {
    if (!shouldInvite) return;

    const addrTexts = addrs.map((addr) => addr.address);

    // Invite new users to a room
    inviteMultipleToRoom(roomId, addrTexts).then((result) => {
        const room = MatrixClientPeg.get().getRoom(roomId);
        return _showAnyInviteErrors(result.states, room, result.inviter);
    }).catch((err) => {
        console.error(err.stack);
        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
            title: _t("Failed to invite"),
            description: ((err && err.message) ? err.message : _t("Operation failed")),
        });
    });
}

function _isDmChat(addrTexts) {
    if (addrTexts.length === 1 && getAddressType(addrTexts[0]) === 'mx-user-id') {
        return true;
    } else {
        return false;
    }
}

function _showAnyInviteErrors(addrs, room, inviter) {
    // Show user any errors
    const failedUsers = Object.keys(addrs).filter(a => addrs[a] === 'error');
    if (failedUsers.length === 1 && inviter.fatal) {
        // Just get the first message because there was a fatal problem on the first
        // user. This usually means that no other users were attempted, making it
        // pointless for us to list who failed exactly.
        const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
        Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, {
            title: _t("Failed to invite users to the room:", {roomName: room.name}),
            description: inviter.getErrorText(failedUsers[0]),
        });
    } else {
        const errorList = [];
        for (const addr of failedUsers) {
            if (addrs[addr] === "error") {
                const reason = inviter.getErrorText(addr);
                errorList.push(addr + ": " + reason);
            }
        }

        if (errorList.length > 0) {
            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
            Modal.createTrackedDialog('Failed to invite the following users to the room', '', ErrorDialog, {
                title: _t("Failed to invite the following users to the %(roomName)s room:", {roomName: room.name}),
                description: errorList.join(
),
            });
        }
    }

    return addrs;
}

function _getDirectMessageRooms(addr) {
    const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
    const dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
    const rooms = dmRooms.filter((dmRoom) => {
        const room = MatrixClientPeg.get().getRoom(dmRoom);
        if (room) {
            return room.getMyMembership() === 'join';
        }
    });
    return rooms;
}

<|endoftext|>


<commit_before>
/**
 * @fileoverview SIP User Agent
 */

/**
 * @augments JsSIP
 * @class Class for outgoing SIP request.
 * @param {String} method request method
 * @param {String} ruri request uri
 * @param {JsSIP.UA} ua
 * @param {Object} params parameters that will have priority over ua.configuration parameters:
 *

 *  - cseq, call_id, from_tag, from_uri, from_display_name, to_uri, to_tag, route_set
 * @param {Object} [headers] extra headers
 * @param {String} [body]
 */
JsSIP.OutgoingRequest = function(method, ruri, ua, params, extraHeaders, body) {
  var
    to_display_name,
    to_uri,
    to_tag,
    to,
    from_display_name,
    from_uri,
    from_tag,
    from,
    call_id,
    cseq,
    header;

  params = params || {};

  // Mandatory parameters check
  if(!method || !ruri || !ua) {
    return null;
  }

  this.headers = {};
  this.method = method;
  this.ruri = ruri;
  this.body = body;
  this.extraHeaders = extraHeaders || [];

  // Fill the Common SIP Request Headers

  //ROUTE
  if (params.route_set) {
    this.setHeader('route', params.route_set);
  } else {
    this.setHeader('route', ua.transport.server.sip_uri);
  }

  // VIA
  // Empty Via header. Will be filled by the client transaction
  this.setHeader('via', '');

  //MAX-FORWARDS
  this.setHeader('max-forwards', JsSIP.c.MAX_FORWARDS);

  //TO
  to_display_name = params.to_display_name ? '"' + params.to_display_name + '" ' : '';
  to_uri = params.to_uri || ruri;
  to_tag = params.to_tag ? ';tag=' + params.to_tag : '';
  to = to_display_name ? '<' + to_uri + '>' : to_uri;
  to += to_tag;
  this.setHeader('to', to);

  //FROM
  from_display_name = params.from_display_name || ua.configuration.display_name || '';
  from_uri = params.from_uri || ua.configuration.from_uri;
  from_tag = params.from_tag || JsSIP.utils.newTag();
  from = from_display_name ? '"' + from_display_name + '" ' : '';
  from += from_display_name ? '<' + from_uri + '>' : from_uri;
  from += ';tag=' + from_tag;
  this.setHeader('from', from);

  //CALL-ID
  if(params.call_id) {
    call_id = params.call_id;
  } else {
    call_id = ua.configuration.jssip_id + Math.random().toString(36).substr(2, 15);
  }
  this.setHeader('call-id', call_id);

  //CSEQ
  cseq = params.cseq || Math.floor(Math.random() * 10000);
  cseq = cseq + ' ' + method;
  this.setHeader('cseq', cseq);
};

JsSIP.OutgoingRequest.prototype = {
  /**
   * Replace the the given header by the given value.
   * @param {String} name header name
   * @param {String | Array} value header value
   */
  setHeader: function(name, value) {
    this.headers[JsSIP.utils.headerize(name)] = (value instanceof Array) ? value : [value];
  },
  toString: function() {
    var msg = '', header, length, idx;

    msg += this.method + ' ' + this.ruri + ' SIP/2.0\r\n';

    for(header in this.headers) {
      for(idx in this.headers[header]) {
        msg += header + ': ' + this.headers[header][idx] + '\r\n';
      }
    }

    length = this.extraHeaders.length;
    for(idx=0; idx < length; idx++) {
      msg += this.extraHeaders[idx] +'\r\n';
    }

    msg += 'Supported: ' +  JsSIP.c.SUPPORTED +'\r\n';
    msg += 'User-Agent: ' + JsSIP.c.USER_AGENT +'\r\n';

    if(this.body) {
      length = JsSIP.utils.str_utf8_length(this.body);
      msg += 'Content-Length: ' + length + '\r\n\r\n';
      msg += this.body;
    } else {
      msg += 'Content-Length: ' + 0 + '\r\n\r\n';
    }

    return msg;
  }
};

/**
 * @augments JsSIP
 * @class Class for incoming SIP message.
 */
JsSIP.IncomingMessage = function(){
  this.headers = null;
  this.method =  null;
  this.via = null;
  this.via_branch = null;
  this.call_id = null;
  this.cseq = null;
  this.from = null;
  this.from_tag = null;
  this.to = null;
  this.to_tag = null;
  this.body = null;
};

JsSIP.IncomingMessage.prototype = {
  /**
  * Insert a header of the given name and value into the last possition of the
  * header array.
  * @param {String} name header name
  * @param {String} value header value
  */
  addHeader: function(name, value) {
    var header = { raw: value };

    name = JsSIP.utils.headerize(name);

    if(this.headers[name]) {
      this.headers[name].push(header);
    } else {
      this.headers[name] = [header];
    }
  },

  /**
   * Count the number of headers of the given header name.
   * @param {String} name header name
   * @returns {Number} Number of headers with the given name
   */
  countHeader: function(name) {
    var header = this.headers[JsSIP.utils.headerize(name)];

    if(header) {
      return header.length;
    } else {
      return 0;
    }
  },

  /**
   * Get the value of the given header name at the given possition.
   * @param {String} name header name
   * @param {Number} [idx=0] header index
   * @returns {String|undefined} Returns the specified header, null if header doesn't exist.
   */
  getHeader: function(name, idx) {
    var header = this.headers[JsSIP.utils.headerize(name)];

    idx = idx || 0;

    if(header) {
      if(header[idx]) {
        return header[idx].raw;
      }
    } else {
      return;
    }
  },

  /**
   * Get the header/s of the given name.
   * @param {String} name header name
   * @returns {Array} Array with all the headers of the specified name.
   */
  getHeaderAll: function(name) {
    var idx,
      header = this.headers[JsSIP.utils.headerize(name)],
      result = [];

    if(!header) {
      return [];
    }

    for(idx in header) {
      result.push(header[idx].raw);
    }

    return result;
  },

  /**
   * Get the URI value of the given header at the given value.
   * @param {String} name header name
   * @param {Number} [idx=0] header index
   * @returns {String|undefined} uri attribute of the header. null if header or uri doesn't exist.
   */
  getHeaderUri: function(name, idx) {
    var header = this.headers[JsSIP.utils.headerize(name)];

    idx = idx || 0;

    if(header) {
      if(header[idx] && header[idx].parsed && header[idx].parsed.uri) {
        return header[idx].parsed.uri;
      }
    } else {
      return;
    }
  },

  /**
   * Verify the existence of the given header.
   * @param {String} name header name
   * @returns {boolean} true if header with given name exists, false otherwise
   */
  hasHeader: function(name) {
    return(this.headers[JsSIP.utils.headerize(name)]) ? true : false;
  },

  /**
  * Parse the given header on the given index.
  * @param {String} name header name
  * @param {Number} [idx=0] header index
  * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
  */
  parseHeader: function(name, idx) {
    var header, value, parsed;

    name = JsSIP.utils.headerize(name);

    idx = idx || 0;

    if(!this.headers[name]) {
      console.info(JsSIP.c.LOG_MESSAGE +'Header "' + name + '" not present');
      return;
    } else if(idx >= this.headers[name].length) {
      console.info(JsSIP.c.LOG_MESSAGE +'Not so many "' + name + '" headers present');
      return;
    }

    header = this.headers[name][idx];
    value = header.raw;

    if(header.parsed) {
      return header.parsed;
    }

    //substitute '-' by '_' for grammar rule matching.
    name = name.replace(/-/g, '_');
    parsed = JsSIP.grammar_sip.parse(value, name);

    if(parsed === -1) {
      this.headers[name].splice(idx, 1); //delete from headers
      console.error(JsSIP.c.LOG_MESSAGE +'Error parsing Header ' + name + ':"' + value + '"');
      return;
    } else {
      header.parsed = parsed;
      return parsed;
    }
  },

  /**
   * Message Header attribute selector. Alias of parseHeader.
   * @param {String} name header name
   * @param {Number} [idx=0] header index
   * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
   *
   * @example
   * message.s('via',3).port
   */
  s: function(name, idx) {
    return this.parseHeader(name, idx);
  },

  /**
  * Replace the value of the given header by the value.
  * @param {String} name header name
  * @param {String} value header value
  */
  setHeader: function(name, value) {
    var header = { raw: value };
    this.headers[JsSIP.utils.headerize(name)] = [header];
  }
};

/**
 * @augments JsSIP.IncomingMessage
 * @class Class for incoming SIP request.
 */
JsSIP.IncomingRequest = (function() {

  var IncomingRequest = function() {
    this.headers = {};
    this.ruri = null;
    this.transport = null;
    this.server_transaction = null;
  };
  IncomingRequest.prototype = new JsSIP.IncomingMessage();

  /**
  * Stateful reply.
  * @param {Number} code status code
  * @param {String} reason reason phrase
  * @param {Object} headers extra headers
  * @param {String} body body
  * @param {Function} [onSuccess] onSuccess callback
  * @param {Function} [onFailure] onFailure callback
  */
  IncomingRequest.prototype.reply = function(code, reason, extraHeaders, body, onSuccess, onFailure) {
    var rr, vias, header, length, idx,
      extraHeaders = extraHeaders || [],
      response = 'SIP/2.0 ' + code + ' ' + reason + '\r\n',
      to = this.to,
      r = 0,
      v = 0;

    if(this.method === JsSIP.c.INVITE && code > 100 && code <= 200) {
      rr = this.countHeader('record-route');

      for(r; r < rr; r++) {
        response += 'Record-Route: ' + this.getHeader('record-route', r) + '\r\n';
      }
    }

    vias = this.countHeader('via');

    for(v; v < vias; v++) {
      response += 'Via: ' + this.getHeader('via', v) + '\r\n';
    }

    response += 'Max-Forwards: ' + JsSIP.c.MAX_FORWARDS + '\r\n';

    if(code !== 100 && !this.to_tag) {
      to += ';tag=' + JsSIP.utils.newTag();
    } else if(this.to_tag && !this.s('to').tag) {
      to += ';tag=' + this.to_tag;
    }

    response += 'To: ' + to + '\r\n';
    response += 'From: ' + this.from + '\r\n';
    response += 'Call-ID: ' + this.call_id + '\r\n';
    response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n';

    length = extraHeaders.length;
    for(idx=0; idx < length; idx++) {
      response += extraHeaders[idx] +'\r\n';
    }

    if(body) {
      length = JsSIP.utils.str_utf8_length(body);
      response += 'Content-Type: application/sdp\r\n';
      response += 'Content-Length: ' + length + '\r\n\r\n';
      response += body;
    } else {
      response += "\r\n";
    }

    this.server_transaction.receiveResponse(code, response, onSuccess, onFailure);
  };

  /**
  * Stateless reply.
  * @param {Number} code status code
  * @param {String} reason reason phrase
  */
  IncomingRequest.prototype.reply_sl = function(code, reason) {
    var to,
      response = 'SIP/2.0 ' + code + ' ' + reason + '\r\n',
      vias = this.countHeader('via');

    for(var v = 0; v < vias; v++) {
      response += 'Via: ' + this.getHeader('via', v) + '\r\n';
    }

    to = this.to;

    if(!this.to_tag) {
      to += ';tag=' + JsSIP.utils.newTag();
    }

    response += 'To: ' + to + '\r\n';
    response += 'From: ' + this.from + '\r\n';
    response += 'Call-ID: ' + this.call_id + '\r\n';
    response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n\r\n';

    this.transport.send(response);
  };

  return IncomingRequest;
}());

/**
 * @augments JsSIP.IncomingMessage
 * @class Class for incoming SIP response.
 */
JsSIP.IncomingResponse = (function() {
  var IncomingResponse = function() {
    this.headers = {};
    this.response_code = null;
    this.reason_phrase = null;
  };
  IncomingResponse.prototype = new JsSIP.IncomingMessage();

  return IncomingResponse;
}());<commit_msg>BugFix. Do not redefine an already defined variable 'extraHeaders' in IncomingRequest.reply().<commit_after>
/**
 * @fileoverview SIP User Agent
 */

/**
 * @augments JsSIP
 * @class Class for outgoing SIP request.
 * @param {String} method request method
 * @param {String} ruri request uri
 * @param {JsSIP.UA} ua
 * @param {Object} params parameters that will have priority over ua.configuration parameters:
 *

 *  - cseq, call_id, from_tag, from_uri, from_display_name, to_uri, to_tag, route_set
 * @param {Object} [headers] extra headers
 * @param {String} [body]
 */
JsSIP.OutgoingRequest = function(method, ruri, ua, params, extraHeaders, body) {
  var
    to_display_name,
    to_uri,
    to_tag,
    to,
    from_display_name,
    from_uri,
    from_tag,
    from,
    call_id,
    cseq,
    header;

  params = params || {};

  // Mandatory parameters check
  if(!method || !ruri || !ua) {
    return null;
  }

  this.headers = {};
  this.method = method;
  this.ruri = ruri;
  this.body = body;
  this.extraHeaders = extraHeaders || [];

  // Fill the Common SIP Request Headers

  //ROUTE
  if (params.route_set) {
    this.setHeader('route', params.route_set);
  } else {
    this.setHeader('route', ua.transport.server.sip_uri);
  }

  // VIA
  // Empty Via header. Will be filled by the client transaction
  this.setHeader('via', '');

  //MAX-FORWARDS
  this.setHeader('max-forwards', JsSIP.c.MAX_FORWARDS);

  //TO
  to_display_name = params.to_display_name ? '"' + params.to_display_name + '" ' : '';
  to_uri = params.to_uri || ruri;
  to_tag = params.to_tag ? ';tag=' + params.to_tag : '';
  to = to_display_name ? '<' + to_uri + '>' : to_uri;
  to += to_tag;
  this.setHeader('to', to);

  //FROM
  from_display_name = params.from_display_name || ua.configuration.display_name || '';
  from_uri = params.from_uri || ua.configuration.from_uri;
  from_tag = params.from_tag || JsSIP.utils.newTag();
  from = from_display_name ? '"' + from_display_name + '" ' : '';
  from += from_display_name ? '<' + from_uri + '>' : from_uri;
  from += ';tag=' + from_tag;
  this.setHeader('from', from);

  //CALL-ID
  if(params.call_id) {
    call_id = params.call_id;
  } else {
    call_id = ua.configuration.jssip_id + Math.random().toString(36).substr(2, 15);
  }
  this.setHeader('call-id', call_id);

  //CSEQ
  cseq = params.cseq || Math.floor(Math.random() * 10000);
  cseq = cseq + ' ' + method;
  this.setHeader('cseq', cseq);
};

JsSIP.OutgoingRequest.prototype = {
  /**
   * Replace the the given header by the given value.
   * @param {String} name header name
   * @param {String | Array} value header value
   */
  setHeader: function(name, value) {
    this.headers[JsSIP.utils.headerize(name)] = (value instanceof Array) ? value : [value];
  },
  toString: function() {
    var msg = '', header, length, idx;

    msg += this.method + ' ' + this.ruri + ' SIP/2.0\r\n';

    for(header in this.headers) {
      for(idx in this.headers[header]) {
        msg += header + ': ' + this.headers[header][idx] + '\r\n';
      }
    }

    length = this.extraHeaders.length;
    for(idx=0; idx < length; idx++) {
      msg += this.extraHeaders[idx] +'\r\n';
    }

    msg += 'Supported: ' +  JsSIP.c.SUPPORTED +'\r\n';
    msg += 'User-Agent: ' + JsSIP.c.USER_AGENT +'\r\n';

    if(this.body) {
      length = JsSIP.utils.str_utf8_length(this.body);
      msg += 'Content-Length: ' + length + '\r\n\r\n';
      msg += this.body;
    } else {
      msg += 'Content-Length: ' + 0 + '\r\n\r\n';
    }

    return msg;
  }
};

/**
 * @augments JsSIP
 * @class Class for incoming SIP message.
 */
JsSIP.IncomingMessage = function(){
  this.headers = null;
  this.method =  null;
  this.via = null;
  this.via_branch = null;
  this.call_id = null;
  this.cseq = null;
  this.from = null;
  this.from_tag = null;
  this.to = null;
  this.to_tag = null;
  this.body = null;
};

JsSIP.IncomingMessage.prototype = {
  /**
  * Insert a header of the given name and value into the last possition of the
  * header array.
  * @param {String} name header name
  * @param {String} value header value
  */
  addHeader: function(name, value) {
    var header = { raw: value };

    name = JsSIP.utils.headerize(name);

    if(this.headers[name]) {
      this.headers[name].push(header);
    } else {
      this.headers[name] = [header];
    }
  },

  /**
   * Count the number of headers of the given header name.
   * @param {String} name header name
   * @returns {Number} Number of headers with the given name
   */
  countHeader: function(name) {
    var header = this.headers[JsSIP.utils.headerize(name)];

    if(header) {
      return header.length;
    } else {
      return 0;
    }
  },

  /**
   * Get the value of the given header name at the given possition.
   * @param {String} name header name
   * @param {Number} [idx=0] header index
   * @returns {String|undefined} Returns the specified header, null if header doesn't exist.
   */
  getHeader: function(name, idx) {
    var header = this.headers[JsSIP.utils.headerize(name)];

    idx = idx || 0;

    if(header) {
      if(header[idx]) {
        return header[idx].raw;
      }
    } else {
      return;
    }
  },

  /**
   * Get the header/s of the given name.
   * @param {String} name header name
   * @returns {Array} Array with all the headers of the specified name.
   */
  getHeaderAll: function(name) {
    var idx,
      header = this.headers[JsSIP.utils.headerize(name)],
      result = [];

    if(!header) {
      return [];
    }

    for(idx in header) {
      result.push(header[idx].raw);
    }

    return result;
  },

  /**
   * Get the URI value of the given header at the given value.
   * @param {String} name header name
   * @param {Number} [idx=0] header index
   * @returns {String|undefined} uri attribute of the header. null if header or uri doesn't exist.
   */
  getHeaderUri: function(name, idx) {
    var header = this.headers[JsSIP.utils.headerize(name)];

    idx = idx || 0;

    if(header) {
      if(header[idx] && header[idx].parsed && header[idx].parsed.uri) {
        return header[idx].parsed.uri;
      }
    } else {
      return;
    }
  },

  /**
   * Verify the existence of the given header.
   * @param {String} name header name
   * @returns {boolean} true if header with given name exists, false otherwise
   */
  hasHeader: function(name) {
    return(this.headers[JsSIP.utils.headerize(name)]) ? true : false;
  },

  /**
  * Parse the given header on the given index.
  * @param {String} name header name
  * @param {Number} [idx=0] header index
  * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
  */
  parseHeader: function(name, idx) {
    var header, value, parsed;

    name = JsSIP.utils.headerize(name);

    idx = idx || 0;

    if(!this.headers[name]) {
      console.info(JsSIP.c.LOG_MESSAGE +'Header "' + name + '" not present');
      return;
    } else if(idx >= this.headers[name].length) {
      console.info(JsSIP.c.LOG_MESSAGE +'Not so many "' + name + '" headers present');
      return;
    }

    header = this.headers[name][idx];
    value = header.raw;

    if(header.parsed) {
      return header.parsed;
    }

    //substitute '-' by '_' for grammar rule matching.
    name = name.replace(/-/g, '_');
    parsed = JsSIP.grammar_sip.parse(value, name);

    if(parsed === -1) {
      this.headers[name].splice(idx, 1); //delete from headers
      console.error(JsSIP.c.LOG_MESSAGE +'Error parsing Header ' + name + ':"' + value + '"');
      return;
    } else {
      header.parsed = parsed;
      return parsed;
    }
  },

  /**
   * Message Header attribute selector. Alias of parseHeader.
   * @param {String} name header name
   * @param {Number} [idx=0] header index
   * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
   *
   * @example
   * message.s('via',3).port
   */
  s: function(name, idx) {
    return this.parseHeader(name, idx);
  },

  /**
  * Replace the value of the given header by the value.
  * @param {String} name header name
  * @param {String} value header value
  */
  setHeader: function(name, value) {
    var header = { raw: value };
    this.headers[JsSIP.utils.headerize(name)] = [header];
  }
};

/**
 * @augments JsSIP.IncomingMessage
 * @class Class for incoming SIP request.
 */
JsSIP.IncomingRequest = (function() {

  var IncomingRequest = function() {
    this.headers = {};
    this.ruri = null;
    this.transport = null;
    this.server_transaction = null;
  };
  IncomingRequest.prototype = new JsSIP.IncomingMessage();

  /**
  * Stateful reply.
  * @param {Number} code status code
  * @param {String} reason reason phrase
  * @param {Object} headers extra headers
  * @param {String} body body
  * @param {Function} [onSuccess] onSuccess callback
  * @param {Function} [onFailure] onFailure callback
  */
  IncomingRequest.prototype.reply = function(code, reason, extraHeaders, body, onSuccess, onFailure) {
    var rr, vias, header, length, idx,
      response = 'SIP/2.0 ' + code + ' ' + reason + '\r\n',
      to = this.to,
      r = 0,
      v = 0;

    extraHeaders = extraHeaders || [];

    if(this.method === JsSIP.c.INVITE && code > 100 && code <= 200) {
      rr = this.countHeader('record-route');

      for(r; r < rr; r++) {
        response += 'Record-Route: ' + this.getHeader('record-route', r) + '\r\n';
      }
    }

    vias = this.countHeader('via');

    for(v; v < vias; v++) {
      response += 'Via: ' + this.getHeader('via', v) + '\r\n';
    }

    response += 'Max-Forwards: ' + JsSIP.c.MAX_FORWARDS + '\r\n';

    if(code !== 100 && !this.to_tag) {
      to += ';tag=' + JsSIP.utils.newTag();
    } else if(this.to_tag && !this.s('to').tag) {
      to += ';tag=' + this.to_tag;
    }

    response += 'To: ' + to + '\r\n';
    response += 'From: ' + this.from + '\r\n';
    response += 'Call-ID: ' + this.call_id + '\r\n';
    response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n';

    length = extraHeaders.length;
    for(idx=0; idx < length; idx++) {
      response += extraHeaders[idx] +'\r\n';
    }

    if(body) {
      length = JsSIP.utils.str_utf8_length(body);
      response += 'Content-Type: application/sdp\r\n';
      response += 'Content-Length: ' + length + '\r\n\r\n';
      response += body;
    } else {
      response += "\r\n";
    }

    this.server_transaction.receiveResponse(code, response, onSuccess, onFailure);
  };

  /**
  * Stateless reply.
  * @param {Number} code status code
  * @param {String} reason reason phrase
  */
  IncomingRequest.prototype.reply_sl = function(code, reason) {
    var to,
      response = 'SIP/2.0 ' + code + ' ' + reason + '\r\n',
      vias = this.countHeader('via');

    for(var v = 0; v < vias; v++) {
      response += 'Via: ' + this.getHeader('via', v) + '\r\n';
    }

    to = this.to;

    if(!this.to_tag) {
      to += ';tag=' + JsSIP.utils.newTag();
    }

    response += 'To: ' + to + '\r\n';
    response += 'From: ' + this.from + '\r\n';
    response += 'Call-ID: ' + this.call_id + '\r\n';
    response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n\r\n';

    this.transport.send(response);
  };

  return IncomingRequest;
}());

/**
 * @augments JsSIP.IncomingMessage
 * @class Class for incoming SIP response.
 */
JsSIP.IncomingResponse = (function() {
  var IncomingResponse = function() {
    this.headers = {};
    this.response_code = null;
    this.reason_phrase = null;
  };
  IncomingResponse.prototype = new JsSIP.IncomingMessage();

  return IncomingResponse;
}());<|endoftext|>


<commit_before>/**
 * SORTED LIST Class
 * Elements are sorted at insertion
 *
 * @author Brice Chevalier
 *
 * @params [comparisonFunction] {function} comparison function that takes two parameters a and b and returns a comparison number
 *
 *
 *    Method                 Time Complexity
 *    ___________________________________
 *
 *    add                    O(n), O(1) if insertion at the beginning or at the end of the list
 *    remove                 O(1)
 *    getFirst               O(1)
 *    getLast                O(1)
 *    popFirst               O(1)
 *    popLast                O(1)
 *    getCount               O(1)
 *    forEach                O(n * P) where P is the complexity of the input function
 *    forEachReverse         O(n * P) where P is the complexity of the input function
 *    clear                  O(n)
 *
 *    Memory Complexity in O(n)
 */

function ListNode(obj, previous, next, container) {
	this.object    = obj;
	this.previous  = previous;
	this.next      = next;
	this.container = container;
}

function SortedList(comparisonFunction) {
	this.length  = 0;
	this.first   = null;
	this.last    = null;
	this.cmpFunc = comparisonFunction;
}
module.exports = SortedList;

SortedList.prototype.add = function (obj) {
	var newNode = new ListNode(obj, null, null, this);
	this.length += 1;

	if (this.first === null) {
		this.first = newNode;
		this.last  = newNode;
		return newNode;
	}

	var cmpFirst = this.cmpFunc(obj, this.first.object);
	if (cmpFirst < 0) {
		// insertion at the beginning of the list
		newNode.next = this.first;
		this.first.previous = newNode;
		this.first = newNode;
		return newNode;
	}

	var cmpLast = this.cmpFunc(obj, this.last.object);
	if (cmpLast >= 0) {
		// insertion at the end
		newNode.previous = this.last;
		this.last.next = newNode;
		this.last = newNode;
		return newNode;
	}

	var current;
	if (cmpFirst + cmpLast < 0) {
		current = this.first.next;
		while (this.cmpFunc(obj, current.object) >= 0) {
			current = current.next;
		}

		// insertion before current
		newNode.next = current;
		newNode.previous = current.previous;
		newNode.previous.next = newNode;
		current.previous = newNode;
	} else {
		current = this.last.previous;
		while (this.cmpFunc(obj, current.object) < 0) {
			current = current.previous;
		}

		// insertion after current
		newNode.previous = current;
		newNode.next = current.next;
		newNode.next.previous = newNode;
		current.next = newNode;
	}
	return newNode;
};

SortedList.prototype.removeByRef = function (node) {
	if (!node || node.container !== this) {
		return node;
	}
	this.length -= 1;

	// Removing any reference to the node
	if (node.previous === null) {
		this.first = node.next;
	} else {
		node.previous.next = node.next;
	}
	if (node.next === null) {
		this.last = node.previous;
	} else {
		node.next.previous = node.previous;
	}

	// Removing any reference from the node to any other element of the list
	node.previous  = null;
	node.next      = null;
	node.container = null;
	return null;
};

SortedList.prototype.remove = function (object) {
	for (var node = this.first; node !== null; node = node.next) {
		if (node.object === object) {
			this.removeByReference(node);
			return true;
		}
	}

	return false;
};

SortedList.prototype.moveToTheBeginning = function (node) {
	if (!node || node.container !== this) {
		return false;
	}

	if (node.previous === null) {
		// node is already the first one
		return true;
	}

	// Connecting previous node to next node
	node.previous.next = node.next;

	if (this.last === node) {
		this.last = node.previous;
	} else {
		// Connecting next node to previous node
		node.next.previous = node.previous;
	}

	// Adding at the beginning
	node.previous = null;
	node.next = this.first;
	node.next.previous = node;
	this.first = node;
	return true;
};

SortedList.prototype.moveToTheEnd = function (node) {
	if (!node || node.container !== this) {
		return false;
	}

	if (node.next === null) {
		// node is already the last one
		return true;
	}

	// Connecting next node to previous node
	node.next.previous = node.previous;

	if (this.first === node) {
		this.first = node.next;
	} else {
		// Connecting previous node to next node
		node.previous.next = node.next;
	}

	// Adding at the end
	node.next = null;
	node.previous = this.last;
	node.previous.next = node;
	this.last = node;
	return true;
};

SortedList.prototype.possess = function (node) {
	return node && (node.container === this);
};

SortedList.prototype.popFirst = function () {
	var node = this.first;
	if (!node) return null;
	this.length -= 1;
	var pop  = node.object;

	this.first = node.next;
	if (this.first !== null) {
		this.first.previous = null;
	}

	node.next      = null;
	node.container = null;
	return pop;
};

SortedList.prototype.popLast = function () {
	var node = this.last;
	if (!node) return null;
	this.length -= 1;
	var pop  = node.object;

	this.last = node.previous;
	if (this.last !== null) {
		this.last.next = null;
	}

	node.previous  = null;
	node.container = null;
	return pop;
};

SortedList.prototype.getFirst = function () {
	return this.first && this.first.object;
};

SortedList.prototype.getLast = function () {
	return this.last && this.last.object;
};

SortedList.prototype.clear = function () {
	for (var current = this.first; current; current = current.next) {
		current.container = null;
	}

	this.length = 0;
	this.first  = null;
	this.last   = null;
};

SortedList.prototype.forEach = function (processingFunc, params) {
	for (var current = this.first; current; current = current.next) {
		processingFunc(current.object, params);
	}
};

SortedList.prototype.forEachReverse = function (processingFunc, params) {
	for (var current = this.last; current; current = current.previous) {
		processingFunc(current.object, params);
	}
};

SortedList.prototype.reposition = function (node) {
	if (node.container !== this) {
		console.warn('[SortedList.reposition] Trying to reposition a node that does not belong to the list');
		return node;
	}

	var prev = node.previous;
	var next = node.next;
	var obj  = node.object;

	if (next === null) {
		this.last = prev;
	} else {
		next.previous = prev;
	}

	if (prev === null) {
		this.first = next;
	} else {
		prev.next = next;
	}

	while (prev !== null && this.cmpFunc(obj, prev.object) < 0) {
		next = prev;
		prev = prev.previous;
	}

	while (next !== null && this.cmpFunc(obj, next.object) >= 0) {
		prev = next;
		next = next.next;
	}

	node.next = next;
	if (next === null) {
		this.last = node;
	} else {
		next.previous = node;
	}

	node.previous = prev;
	if (prev === null) {
		this.first = node;
	} else {
		prev.next = node;
	}

	return node;
};

SortedList.prototype.toArray = function () {
	var objects = [];
	for (var node = this.first; node !== null; node = node.next) {
		objects.push(node.object);
	}

	return objects;
};<commit_msg>Fix CRLF<commit_after>/**
 * SORTED LIST Class
 * Elements are sorted at insertion
 *
 * @author Brice Chevalier
 *
 * @params [comparisonFunction] {function} comparison function that takes two parameters a and b and returns a comparison number
 *
 *
 *    Method                 Time Complexity
 *    ___________________________________
 *
 *    add                    O(n), O(1) if insertion at the beginning or at the end of the list
 *    remove                 O(1)
 *    getFirst               O(1)
 *    getLast                O(1)
 *    popFirst               O(1)
 *    popLast                O(1)
 *    getCount               O(1)
 *    forEach                O(n * P) where P is the complexity of the input function
 *    forEachReverse         O(n * P) where P is the complexity of the input function
 *    clear                  O(n)
 *
 *    Memory Complexity in O(n)
 */

function ListNode(obj, previous, next, container) {
	this.object    = obj;
	this.previous  = previous;
	this.next      = next;
	this.container = container;
}

function SortedList(comparisonFunction) {
	this.length  = 0;
	this.first   = null;
	this.last    = null;
	this.cmpFunc = comparisonFunction;
}
module.exports = SortedList;

SortedList.prototype.add = function (obj) {
	var newNode = new ListNode(obj, null, null, this);
	this.length += 1;

	if (this.first === null) {
		this.first = newNode;
		this.last  = newNode;
		return newNode;
	}

	var cmpFirst = this.cmpFunc(obj, this.first.object);
	if (cmpFirst < 0) {
		// insertion at the beginning of the list
		newNode.next = this.first;
		this.first.previous = newNode;
		this.first = newNode;
		return newNode;
	}

	var cmpLast = this.cmpFunc(obj, this.last.object);
	if (cmpLast >= 0) {
		// insertion at the end
		newNode.previous = this.last;
		this.last.next = newNode;
		this.last = newNode;
		return newNode;
	}

	var current;
	if (cmpFirst + cmpLast < 0) {
		current = this.first.next;
		while (this.cmpFunc(obj, current.object) >= 0) {
			current = current.next;
		}

		// insertion before current
		newNode.next = current;
		newNode.previous = current.previous;
		newNode.previous.next = newNode;
		current.previous = newNode;
	} else {
		current = this.last.previous;
		while (this.cmpFunc(obj, current.object) < 0) {
			current = current.previous;
		}

		// insertion after current
		newNode.previous = current;
		newNode.next = current.next;
		newNode.next.previous = newNode;
		current.next = newNode;
	}
	return newNode;
};

SortedList.prototype.removeByRef = function (node) {
	if (!node || node.container !== this) {
		return node;
	}
	this.length -= 1;

	// Removing any reference to the node
	if (node.previous === null) {
		this.first = node.next;
	} else {
		node.previous.next = node.next;
	}
	if (node.next === null) {
		this.last = node.previous;
	} else {
		node.next.previous = node.previous;
	}

	// Removing any reference from the node to any other element of the list
	node.previous  = null;
	node.next      = null;
	node.container = null;
	return null;
};

SortedList.prototype.remove = function (object) {
	for (var node = this.first; node !== null; node = node.next) {
		if (node.object === object) {
			this.removeByReference(node);
			return true;
		}
	}

	return false;
};

SortedList.prototype.moveToTheBeginning = function (node) {
	if (!node || node.container !== this) {
		return false;
	}

	if (node.previous === null) {
		// node is already the first one
		return true;
	}

	// Connecting previous node to next node
	node.previous.next = node.next;

	if (this.last === node) {
		this.last = node.previous;
	} else {
		// Connecting next node to previous node
		node.next.previous = node.previous;
	}

	// Adding at the beginning
	node.previous = null;
	node.next = this.first;
	node.next.previous = node;
	this.first = node;
	return true;
};

SortedList.prototype.moveToTheEnd = function (node) {
	if (!node || node.container !== this) {
		return false;
	}

	if (node.next === null) {
		// node is already the last one
		return true;
	}

	// Connecting next node to previous node
	node.next.previous = node.previous;

	if (this.first === node) {
		this.first = node.next;
	} else {
		// Connecting previous node to next node
		node.previous.next = node.next;
	}

	// Adding at the end
	node.next = null;
	node.previous = this.last;
	node.previous.next = node;
	this.last = node;
	return true;
};

SortedList.prototype.possess = function (node) {
	return node && (node.container === this);
};

SortedList.prototype.popFirst = function () {
	var node = this.first;
	if (!node) return null;
	this.length -= 1;
	var pop  = node.object;

	this.first = node.next;
	if (this.first !== null) {
		this.first.previous = null;
	}

	node.next      = null;
	node.container = null;
	return pop;
};

SortedList.prototype.popLast = function () {
	var node = this.last;
	if (!node) return null;
	this.length -= 1;
	var pop  = node.object;

	this.last = node.previous;
	if (this.last !== null) {
		this.last.next = null;
	}

	node.previous  = null;
	node.container = null;
	return pop;
};

SortedList.prototype.getFirst = function () {
	return this.first && this.first.object;
};

SortedList.prototype.getLast = function () {
	return this.last && this.last.object;
};

SortedList.prototype.clear = function () {
	for (var current = this.first; current; current = current.next) {
		current.container = null;
	}

	this.length = 0;
	this.first  = null;
	this.last   = null;
};

SortedList.prototype.forEach = function (processingFunc, params) {
	for (var current = this.first; current; current = current.next) {
		processingFunc(current.object, params);
	}
};

SortedList.prototype.forEachReverse = function (processingFunc, params) {
	for (var current = this.last; current; current = current.previous) {
		processingFunc(current.object, params);
	}
};

SortedList.prototype.reposition = function (node) {
	if (node.container !== this) {
		console.warn('[SortedList.reposition] Trying to reposition a node that does not belong to the list');
		return node;
	}

	var prev = node.previous;
	var next = node.next;
	var obj  = node.object;

	if (next === null) {
		this.last = prev;
	} else {
		next.previous = prev;
	}

	if (prev === null) {
		this.first = next;
	} else {
		prev.next = next;
	}

	while (prev !== null && this.cmpFunc(obj, prev.object) < 0) {
		next = prev;
		prev = prev.previous;
	}

	while (next !== null && this.cmpFunc(obj, next.object) >= 0) {
		prev = next;
		next = next.next;
	}

	node.next = next;
	if (next === null) {
		this.last = node;
	} else {
		next.previous = node;
	}

	node.previous = prev;
	if (prev === null) {
		this.first = node;
	} else {
		prev.next = node;
	}

	return node;
};

SortedList.prototype.toArray = function () {
	var objects = [];
	for (var node = this.first; node !== null; node = node.next) {
		objects.push(node.object);
	}

	return objects;
};<|endoftext|>


<commit_before>import {axisLength, axisMinMax} from './PieceBlock.js';

export function newCubeMat(cubeDim) {
  return Array(...Array(cubeDim * cubeDim)).map(() => Array(cubeDim).fill(0));
}

export class CubeState {
  constructor(cubeMat, currentPieces, pieceIsometeries, pieceShifts) {
    this.cubeMat = cubeMat;
    this.currentPieces = currentPieces;
    this.pieceIsometeries = pieceIsometeries;
    this.pieceShifts = pieceShifts;
  }
}

export class StateQueue {
  constructor(cubeDim, totalPieces, Pieces) {
    this.cubeDim = cubeDim;
    this.totalPieces = totalPieces;
    this.Pieces = Pieces;
    this.cubeMat = newCubeMat(cubeDim);
  }

  // State Queue - Depth First Search
  // [ Current Cube 3D Matrix ] , [ Current Pieces ] , [ Piece Isometries ] , [ Piece Shifts ]
  stateDepthFirstSearch(stateQueueElement) {

    let pieceIdx;
    let cubePos; let cubePosShift;
    let newRange;
    let bounds;
    let pieceShifts;

    let posMap;
    let cubeMatValues;

    let x;     let y;     let z;
    let xDis;  let yDis;  let zDis;
    let xFree; let yFree; let zFree;

    // Check previous configuration
    if (stateQueueElement) { // ------------------- If state element is not null
      pieceIdx = stateQueueElement.currentPieces;
    } else {
      pieceIdx = 0;
    }

    let currentPieces = pieceIdx + 1;
    let pieceIsometeries;

    // All pieces were placed, return configuration
    if (pieceIdx == this.totalPieces) {
      return stateQueueElement;
    }
    let posIsos = this.Pieces[pieceIdx].getUniqueIsometries();

    for (let i of posIsos) {
      // console.log('Pos',this.Pieces[pieceIdx].cubePositions);
      cubePos = this.Pieces[pieceIdx].performIsometry(i);
      // this.Pieces[pieceIdx].cubePositions = cubePos;

      newRange = axisLength(cubePos);
      bounds = axisMinMax(cubePos);
      // console.log('oldPos',cubePos);
      // console.log('checkPos',this.Pieces[pieceIdx].cubePositions);
      // debugger;
      xDis = 0 - bounds[0][0]; // ---------------- How Far Piece's x-axis boundary is from origin
      yDis = 0 - bounds[1][0]; // ---------------- How Far Piece's y-axis boundary is from origin
      zDis = 0 - bounds[2][0]; // ---------------- How Far Piece's z-axis boundary is from origin

      xFree = this.cubeDim - newRange[0] + 1; // ------ How Much Free Space in X-axis
      yFree = this.cubeDim - newRange[1] + 1; // ------ How Much Free Space in Y-axis
      zFree = this.cubeDim - newRange[2] + 1; // ------ How Much Free Space in Z-axis

      pieceIsometeries = [];
      pieceShifts = [];
      if (pieceIdx) {
        pieceIsometeries = [...stateQueueElement.pieceIsometeries];
      }
      pieceIsometeries.push(i);

      // Determine How Much Shape can be shifted
      for (x = 0; x < xFree; x++) {
        for (y = 0; y < yFree; y++) {
          for (z = 0; z < zFree; z++) {

            // -------------------------------------------------------------------------------------------- (x,y,z) mapping to 2d matrix
            posMap = cubePos.map(e => [(this.cubeDim * (e[2] + zDis + z)) + e[0] + xDis + x, e[1] + yDis + y]);
            cubeMatValues = posMap.map(e => this.cubeMat[e[0]][e[1]]); // --------------------------------- Grab values from the cube matrix

            if (cubeMatValues.every(e => e == 0)) { // ------------------------------------------------------ Cube can fit piece

              pieceShifts = [];
              if (pieceIdx) {
                pieceShifts = [...stateQueueElement.pieceShifts];
              }
              pieceShifts.push([x + xDis, y + yDis, z + zDis]);

              posMap.map(e => (this.cubeMat[e[0]][e[1]] = this.Pieces[pieceIdx].pieceID)); // ----------------------------------------------- Place 1s into cube matrix to indicate filled spot
              let temp = this.stateDepthFirstSearch(new CubeState(this.cubeMat, currentPieces, pieceIsometeries, pieceShifts));
              if (temp != null) {
                return temp;
              }
              posMap.map(e => (this.cubeMat[e[0]][e[1]] = 0));

            }

          }
        }
      }

    }
    return null;
  }
}
<commit_msg>Added some comments.<commit_after>import {axisLength, axisMinMax} from './PieceBlock.js';

// 3D Matrix expressed as 2D Matrix
// if cube dimension is 3, matrix is 9 x 3 with the first three rows being one layer of the cube
export function newCubeMat(cubeDim) {
  return Array(...Array(cubeDim * cubeDim)).map(() => Array(cubeDim).fill(0));
}

// Current of the Cube after rotation and shifting some pieces
export class CubeState {
  constructor(cubeMat, currentPieces, pieceIsometeries, pieceShifts) {
    this.cubeMat = cubeMat;
    this.currentPieces = currentPieces;
    this.pieceIsometeries = pieceIsometeries;
    this.pieceShifts = pieceShifts;
  }
}

// Depth-First Searcher
export class StateQueue {

  // Values to reference as depth-first searcher is performed
  constructor(cubeDim, totalPieces, Pieces) {
    this.cubeDim = cubeDim;
    this.totalPieces = totalPieces;
    this.Pieces = Pieces; // All the piece objects including their id and cube positions
    this.cubeMat = newCubeMat(cubeDim); // create new empty cube matrix
  }

  // State Queue - Depth First Search
  // Checks CubeState element for filled matrix otherwise keeps searching for a valid configuration
  stateDepthFirstSearch(stateQueueElement) {

    let pieceIdx;

    // Check previous configuration
    if (stateQueueElement) { // ------------------- If state element is not null
      pieceIdx = stateQueueElement.currentPieces;
    } else {
      pieceIdx = 0;
    }

    // All pieces were placed, return configuration
    if (pieceIdx == this.totalPieces) {
      return stateQueueElement;
    }

    let currentPieces = pieceIdx + 1;
    let pieceIsometeries;

    let cubePos;
    let cubePosShift;
    let newRange;
    let bounds;
    let pieceShifts;

    let posMap;
    let cubeMatValues;

    let x;
    let y;
    let z;
    let xDis;
    let yDis;
    let zDis;
    let xFree;
    let yFree;
    let zFree;

    let posIsos = this.Pieces[pieceIdx].getUniqueIsometries();

    for (let i of posIsos) {

      cubePos = this.Pieces[pieceIdx].performIsometry(i);
      newRange = axisLength(cubePos);
      bounds = axisMinMax(cubePos);

      xDis = 0 - bounds[0][0]; // ---------------- How Far Piece's x-axis boundary is from origin
      yDis = 0 - bounds[1][0]; // ---------------- How Far Piece's y-axis boundary is from origin
      zDis = 0 - bounds[2][0]; // ---------------- How Far Piece's z-axis boundary is from origin

      xFree = this.cubeDim - newRange[0] + 1; // ------ How Much Free Space in X-axis
      yFree = this.cubeDim - newRange[1] + 1; // ------ How Much Free Space in Y-axis
      zFree = this.cubeDim - newRange[2] + 1; // ------ How Much Free Space in Z-axis

      pieceIsometeries = [];
      pieceShifts = [];
      if (pieceIdx) {
        pieceIsometeries = [...stateQueueElement.pieceIsometeries];
      }
      pieceIsometeries.push(i);

      // Determine How Much Shape can be shifted
      for (x = 0; x < xFree; x++) {
        for (y = 0; y < yFree; y++) {
          for (z = 0; z < zFree; z++) {

            // -------------------------------------------------------------------------------------------- (x,y,z) mapping to 2d matrix
            posMap = cubePos.map(e => [
              (this.cubeDim * (e[2] + zDis + z)) + e[0] + xDis + x,
              e[1] + yDis + y
            ]);
            cubeMatValues = posMap.map(e => this.cubeMat[e[0]][e[1]]); // --------------------------------- Grab values from the cube matrix

            if (cubeMatValues.every(e => e == 0)) { // ---------------------------------------------------- Cube can fit piece

              pieceShifts = [];
              if (pieceIdx) {
                pieceShifts = [...stateQueueElement.pieceShifts];
              }
              pieceShifts.push([
                x + xDis,
                y + yDis,
                z + zDis
              ]);

              // place piece and update the matrix
              posMap.map(e => (this.cubeMat[e[0]][e[1]] = this.Pieces[pieceIdx].pieceID)); // ----------------------------------------------- Place 1s into cube matrix to indicate filled spot

              let temp = this.stateDepthFirstSearch(new CubeState(this.cubeMat, currentPieces, pieceIsometeries, pieceShifts));

              // return element if on next call, all other pieces fit
              if (temp != null) {
                return temp;
              }

              // otherwise, restore matrix back to when piece wasnt placed
              posMap.map(e => (this.cubeMat[e[0]][e[1]] = 0));

            }

          }
        }
      }

    }

    // piece couldnt fit into matrix
    return null;
  }
}
<|endoftext|>