<commit_before>'use strict';

// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------

var InputStream = require('./InputStream');
var PosInfo = require('./PosInfo');
var Trace = require('./Trace');
var pexprs = require('./pexprs');

// --------------------------------------------------------------------
// Private stuff
// --------------------------------------------------------------------

var RM_RIGHTMOST_FAILURE_POSITION = 0;
var RM_RIGHTMOST_FAILURES = 1;

var applySpaces = new pexprs.Apply('spaces');

function State(grammar, input, opts) {
  this.grammar = grammar;
  this.startExpr = this._getStartExpr(grammar, opts.startApplication);
  this.inputStream = new InputStream(input);
  this.tracingEnabled = opts.trace || false;
  this.init(RM_RIGHTMOST_FAILURE_POSITION);
}

State.prototype = {
  init: function(recordingMode) {
    this.posInfos = [];
    this._bindings = [];
    this._bindingOffsets = [];
    this._applicationStack = [];
    this._posStack = [0];
    this.inLexifiedContextStack = [false];

    this.recordingMode = recordingMode;
    if (recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      this.rightmostFailurePosition = -1;
    } else if (recordingMode === RM_RIGHTMOST_FAILURES) {
      // We always run in *rightmost failure position* recording mode before running in
      // *rightmost failures* recording mode. And since the traces generated by each of
      // these passes would be identical, there's no need to record it now if we have
      // already recorded it in the first pass.
      this.tracingEnabled = false;
    } else {
      throw new Error('invalid recording mode: ' + recordingMode);
    }

    if (this.isTracing()) {
      this.trace = [];
    }
  },

  posToOffset: function(pos) {
    return pos - this._posStack[this._posStack.length - 1];
  },

  enterApplication: function(posInfo, app) {
    this._posStack.push(this.inputStream.pos);
    this._applicationStack.push(app);
    this.inLexifiedContextStack.push(false);
    posInfo.enter(app);
  },

  exitApplication: function(posInfo, optNode) {
    var origPos = this._posStack.pop();
    this._applicationStack.pop();
    this.inLexifiedContextStack.pop();
    posInfo.exit();

    if (optNode) {
      this.pushBinding(optNode, origPos);
    }
  },

  enterLexifiedContext: function() {
    this.inLexifiedContextStack.push(true);
  },

  exitLexifiedContext: function() {
    this.inLexifiedContextStack.pop();
  },

  currentApplication: function() {
    return this._applicationStack[this._applicationStack.length - 1];
  },

  inSyntacticContext: function() {
    if (typeof this.inputStream.source !== 'string') {
      return false;
    }
    var currentApplication = this.currentApplication();
    if (currentApplication) {
      return currentApplication.isSyntactic() && !this.inLexifiedContext();
    } else {
      // The top-level context is syntactic if the start application is.
      return this.startExpr.factors[0].isSyntactic();
    }
  },

  inLexifiedContext: function() {
    return this.inLexifiedContextStack[this.inLexifiedContextStack.length - 1];
  },

  skipSpaces: function() {
    var origFailuresInfo = this.getFailuresInfo();
    this.eval(applySpaces);
    this.popBinding();
    this.restoreFailuresInfo(origFailuresInfo);
    return this.inputStream.pos;
  },

  skipSpacesIfInSyntacticContext: function() {
    return this.inSyntacticContext() ?
        this.skipSpaces() :
        this.inputStream.pos;
  },

  maybeSkipSpacesBefore: function(expr) {
    if (expr instanceof pexprs.Apply && expr.isSyntactic()) {
      return this.skipSpaces();
    } else if (expr.allowsSkippingPrecedingSpace() && expr !== applySpaces) {
      return this.skipSpacesIfInSyntacticContext();
    } else {
      return this.inputStream.pos;
    }
  },

  pushBinding: function(node, origPos) {
    this._bindings.push(node);
    this._bindingOffsets.push(this.posToOffset(origPos));
  },

  popBinding: function() {
    this._bindings.pop();
    this._bindingOffsets.pop();
  },

  numBindings: function() {
    return this._bindings.length;
  },

  truncateBindings: function(newLength) {
    // Yes, this is this really faster than setting the `length` property (tested with
    // bin/es5bench on Node v6.1.0).
    while (this._bindings.length > newLength) {
      this.popBinding();
    }
  },

  getCurrentPosInfo: function() {
    return this.getPosInfo(this.inputStream.pos);
  },

  getPosInfo: function(pos) {
    var posInfo = this.posInfos[pos];
    if (!posInfo) {
      posInfo = this.posInfos[pos] = new PosInfo(this);
    }
    return posInfo;
  },

  processFailure: function(pos, expr) {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      if (pos > this.rightmostFailurePosition) {
        this.rightmostFailurePosition = pos;
      }
    } else /* if (this.recordingMode === RM_RIGHTMOST_FAILURES) */ {
      if (pos === this.rightmostFailurePosition) {
        // We're only interested in failures at the rightmost failure position that haven't
        // already been recorded.

        var app = this.currentApplication();
        if (app) {
          // Substitute parameters with the actual pexprs that were passed to
          // the current rule.
          expr = expr.substituteParams(app.args);
        } else {
          // This branch is only reached for the "end-check" that is
          // performed after the top-level application. In that case,
          // expr === pexprs.end so there is no need to substitute
          // parameters.
        }

        this.addRightmostFailure(expr.toFailure(this.grammar), false);
      }
    }
  },

  ensureRightmostFailures: function() {
    if (!this.rightmostFailures) {
      this.rightmostFailures = Object.create(null);
    }
  },

  addRightmostFailure: function(failure, shouldCloneIfNew) {
    this.ensureRightmostFailures();
    var key = failure.toKey();
    if (!this.rightmostFailures[key]) {
      this.rightmostFailures[key] = shouldCloneIfNew ? failure.clone() : failure;
    } else if (this.rightmostFailures[key].isFluffy() && !failure.isFluffy()) {
      this.rightmostFailures[key].clearFluffy();
    }
  },

  addRightmostFailures: function(failures, shouldCloneIfNew) {
    var self = this;
    Object.keys(failures).forEach(function(key) {
      self.addRightmostFailure(failures[key], shouldCloneIfNew);
    });
  },

  cloneRightmostFailures: function() {
    if (!this.rightmostFailures) {
      return undefined;
    }

    var ans = Object.create(null);
    var self = this;
    Object.keys(this.rightmostFailures).forEach(function(key) {
      ans[key] = self.rightmostFailures[key].clone();
    });
    return ans;
  },

  getRightmostFailurePosition: function() {
    return this.rightmostFailurePosition;
  },

  getFailures: function() {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      // Rewind, then try to match the input again, recording failures.
      this.init(RM_RIGHTMOST_FAILURES);
      this.evalFromStart();
    }

    this.ensureRightmostFailures();
    var self = this;
    return Object.keys(this.rightmostFailures).map(function(key) {
      return self.rightmostFailures[key];
    });
  },

  // Returns the memoized trace entry for `expr` at `pos`, if one exists, `null` otherwise.
  getMemoizedTraceEntry: function(pos, expr) {
    var posInfo = this.posInfos[pos];
    if (posInfo && expr.ruleName) {
      var memoRec = posInfo.memo[expr.toMemoKey()];
      if (memoRec && memoRec.traceEntry) {
        var entry = memoRec.traceEntry.cloneWithExpr(expr);
        entry.isMemoized = true;
        return entry;
      }
    }
    return null;
  },

  // Returns a new trace entry, with the currently active trace array as its children.
  getTraceEntry: function(pos, expr, succeeded, bindings) {
    return this.getMemoizedTraceEntry(pos, expr) ||
           new Trace(this.inputStream, pos, expr, succeeded, bindings, this.trace);
  },

  isTracing: function() {
    return this.tracingEnabled;
  },

  useMemoizedResult: function(origPos, memoRec) {
    if (this.isTracing()) {
      this.trace.push(memoRec.traceEntry);
    }

    if (this.recordingMode === RM_RIGHTMOST_FAILURES && memoRec.failuresAtRightmostPosition) {
      this.addRightmostFailures(memoRec.failuresAtRightmostPosition, true);
    }

    this.inputStream.examinedLength =
        Math.max(this.inputStream.examinedLength, memoRec.examinedLength + origPos);

    if (memoRec.value) {
      this.inputStream.pos += memoRec.matchLength;
      this.pushBinding(memoRec.value, origPos);
      return true;
    }
    return false;
  },

  // Evaluate `expr` and return `true` if it succeeded, `false` otherwise. On success, `bindings`
  // will have `expr.getArity()` more elements than before, and the input stream's position may
  // have increased. On failure, `bindings` and position will be unchanged.
  eval: function(expr) {
    var inputStream = this.inputStream;
    var origNumBindings = this._bindings.length;

    var origFailures;
    if (this.recordingMode === RM_RIGHTMOST_FAILURES) {
      origFailures = this.rightmostFailures;
      this.rightmostFailures = undefined;
    }

    var origPos = inputStream.pos;
    var memoPos = this.maybeSkipSpacesBefore(expr);

    var origTrace;
    if (this.isTracing()) {
      origTrace = this.trace;
      this.trace = [];
    }

    // Do the actual evaluation.
    var ans = expr.eval(this);

    if (this.isTracing()) {
      var bindings = this._bindings.slice(origNumBindings);
      var traceEntry = this.getTraceEntry(memoPos, expr, ans, bindings);
      traceEntry.isImplicitSpaces = expr === applySpaces;
      traceEntry.isRootNode = expr === this.startExpr;
      origTrace.push(traceEntry);
      this.trace = origTrace;
    }

    if (ans) {
      if (this.rightmostFailures &&
        (inputStream.pos === this.rightmostFailurePosition ||
         this.skipSpacesIfInSyntacticContext() === this.rightmostFailurePosition)) {
        var self = this;
        Object.keys(this.rightmostFailures).forEach(function(key) {
          self.rightmostFailures[key].makeFluffy();
        });
      }
    } else {
      // Reset the position and the bindings.
      inputStream.pos = origPos;
      this.truncateBindings(origNumBindings);
    }

    if (this.recordingMode === RM_RIGHTMOST_FAILURES && origFailures) {
      this.addRightmostFailures(origFailures, false);
    }

    return ans;
  },

  // Return the starting expression for this grammar. If `optStartApplication` is specified, it
  // is a string expressing a rule application in the grammar. If not specified, the grammar's
  // default start rule will be used.
  _getStartExpr: function(grammar, optStartApplication) {
    var applicationStr = optStartApplication || grammar.defaultStartRule;
    if (!applicationStr) {
      throw new Error('Missing start rule argument -- the grammar has no default start rule.');
    }

    var startApp = grammar.parseApplication(applicationStr);
    return new pexprs.Seq([startApp, pexprs.end]);
  },

  evalFromStart: function() {
    this.eval(this.startExpr);
  },

  getFailuresInfo: function() {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      return this.rightmostFailurePosition;
    } else /* if (this.recordingMode === RM_RIGHTMOST_FAILURES) */ {
      return this.rightmostFailures;
    }
  },

  restoreFailuresInfo: function(failuresInfo) {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      this.rightmostFailurePosition = failuresInfo;
    } else /* if (this.recordingMode === RM_RIGHTMOST_FAILURES) */ {
      this.rightmostFailures = failuresInfo;
    }
  },

  replaceInput: function(startIdx, endIdx, str) {
    var currentInput = this.inputStream.source;
    if (startIdx < 0 || startIdx > currentInput.length ||
        endIdx < 0 || endIdx > currentInput.length ||
        startIdx > endIdx) {
      throw new Error('Invalid indices: ' + startIdx + ' and ' + endIdx);
    }
    // TODO: Consider reusing the same InputStream instance here.
    this.inputStream =
        new InputStream(currentInput.slice(0, startIdx) + str + currentInput.slice(endIdx));

    // Keep the memo table in sync with the edits.
    var newValues = Array.apply(null, new Array(str.length));  // Array of `undefined` (wat)

    // Replace the contents from startIdx:endIdx with `newValues`.
    this.posInfos.splice.apply(
        this.posInfos,
        [startIdx, endIdx - startIdx].concat(newValues));

    // Invalidate memoRecs
    for (var pos = 0; pos < startIdx; pos++) {
      var posInfo = this.posInfos[pos];
      if (posInfo) {
        posInfo.clearObsoleteEntries(pos, startIdx);
      }
    }
  }
};

// --------------------------------------------------------------------
// Exports
// --------------------------------------------------------------------

module.exports = State;
<commit_msg>keep the value of tracingEnabled so that it will be respected when State is used incrementally<commit_after>'use strict';

// --------------------------------------------------------------------
// Imports
// --------------------------------------------------------------------

var InputStream = require('./InputStream');
var PosInfo = require('./PosInfo');
var Trace = require('./Trace');
var pexprs = require('./pexprs');

// --------------------------------------------------------------------
// Private stuff
// --------------------------------------------------------------------

var RM_RIGHTMOST_FAILURE_POSITION = 0;
var RM_RIGHTMOST_FAILURES = 1;

var applySpaces = new pexprs.Apply('spaces');

function State(grammar, input, opts) {
  this.grammar = grammar;
  this.startExpr = this._getStartExpr(grammar, opts.startApplication);
  this.inputStream = new InputStream(input);
  this.tracingEnabled = opts.trace || false;
  this.init(RM_RIGHTMOST_FAILURE_POSITION);
}

State.prototype = {
  init: function(recordingMode) {
    this.posInfos = [];
    this._bindings = [];
    this._bindingOffsets = [];
    this._applicationStack = [];
    this._posStack = [0];
    this.inLexifiedContextStack = [false];

    this.recordingMode = recordingMode;
    if (recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      this.rightmostFailurePosition = -1;
    } else if (recordingMode === RM_RIGHTMOST_FAILURES) {
      // no-op
    } else {
      throw new Error('invalid recording mode: ' + recordingMode);
    }

    if (this.isTracing()) {
      this.trace = [];
    }
  },

  posToOffset: function(pos) {
    return pos - this._posStack[this._posStack.length - 1];
  },

  enterApplication: function(posInfo, app) {
    this._posStack.push(this.inputStream.pos);
    this._applicationStack.push(app);
    this.inLexifiedContextStack.push(false);
    posInfo.enter(app);
  },

  exitApplication: function(posInfo, optNode) {
    var origPos = this._posStack.pop();
    this._applicationStack.pop();
    this.inLexifiedContextStack.pop();
    posInfo.exit();

    if (optNode) {
      this.pushBinding(optNode, origPos);
    }
  },

  enterLexifiedContext: function() {
    this.inLexifiedContextStack.push(true);
  },

  exitLexifiedContext: function() {
    this.inLexifiedContextStack.pop();
  },

  currentApplication: function() {
    return this._applicationStack[this._applicationStack.length - 1];
  },

  inSyntacticContext: function() {
    if (typeof this.inputStream.source !== 'string') {
      return false;
    }
    var currentApplication = this.currentApplication();
    if (currentApplication) {
      return currentApplication.isSyntactic() && !this.inLexifiedContext();
    } else {
      // The top-level context is syntactic if the start application is.
      return this.startExpr.factors[0].isSyntactic();
    }
  },

  inLexifiedContext: function() {
    return this.inLexifiedContextStack[this.inLexifiedContextStack.length - 1];
  },

  skipSpaces: function() {
    var origFailuresInfo = this.getFailuresInfo();
    this.eval(applySpaces);
    this.popBinding();
    this.restoreFailuresInfo(origFailuresInfo);
    return this.inputStream.pos;
  },

  skipSpacesIfInSyntacticContext: function() {
    return this.inSyntacticContext() ?
        this.skipSpaces() :
        this.inputStream.pos;
  },

  maybeSkipSpacesBefore: function(expr) {
    if (expr instanceof pexprs.Apply && expr.isSyntactic()) {
      return this.skipSpaces();
    } else if (expr.allowsSkippingPrecedingSpace() && expr !== applySpaces) {
      return this.skipSpacesIfInSyntacticContext();
    } else {
      return this.inputStream.pos;
    }
  },

  pushBinding: function(node, origPos) {
    this._bindings.push(node);
    this._bindingOffsets.push(this.posToOffset(origPos));
  },

  popBinding: function() {
    this._bindings.pop();
    this._bindingOffsets.pop();
  },

  numBindings: function() {
    return this._bindings.length;
  },

  truncateBindings: function(newLength) {
    // Yes, this is this really faster than setting the `length` property (tested with
    // bin/es5bench on Node v6.1.0).
    while (this._bindings.length > newLength) {
      this.popBinding();
    }
  },

  getCurrentPosInfo: function() {
    return this.getPosInfo(this.inputStream.pos);
  },

  getPosInfo: function(pos) {
    var posInfo = this.posInfos[pos];
    if (!posInfo) {
      posInfo = this.posInfos[pos] = new PosInfo(this);
    }
    return posInfo;
  },

  processFailure: function(pos, expr) {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      if (pos > this.rightmostFailurePosition) {
        this.rightmostFailurePosition = pos;
      }
    } else /* if (this.recordingMode === RM_RIGHTMOST_FAILURES) */ {
      if (pos === this.rightmostFailurePosition) {
        // We're only interested in failures at the rightmost failure position that haven't
        // already been recorded.

        var app = this.currentApplication();
        if (app) {
          // Substitute parameters with the actual pexprs that were passed to
          // the current rule.
          expr = expr.substituteParams(app.args);
        } else {
          // This branch is only reached for the "end-check" that is
          // performed after the top-level application. In that case,
          // expr === pexprs.end so there is no need to substitute
          // parameters.
        }

        this.addRightmostFailure(expr.toFailure(this.grammar), false);
      }
    }
  },

  ensureRightmostFailures: function() {
    if (!this.rightmostFailures) {
      this.rightmostFailures = Object.create(null);
    }
  },

  addRightmostFailure: function(failure, shouldCloneIfNew) {
    this.ensureRightmostFailures();
    var key = failure.toKey();
    if (!this.rightmostFailures[key]) {
      this.rightmostFailures[key] = shouldCloneIfNew ? failure.clone() : failure;
    } else if (this.rightmostFailures[key].isFluffy() && !failure.isFluffy()) {
      this.rightmostFailures[key].clearFluffy();
    }
  },

  addRightmostFailures: function(failures, shouldCloneIfNew) {
    var self = this;
    Object.keys(failures).forEach(function(key) {
      self.addRightmostFailure(failures[key], shouldCloneIfNew);
    });
  },

  cloneRightmostFailures: function() {
    if (!this.rightmostFailures) {
      return undefined;
    }

    var ans = Object.create(null);
    var self = this;
    Object.keys(this.rightmostFailures).forEach(function(key) {
      ans[key] = self.rightmostFailures[key].clone();
    });
    return ans;
  },

  getRightmostFailurePosition: function() {
    return this.rightmostFailurePosition;
  },

  getFailures: function() {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      // Rewind, then try to match the input again, recording failures.
      this.init(RM_RIGHTMOST_FAILURES);
      this.evalFromStart();
    }

    this.ensureRightmostFailures();
    var self = this;
    return Object.keys(this.rightmostFailures).map(function(key) {
      return self.rightmostFailures[key];
    });
  },

  // Returns the memoized trace entry for `expr` at `pos`, if one exists, `null` otherwise.
  getMemoizedTraceEntry: function(pos, expr) {
    var posInfo = this.posInfos[pos];
    if (posInfo && expr.ruleName) {
      var memoRec = posInfo.memo[expr.toMemoKey()];
      if (memoRec && memoRec.traceEntry) {
        var entry = memoRec.traceEntry.cloneWithExpr(expr);
        entry.isMemoized = true;
        return entry;
      }
    }
    return null;
  },

  // Returns a new trace entry, with the currently active trace array as its children.
  getTraceEntry: function(pos, expr, succeeded, bindings) {
    return this.getMemoizedTraceEntry(pos, expr) ||
           new Trace(this.inputStream, pos, expr, succeeded, bindings, this.trace);
  },

  isTracing: function() {
    // We always run in *rightmost failure position* recording mode before running in
    // *rightmost failures* recording mode. And since the traces generated by each of
    // these passes would be identical, we only have to record it in the first pass.
    return this.tracingEnabled && this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION;
  },

  useMemoizedResult: function(origPos, memoRec) {
    if (this.isTracing()) {
      this.trace.push(memoRec.traceEntry);
    }

    if (this.recordingMode === RM_RIGHTMOST_FAILURES && memoRec.failuresAtRightmostPosition) {
      this.addRightmostFailures(memoRec.failuresAtRightmostPosition, true);
    }

    this.inputStream.examinedLength =
        Math.max(this.inputStream.examinedLength, memoRec.examinedLength + origPos);

    if (memoRec.value) {
      this.inputStream.pos += memoRec.matchLength;
      this.pushBinding(memoRec.value, origPos);
      return true;
    }
    return false;
  },

  // Evaluate `expr` and return `true` if it succeeded, `false` otherwise. On success, `bindings`
  // will have `expr.getArity()` more elements than before, and the input stream's position may
  // have increased. On failure, `bindings` and position will be unchanged.
  eval: function(expr) {
    var inputStream = this.inputStream;
    var origNumBindings = this._bindings.length;

    var origFailures;
    if (this.recordingMode === RM_RIGHTMOST_FAILURES) {
      origFailures = this.rightmostFailures;
      this.rightmostFailures = undefined;
    }

    var origPos = inputStream.pos;
    var memoPos = this.maybeSkipSpacesBefore(expr);

    var origTrace;
    if (this.isTracing()) {
      origTrace = this.trace;
      this.trace = [];
    }

    // Do the actual evaluation.
    var ans = expr.eval(this);

    if (this.isTracing()) {
      var bindings = this._bindings.slice(origNumBindings);
      var traceEntry = this.getTraceEntry(memoPos, expr, ans, bindings);
      traceEntry.isImplicitSpaces = expr === applySpaces;
      traceEntry.isRootNode = expr === this.startExpr;
      origTrace.push(traceEntry);
      this.trace = origTrace;
    }

    if (ans) {
      if (this.rightmostFailures &&
        (inputStream.pos === this.rightmostFailurePosition ||
         this.skipSpacesIfInSyntacticContext() === this.rightmostFailurePosition)) {
        var self = this;
        Object.keys(this.rightmostFailures).forEach(function(key) {
          self.rightmostFailures[key].makeFluffy();
        });
      }
    } else {
      // Reset the position and the bindings.
      inputStream.pos = origPos;
      this.truncateBindings(origNumBindings);
    }

    if (this.recordingMode === RM_RIGHTMOST_FAILURES && origFailures) {
      this.addRightmostFailures(origFailures, false);
    }

    return ans;
  },

  // Return the starting expression for this grammar. If `optStartApplication` is specified, it
  // is a string expressing a rule application in the grammar. If not specified, the grammar's
  // default start rule will be used.
  _getStartExpr: function(grammar, optStartApplication) {
    var applicationStr = optStartApplication || grammar.defaultStartRule;
    if (!applicationStr) {
      throw new Error('Missing start rule argument -- the grammar has no default start rule.');
    }

    var startApp = grammar.parseApplication(applicationStr);
    return new pexprs.Seq([startApp, pexprs.end]);
  },

  evalFromStart: function() {
    this.eval(this.startExpr);
  },

  getFailuresInfo: function() {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      return this.rightmostFailurePosition;
    } else /* if (this.recordingMode === RM_RIGHTMOST_FAILURES) */ {
      return this.rightmostFailures;
    }
  },

  restoreFailuresInfo: function(failuresInfo) {
    if (this.recordingMode === RM_RIGHTMOST_FAILURE_POSITION) {
      this.rightmostFailurePosition = failuresInfo;
    } else /* if (this.recordingMode === RM_RIGHTMOST_FAILURES) */ {
      this.rightmostFailures = failuresInfo;
    }
  },

  replaceInput: function(startIdx, endIdx, str) {
    var currentInput = this.inputStream.source;
    if (startIdx < 0 || startIdx > currentInput.length ||
        endIdx < 0 || endIdx > currentInput.length ||
        startIdx > endIdx) {
      throw new Error('Invalid indices: ' + startIdx + ' and ' + endIdx);
    }
    // TODO: Consider reusing the same InputStream instance here.
    this.inputStream =
        new InputStream(currentInput.slice(0, startIdx) + str + currentInput.slice(endIdx));

    // Keep the memo table in sync with the edits.
    var newValues = Array.apply(null, new Array(str.length));  // Array of `undefined` (wat)

    // Replace the contents from startIdx:endIdx with `newValues`.
    this.posInfos.splice.apply(
        this.posInfos,
        [startIdx, endIdx - startIdx].concat(newValues));

    // Invalidate memoRecs
    for (var pos = 0; pos < startIdx; pos++) {
      var posInfo = this.posInfos[pos];
      if (posInfo) {
        posInfo.clearObsoleteEntries(pos, startIdx);
      }
    }
  }
};

// --------------------------------------------------------------------
// Exports
// --------------------------------------------------------------------

module.exports = State;
<|endoftext|>


<commit_before>'use strict';

const POSTCSS_SEVERITY_MAP = {
	'warning': 'warn',
};
const LintError = require('./lint-error');

/**
 * PostCSS错误对象
 *
 * @class PostCSSError
 * @extends {LintError}
 */
class PostCSSError extends LintError {
	/**
	 * Creates an instance of PostCSSError.
	 *
	 * @param {Object} error 原始的PostCSS错误对象
	 *
	 * @memberOf PostCSSError
	 */
	constructor(error) {
		super({

			// 文件名
			fileName: error.file || (error.input && error.input.file),

			// 错误等级默认error，后面会覆盖
			severity: 'error',

			// 行号
			lineNumber: error.line,

			// 列号
			columnNumber: error.column,

			// 错误信息
			message: error.text.replace(new RegExp('\\s*\\(' + error.rule + '\\)$'), ''),

			// 错误ID
			// rule: error.rule,

			// 源代码上下文
			source: (error.node && error.node.type && error.node.type !== 'root') ? String(error.node) : '',

			// 报错插件
			plugin: 'PostCSS',
			doc: error.rule && error.plugin === 'stylelint' && `https://stylelint.io/user-guide/rules/${ error.rule }/`
		}, error, {
			// 错误等级
			severity: POSTCSS_SEVERITY_MAP[String(error.severity)] || error.severity || 'error',
		});
	}
}

module.exports = PostCSSError;
<commit_msg>update severity of postcss error<commit_after>'use strict';

const POSTCSS_SEVERITY_MAP = {
	'warning': 'warn',
};
const LintError = require('./lint-error');

/**
 * PostCSS错误对象
 *
 * @class PostCSSError
 * @extends {LintError}
 */
class PostCSSError extends LintError {
	/**
	 * Creates an instance of PostCSSError.
	 *
	 * @param {Object} error 原始的PostCSS错误对象
	 *
	 * @memberOf PostCSSError
	 */
	constructor(error) {
		super({

			// 文件名
			fileName: error.file || (error.input && error.input.file),

			// 错误等级默认error，后面会覆盖
			// severity: 'error',

			// 行号
			lineNumber: error.line,

			// 列号
			columnNumber: error.column,

			// 错误信息
			message: error.text.replace(new RegExp('\\s*\\(' + error.rule + '\\)$'), ''),

			// 错误ID
			// rule: error.rule,

			// 源代码上下文
			source: (error.node && error.node.type && error.node.type !== 'root') ? String(error.node) : '',

			// 报错插件
			plugin: 'PostCSS',
			doc: error.rule && error.plugin === 'stylelint' && `https://stylelint.io/user-guide/rules/${ error.rule }/`
		}, error, {
			// 错误等级
			severity: error.severity && (POSTCSS_SEVERITY_MAP[error.severity] || error.severity) || 'error',
		});
	}
}

module.exports = PostCSSError;
<|endoftext|>


<commit_before>const riTable = document.querySelector("#services-ri")
const riRows = riTable.querySelectorAll("tbody tr")
const riFilterInput = document.querySelector("#ri-textfilter")
const riFilterBtns = document.querySelectorAll("[data-rifilterbtn]")
const riFilterSets = document.querySelectorAll("#ri-filtersets fieldset")
const riFilterChecks = document.querySelectorAll(".checkitem")
const riClearBtn = document.querySelector("#ri-clearfilters")
const riFilterCombo = document.querySelector("#ri-filtercombo")
const originalEls = document.querySelectorAll(".originalel")
const riCutoff = document.querySelector("#ri-cutoff")
const riCutoffBtn = riCutoff.querySelector("button")
let allFilters

// Initial cutoff
function riTableCutoff() {
  riRows.forEach((row, i) => {
    if (i > 12) {
      row.hidden = true
    }
  })
}

// Show empty results
function riEmptyCheck() {
  const noMatch = document.querySelector("#ri-nomatch")
  let insMessage = true
  if (noMatch) {
    noMatch.remove()
    riTable.classList.remove("dn")
  }
  riRows.forEach((row) => {
    if (row.hidden === false) {
      insMessage = false
      return
    }
  })
  if (insMessage) {
    riTable.classList.add("dn")
    riTable.insertAdjacentHTML(
      "afterend",
      '
No matches
'
    )
  }
}

// Remove inserted cells
function removeInsCells() {
  const inserts = document.querySelectorAll("[data-ins]")
  inserts.forEach((insert) => {
    insert.remove()
  })
}

// Hide cutoff elements
function riHideCutoffs() {
  riCutoff.hidden = true
}

// Hide original elements for restoration later
function hideOriginals(bool) {
  originalEls.forEach((ogEl) => {
    ogEl.hidden = bool
  })
}

function riCloseFilter() {
  const activeBtn = document.querySelector("[data-rifilterbtn].active")

  if (activeBtn) {
    activeBtn.classList.remove("active")
  }
  riFilterSets.forEach((set) => {
    set.hidden = true
    set.classList.remove("flex")
  })

  let clearFilters = true
  riFilterChecks.forEach((filterCheck) => {
    if (filterCheck.checked == true) {
      clearFilters = false
    }
  })
  if (clearFilters) {
    riClearFilters()
  }
}

// Clear all filters
function riClearFilters() {
  riHideCutoffs()
  removeInsCells()
  hideOriginals(false)

  riFilterChecks.forEach((filterCheck) => {
    filterCheck.checked = false
  })
  riRows.forEach((row) => {
    row.hidden = false
  })
  riFilterInput.disabled = false
  riFilterInput.value = ""

  riEmptyCheck()
  allFilters = {}
  riFilterCombo.innerHTML = ""
  riClearBtn.classList.add("dn")
}

riCutoffBtn.addEventListener("click", function () {
  riCloseFilter()
  riClearFilters()
})

riClearBtn.addEventListener("click", function () {
  riCloseFilter()
  riClearFilters()
})

// Remove filters via combo overview
riFilterCombo.addEventListener("click", function (e) {
  // Delete all
  if (e.target.dataset.rititle && !e.target.dataset.ritype) {
    let subButtons = document.querySelectorAll(
      'button[data-ritype="' + e.target.dataset.rititle + '"]'
    )
    subButtons.forEach((subButton) => {
      toggleCheckbox(subButton)
    })
  }

  // Delete one
  if (e.target.dataset.ritype && e.target.dataset.rititle) {
    toggleCheckbox(e.target)
  }
})

allFilters = {}
riFilterSets.forEach((set) => {
  allFilters[set.id] = []
})

// Render combofilter overview
function comboFilter(allFilters) {
  riFilterCombo.innerHTML = ""
  for (let activeFilter in allFilters) {
    if (allFilters[activeFilter].length > 0) {
      const filterTitle = document.querySelector(
        '[data-rifilterbtn="' + activeFilter + '"]'
      ).textContent
      let appliedFilter =
        '<button type="button" data-rititle="' +
        activeFilter +
        '" class="btn-link btn-link--filter">' +
        filterTitle +
        '</button>
'
      allFilters[activeFilter].forEach((filter) => {
        appliedFilter =
          appliedFilter +
          '<button type="button"' +
          'data-ritype="' +
          activeFilter +
          '" data-rititle="' +
          filter +
          '" class="btn-link btn-link--filter mrxs">' +
          filter +
          "</button>"
      })
      appliedFilter = appliedFilter + "
"
      riFilterCombo.insertAdjacentHTML("beforeend", appliedFilter)
      window.loadMybringIcons()
    }
  }
}

function toggleCheckbox(box) {
  const checkEl = document.querySelector(
    'input[data-filter="' +
      box.dataset.ritype +
      '"][value="' +
      box.dataset.rititle +
      '"'
  )
  checkEl.click()
}

// Render result
function renderResult(allFilters, showAllRows, filterKey) {
  removeInsCells()
  if (showAllRows) {
    riRows.forEach((row) => {
      hideOriginals(false)
      row.hidden = false
    })
  } else {
    hideOriginals(true)
    let prevFamily = ""
    let setId = 0
    let count = 1
    let prevApi = ""
    let apiId = 0
    let apiCount = 1
    // Always iterate in the same order
    const setOrder = ["rifamily", "riapi", "rireptype"]

    riRows.forEach((row) => {
      let setInd = 0
      let hideRow = true

      if (filterKey === "rirepname") {
        const query = allFilters
        row.hidden = true
        if (row.dataset[filterKey].toLowerCase().includes(query)) {
            row.hidden = false
        }

      } else {
      setOrder.forEach((setName) => {
        const filterSet = allFilters[setName]
        if (filterSet) {

          // Show all rows before applying filters
          if (setInd === 0 ) {
            row.hidden = false
          }

          if (row.hidden === false ) {
            let alreadyVisible = false
            filterSet.forEach((filter => {
              // Avoid re-checking already visible rows
              if (!alreadyVisible) {
                if (row.dataset[setName] === filter) {
                  hideRow = false
                  alreadyVisible = true
                }
                else {
                  hideRow = true
                }
              }
            }))
          }
          row.hidden = hideRow
          setInd = setInd + 1
        }
      })
    }

      // Insert additional cells
      if (row.hidden === false) {
            if (row.dataset.rifamily != prevFamily) {
              setId++
            }

            if (
              row.dataset.riapi != prevApi ||
              row.dataset.rifamily != prevFamily
            ) {
              prevApi = ""
              apiId++
              apiCount = 1
              row.insertAdjacentHTML(
                "afterbegin",
                '' +
                  row.dataset.riapi +
                  "	"
              )
            } else {
              apiCount++
              const apis = document.querySelectorAll(
                '[data-ins="' + setId + apiId + '"]'
              )
              apis.forEach((api) => {
                api.rowSpan = apiCount
              })
            }

            if (row.dataset.rifamily != prevFamily) {
              count = 1
              row.insertAdjacentHTML(
                "afterbegin",
                '' +
                  row.dataset.cntype +
                  "	"
              )
              row.insertAdjacentHTML(
                "afterbegin",
                '' +
                  row.dataset.rifamily +
                  "	"
              )
            } else {
              count++
              const sets = document.querySelectorAll(
                '[data-ins="' + setId + '"]'
              )
              sets.forEach((set) => {
                set.rowSpan = count
              })
            }

            prevApi = row.dataset.riapi
            prevFamily = row.dataset.rifamily
      }
    })
  }
}

// Input filter
riFilterInput.addEventListener("keyup", function (e) {
  riHideCutoffs()
  let query = e.target.value.toLowerCase()
  const notEmpty = query.length >= 1 && query !== "-"
  const filterKey = "rirepname"
  let showAllRows = true
  if (notEmpty) {
    riClearBtn.classList.remove("dn")
    showAllRows = false
  } else {
    riClearBtn.classList.add("dn")
  }

  renderResult(query, showAllRows, filterKey)
  riEmptyCheck()
})

// Set filters
riFilterBtns.forEach((filterBtn) => {
  filterBtn.addEventListener("click", function (e) {
    const filterKey = e.target.dataset.rifilterbtn
    if (e.target.classList.contains("active")) {
      riCloseFilter()
      return
    }
    riCloseFilter()
    riClearBtn.classList.remove("dn")

    e.target.classList.add("active")
    riFilterInput.disabled = true
    riFilterSets.forEach((set) => {
      if (set.id === filterKey) {
        if (set.hidden === false) {
          set.hidden = true
          set.classList.remove("flex")
        } else {
          set.hidden = false
          set.classList.add("flex")
        }
      } else {
        set.hidden = true
        set.classList.remove("flex")
      }
    })

    const riSetChecks = document.querySelectorAll(
      'input[data-filter="' + filterKey + '"]'
    )

    riSetChecks.forEach((setCheck) => {
      setCheck.addEventListener("click", function (e) {
        const filterKey = e.target.dataset.filter

        // Store active filters
        allFilters[filterKey] = []
        riSetChecks.forEach((setCheck) => {
          if (setCheck.checked === true) {
            allFilters[filterKey].push(setCheck.value)
          }
        })

        let showAllRows = true
        for (const [k, _] of Object.entries(allFilters)) {
          if (allFilters[k].length > 0) {
            showAllRows = false
          }
        }

        comboFilter(allFilters)
        renderResult(allFilters, showAllRows, filterKey)
        riEmptyCheck()
      })
    })
  })
})

document.addEventListener("DOMContentLoaded", function () {
  riTableCutoff()
})
<commit_msg>Refactor ri js<commit_after>const table = document.querySelector("#services-ri")
const rows = table.querySelectorAll("tbody tr")
const filterInput = document.querySelector("#ri-textfilter")
const filterSetBtns = document.querySelectorAll("[data-rifilterbtn]")
const filterSets = document.querySelectorAll("#ri-filtersets fieldset")
const filterChecks = document.querySelectorAll(".checkitem")
const clearBtn = document.querySelector("#ri-clearfilters")
const filterCombo = document.querySelector("#ri-filtercombo")
const originalEls = document.querySelectorAll(".originalel")
const cutoff = document.querySelector("#ri-cutoff")
const cutoffBtn = cutoff.querySelector("button")
let allFilters

function cutoffTable() {
  rows.forEach((row, i) => {
    if (i > 12) {
      row.hidden = true
    }
  })
}

function checkNoMatches() {
  const noMatch = document.querySelector("#ri-nomatch")
  let insMessage = true
  if (noMatch) {
    noMatch.remove()
    table.classList.remove("dn")
  }
  rows.forEach((row) => {
    if (row.hidden === false) {
      insMessage = false
      return
    }
  })
  if (insMessage) {
    table.classList.add("dn")
    table.insertAdjacentHTML(
      "afterend",
      '
No matches
'
    )
  }
}

function removeInsCells() {
  const inserts = document.querySelectorAll("[data-ins]")
  inserts.forEach((insert) => {
    insert.remove()
  })
}

function hideCutoffs() {
  cutoff.hidden = true
}

function hideOriginals(bool) {
  originalEls.forEach((ogEl) => {
    ogEl.hidden = bool
  })
}

function closeFilterSet() {
  const activeBtn = document.querySelector("[data-rifilterbtn].active")

  if (activeBtn) {
    activeBtn.classList.remove("active")
  }
  filterSets.forEach((set) => {
    set.hidden = true
    set.classList.remove("flex")
  })

  let clearFilters = true
  filterChecks.forEach((filterCheck) => {
    if (filterCheck.checked == true) {
      clearFilters = false
    }
  })
  if (clearFilters) {
    clearAllFilters()
  }
}

function clearAllFilters() {
  hideCutoffs()
  removeInsCells()
  hideOriginals(false)

  filterChecks.forEach((filterCheck) => {
    filterCheck.checked = false
  })
  rows.forEach((row) => {
    row.hidden = false
  })
  filterInput.disabled = false
  filterInput.value = ""

  checkNoMatches()
  allFilters = {}
  filterCombo.innerHTML = ""
  clearBtn.classList.add("dn")
}

cutoffBtn.addEventListener("click", function () {
  closeFilterSet()
  clearAllFilters()
})

clearBtn.addEventListener("click", function () {
  closeFilterSet()
  clearAllFilters()
})

// Remove filters via combo overview
filterCombo.addEventListener("click", function (e) {
  // Delete all
  if (e.target.dataset.rititle && !e.target.dataset.ritype) {
    let subButtons = document.querySelectorAll(
      'button[data-ritype="' + e.target.dataset.rititle + '"]'
    )
    subButtons.forEach((subButton) => {
      toggleCheckbox(subButton)
    })
  }

  // Delete one
  if (e.target.dataset.ritype && e.target.dataset.rititle) {
    toggleCheckbox(e.target)
  }
})

allFilters = {}
filterSets.forEach((set) => {
  allFilters[set.id] = []
})

function makeComboOverview(allFilters) {
  filterCombo.innerHTML = ""
  for (let activeFilter in allFilters) {
    if (allFilters[activeFilter].length > 0) {
      const filterTitle = document.querySelector(
        '[data-rifilterbtn="' + activeFilter + '"]'
      ).textContent
      let appliedFilter =
        '<button type="button" data-rititle="' +
        activeFilter +
        '" class="btn-link btn-link--filter">' +
        filterTitle +
        '</button>
'
      allFilters[activeFilter].forEach((filter) => {
        appliedFilter =
          appliedFilter +
          '<button type="button"' +
          'data-ritype="' +
          activeFilter +
          '" data-rititle="' +
          filter +
          '" class="btn-link btn-link--filter mrxs">' +
          filter +
          "</button>"
      })
      appliedFilter = appliedFilter + "
"
      filterCombo.insertAdjacentHTML("beforeend", appliedFilter)
      window.loadMybringIcons()
    }
  }
}

function toggleCheckbox(box) {
  const checkEl = document.querySelector(
    'input[data-filter="' +
      box.dataset.ritype +
      '"][value="' +
      box.dataset.rititle +
      '"'
  )
  checkEl.click()
}

function renderResult(allFilters, showAllRows, filterKey) {
  removeInsCells()
  if (showAllRows) {
    rows.forEach((row) => {
      hideOriginals(false)
      row.hidden = false
    })
  } else {
    hideOriginals(true)
    let prevFamily = ""
    let setId = 0
    let rowspan = 1
    let prevApi = ""
    let apiId = 0
    let apiCount = 1
    // Always iterate in the same order
    const setOrder = ["rifamily", "riapi", "rireptype"]

    rows.forEach((row) => {
      let setInd = 0
      let hideRow = true

      if (filterKey === "rirepname") {
        const query = allFilters
        row.hidden = true
        if (row.dataset[filterKey].toLowerCase().includes(query)) {
            row.hidden = false
        }

      } else {
      setOrder.forEach((setName) => {
        const filterSet = allFilters[setName]
        if (filterSet) {

          // Show all rows before applying filters
          if (setInd === 0 ) {
            row.hidden = false
          }

          if (row.hidden === false ) {
            let alreadyVisible = false
            filterSet.forEach((filter => {
              // Avoid re-checking already visible rows
              if (!alreadyVisible) {
                if (row.dataset[setName] === filter) {
                  hideRow = false
                  alreadyVisible = true
                }
                else {
                  hideRow = true
                }
              }
            }))
          }
          row.hidden = hideRow
          setInd = setInd + 1
        }
      })
    }

      // Insert additional cells
      if (row.hidden === false) {
            if (row.dataset.rifamily != prevFamily) {
              setId++
            }

            if (
              row.dataset.riapi != prevApi ||
              row.dataset.rifamily != prevFamily
            ) {
              prevApi = ""
              apiId++
              apiCount = 1
              row.insertAdjacentHTML(
                "afterbegin",
                '' +
                  row.dataset.riapi +
                  "	"
              )
            } else {
              apiCount++
              const apis = document.querySelectorAll(
                '[data-ins="' + setId + apiId + '"]'
              )
              apis.forEach((api) => {
                api.rowSpan = apiCount
              })
            }

            if (row.dataset.rifamily != prevFamily) {
              rowspan = 1
              row.insertAdjacentHTML(
                "afterbegin",
                '' +
                  row.dataset.cntype +
                  "	"
              )
              row.insertAdjacentHTML(
                "afterbegin",
                '' +
                  row.dataset.rifamily +
                  "	"
              )
            } else {
              rowspan++
              const sets = document.querySelectorAll(
                '[data-ins="' + setId + '"]'
              )
              sets.forEach((set) => {
                set.rowSpan = rowspan
              })
            }

            prevApi = row.dataset.riapi
            prevFamily = row.dataset.rifamily
      }
    })
  }
}

// Input filter
filterInput.addEventListener("keyup", function (e) {
  hideCutoffs()
  let query = e.target.value.toLowerCase()
  const notEmpty = query.length >= 1 && query !== "-"
  const filterKey = "rirepname"
  let showAllRows = true
  if (notEmpty) {
    clearBtn.classList.remove("dn")
    showAllRows = false
  } else {
    clearBtn.classList.add("dn")
  }

  renderResult(query, showAllRows, filterKey)
  checkNoMatches()
})

// Set filters
filterSetBtns.forEach((filterBtn) => {
  filterBtn.addEventListener("click", function (e) {
    const filterKey = e.target.dataset.rifilterbtn
    if (e.target.classList.contains("active")) {
      closeFilterSet()
      return
    }
    closeFilterSet()
    clearBtn.classList.remove("dn")

    e.target.classList.add("active")
    filterInput.disabled = true
    filterSets.forEach((set) => {
      if (set.id === filterKey) {
        if (set.hidden === false) {
          set.hidden = true
          set.classList.remove("flex")
        } else {
          set.hidden = false
          set.classList.add("flex")
        }
      } else {
        set.hidden = true
        set.classList.remove("flex")
      }
    })

    const riSetChecks = document.querySelectorAll(
      'input[data-filter="' + filterKey + '"]'
    )

    riSetChecks.forEach((setCheck) => {
      setCheck.addEventListener("click", function (e) {
        const filterKey = e.target.dataset.filter

        // Store active filters
        allFilters[filterKey] = []
        riSetChecks.forEach((setCheck) => {
          if (setCheck.checked === true) {
            allFilters[filterKey].push(setCheck.value)
          }
        })

        let showAllRows = true
        for (const [k, _] of Object.entries(allFilters)) {
          if (allFilters[k].length > 0) {
            showAllRows = false
          }
        }

        makeComboOverview(allFilters)
        renderResult(allFilters, showAllRows, filterKey)
        checkNoMatches()
      })
    })
  })
})

document.addEventListener("DOMContentLoaded", function () {
  cutoffTable()
})
<|endoftext|>


<commit_before><commit_msg>default to all plots if saved design does not have plot filter<commit_after><|endoftext|>


<commit_before>/**
 * New node file
 */
		$(document).ready(function() {

			/*
			* Plugin intialization
			*/
	    	$('#pagepiling').pagepiling({
	    		menu: '#menu',
	    		anchors: ['page1', 'page2', 'page3', 'page4','page5'],				// 영역 ID를 배열로 선언해 둔다
			    sectionsColor: ['white', '#ee005a', '#2C3E50', '#39C','#e8e8e8'],	// 배경셋 셋팅
			    navigation: {
			    	// 네비게이션 위치 및 툴팁 부분 문구를 배열로 넣을 수 있따.
			    	'position': 'left',
			   		'tooltips': ['Page 1', 'Page 2', 'Page 3', 'Page 4','Page 5']
			   	},
			    afterRender: function(){
			    	// 페이지 초기 랜더시 이벤트 영역 페이지 랜더시 이벤트 발생 영역
			    	$('#pp-nav').addClass('custom');
			    	console.log("render");
			    },
			    afterLoad: function(anchorLink, index){
			    	// 페이지 로드 된 후 이벤트 진행 가능한 영역
			    	if(index>1){
			    		$('#pp-nav').removeClass('custom');
			    	}else{
			    		$('#pp-nav').addClass('custom');
			    	}
			    	console.log("index  " + index);
			    }
			});

	    });<commit_msg>scroll 플러그인 관련 내용  주석 추가<commit_after>/**
 * New node file
 */

/**
 *  Plugin 예제 url : http://alvarotrigo.com/pagePiling/
 */
		$(document).ready(function() {

			/*
			* Plugin intialization
			*/
	    	$('#pagepiling').pagepiling({
	    		menu: '#menu',
	    		anchors: ['page1', 'page2', 'page3', 'page4','page5'],				// 영역 ID를 배열로 선언해 둔다
			    sectionsColor: ['white', '#ee005a', '#2C3E50', '#39C','#e8e8e8'],	// 배경셋 셋팅
			    navigation: {
			    	// 네비게이션 위치 및 툴팁 부분 문구를 배열로 넣을 수 있따.
			    	'position': 'left',
			   		'tooltips': ['Page 1', 'Page 2', 'Page 3', 'Page 4','Page 5']
			   	},
			    afterRender: function(){
			    	// 페이지 초기 랜더시 이벤트 영역 페이지 랜더시 이벤트 발생 영역
			    	$('#pp-nav').addClass('custom');
			    	console.log("render");
			    },
			    afterLoad: function(anchorLink, index){
			    	// 페이지 로드 된 후 이벤트 진행 가능한 영역
			    	if(index>1){
			    		$('#pp-nav').removeClass('custom');
			    	}else{
			    		$('#pp-nav').addClass('custom');
			    	}
			    	console.log("index  " + index);
			    }
			});

	    });
<|endoftext|>


<commit_before>var display = require("../util/display");
var eb = require("../eventBus");
var template = require("../tmpl/analyze-group");
var CommentModel = require("../models/comment");
var Handlebones = require("handlebones");
var Utils = require("../util/utils");

var NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW = 5;

var el_carouselSelector = "#carousel";

function addMultipleOwlItems(htmlStrings, targetPosition) {
    var base = this,
        i,
        position;

    if (!htmlStrings || !htmlStrings.length) {return false; }

    if (base.$elem.children().length === 0) {
        for (i = 0; i < htmlStrings.length; i++) {
          base.$elem.append(htmlStrings[i]);
        }
        base.setVars();
        return false;
    }
    base.unWrap();
    if (targetPosition === undefined || targetPosition === -1) {
        position = -1;
    } else {
        position = targetPosition;
    }
    if (position >= base.$userItems.length || position === -1) {
      for (i = 0; i < htmlStrings.length; i++) {
        base.$userItems.eq(-1).after(htmlStrings[i]);
      }
    } else {
      for (i = 0; i < htmlStrings.length; i++) {
        base.$userItems.eq(position).before(htmlStrings[i]);
      }
    }

    base.setVars();
}

function bbCompare(propertyName, a, b) {
  var x = b.get(propertyName) - a.get(propertyName);
  return x;
}
function bbCompareAscending(propertyName, a, b) {
  return -bbCompare(propertyName, a, b);
}
function compareTieBreaker(a, b) {
  var x = bbCompare("stars", a, b);
  x = x || a.get("txt").length - b.get("txt").length; // shorter comments first
  x = x || bbCompare("created", a, b);
  // x = x || (b.get("txt").toLowerCase() < a.get("txt").toLowerCase()) ? 1 : -1; // alphabetic
  return x;
}
function comparatorAgree(a, b) {
  var x = bbCompare("A", a, b);
  x = x || bbCompareAscending("D", a, b);
  return x || compareTieBreaker(a, b);
}
function comparatorDisagree(a, b) {
  var x = bbCompare("D", a, b);
  x = x || bbCompareAscending("A", a, b);
  return x || compareTieBreaker(a, b);
}
function comparatorDivisive(a, b) {
  var b_agrees = b.get("A");
  var b_disagrees = b.get("D");
  var a_agrees = a.get("A");
  var a_disagrees = a.get("D");
  var b_product = b_agrees * b_disagrees;
  var a_product = a_agrees * a_disagrees;
  var b_sum = b_agrees + b_disagrees + 1; // Add 1 to prevent divide-by-zero
  var a_sum = a_agrees + a_disagrees + 1; // Add 1 to prevent divide-by-zero
  var x = b_product/b_sum - a_product/a_sum;
  x = x || bbCompareAscending("A", a, b);
  return x || compareTieBreaker(a, b);
}

function comparatorStars(a, b) {
  var x = bbCompare("stars", a, b);
  return x || compareTieBreaker(a, b);
}

module.exports = Handlebones.View.extend({
    name: "analyze-global-view",
    template: template,
    tidsForGroup: null,
    events: {
      "click #sortAgree": "sortAgree",
      "click #sortDisagree": "sortDisagree",
      "click #sortDivisive": "sortDivisive",
      "keyup input": "updateSearch",
      "propertychange input": "updateSearch",
      submit: function(e) {
        e.preventDefault();
      },
      // "rendered:collection": function() {
      //   this.selectFirst();
      //   console.log('rendered:collection');
      // },
      rendered: function() {
        var that = this;
        var items = this.$(".query_result_item");
        items.on("mouseover", function() {
            $(this).addClass("hover");
        });
        items.on("mouseout", function() {
            $(this).removeClass("hover");
        });
      }
    },
    selectSortModes: function(chosenButtonSelector) {
      this.$("#sortAgree,#sortDisagree,#sortDivisive").removeClass("enabled");
      this.$(chosenButtonSelector).addClass("enabled");
    },
    selectFirst: function() {

    },
  searchEnabled: true,
  sortEnabled: true,

  groupInfo: function() {
    return this.parent.groupInfo();
  },

  // sort with the current comparator
  sort: function() {
    this.collection.sort();
    // this.selectFirst();
  },
  sortAgree: function(e) {
    this.collection.comparator = comparatorAgree;
    this.collection.sort();
    // this.selectFirst();
    this.selectSortModes("#sortAgree");
  },
  sortDisagree: function(e) {
    this.collection.comparator = comparatorDisagree;
    this.collection.sort();
    // this.selectFirst();
    this.selectSortModes("#sortDisagree");
  },
  sortDivisive: function(e) {
    this.collection.comparator = comparatorDivisive;
    this.collection.sort();
    // this.selectFirst();
    this.selectSortModes("#sortDivisive");
  },
  useCarousel: function() {
    return true;
    // return !Utils.isIE8() && display.xs();
  },
  hideCarousel: function() {
    // this.$("#commentListView").show();
    // this.$("#carousel").hide();
  },
  showCarousel: function() {
    this.$("#commentListView").hide();
    this.$("#carousel").show();
  },
  updateSearch: function(e) {
    this.searchString = e.target.value;
    this.deselectComments();
    // this.selectFirst();
  },
  deselectComments: function() {
    eb.trigger(eb.commentSelected, false);
  },
  renderWithCarousel: function(gid, tidToR) {
    var that = this;
    // let stack breathe
    setTimeout(function() {

      var indexToTid = [];

      var info = that.groupInfo();
      if (!info.repness || !info.repness.length) {
        console.error("missing repness");
        return;
      }
      var repnessInfo = info.repness.slice(0);
      repnessInfo.sort(function(a, b) {
        if (a["repful-for"] === "agree" && b["repful-for"] === "disagree") {
          return -1;
        }
        if (b["repful-for"] === "agree" && a["repful-for"] === "disagree") {
          return 1;
        }
        // secondary sort is descending repness
        // TODO condider confidence values
        return b.repness - a.repness;
      });
      var tids = _.pluck(repnessInfo, "tid");

      // Copy comments out of collection. don't want to sort collection, since it's shared with Analyze View.
      var comments = that.collection.models.slice(0);
      comments = _.filter(comments, function(comment) {
        return _.contains(tids, comment.get('tid'));
      });

      comments = _.indexBy(comments, "id"); // id is tid
      // use ordering of tids, but fetch out the comments we want.
      comments = _.map(tids, function(tid) {
        return comments[tid];
      });

      // // XXX HACK - should ideally be incorporated in the primary sort that we do before truncating the array.
      // comments.sort(function(a, b) {

      //     var vA = info.votes[a.get('tid')];
      //     var vB = info.votes[b.get('tid')];
      //     var percentA = (vA.gA_total / info.count * 100);
      //     var percentB = (vB.gA_total / info.count * 100);
      //     return percentB - percentA;
      // });

      var htmlStrings = _.map(comments, function(c) {
          var tid = c.get('tid');
          var repness = tidToR[tid];
          var repfullForAgree = repness["repful-for"] === "agree";
          indexToTid.push(tid);
          var header;
          var v = info.votes[tid];

          var percent = repfullForAgree ?
            "▲ " + ((v.gA_total / info.count * 100) >> 0) : // WARNING duplicated in analyze-comment.js
            "▼ " + ((v.gD_total / info.count * 100) >> 0); // WARNING duplicated in analyze-comment.js
          var leClass = repfullForAgree ?
            "a":
            "d";
          var count = repfullForAgree ?
            v.gA_total :
            v.gD_total;
          // L10N gods forgive me
          var word = repfullForAgree ?
            "agreed" :
            "disagreed";
          var bodyColor = "black"; //repfullForAgree ?
            // "#20442F" :
            // "rgb(68, 33, 33)";
          var backgroundColor = "none"; //repfullForAgree ?
            // "rgba(46, 204, 84, 0.07)" :
            // "rgba(231, 76, 60, 0.05)";
          header =
              "" + percent + "% " +
              "("+ count+"/"+info.count +") of this group "+ word + "";

          var html =
            "
" +
              "
" +
                header +
              "
" +
              c.get("txt") +
            "
";
          return html;
        });

        // let stack breathe
        setTimeout(function() {
          $(el_carouselSelector).html("");
          // $(el_carouselSelector).css("overflow", "hidden");

          // $(el_carouselSelector).append("

");
          $(el_carouselSelector).append("

");

          var results = $("#smallWindow");
          results.addClass("owl-carousel");
          // results.css('background-color', 'yellow');

          if (results.data('owlCarousel')) {
            results.data('owlCarousel').destroy();
          }
          results.owlCarousel({
            items : NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW, //3 items above 1000px browser width
            // itemsDesktop : [1000,5], //5 items between 1000px and 901px
            // itemsDesktopSmall : [900,3], // betweem 900px and 601px
            // itemsTablet: [600,2], //2 items between 600 and 0
            // itemsMobile : false // itemsMobile disabled - inherit from itemsTablet option
             singleItem : true,
             // autoHeight : true,
           //  transitionStyle: "fade", // this should enable CSS3 transitions
            afterInit : function(elem){
              if (!display.xs()) {
                this.owlControls.prependTo(elem);
              }
            },
             afterMove: (function() {return function() {
                var tid = indexToTid[this.currentItem];
                setTimeout(function() {
                    eb.trigger(eb.commentSelected, tid);
                }, 200);

            }}())
          });

          $(el_carouselSelector).on("click", function(e) {
            var owl = $("#smallWindow").data('owlCarousel');
            // var $comment = $(e);
            var index = $(e.target).data("idx");
            if (_.isNumber(index)) {
              owl.goTo(index, true);
            }
            // alert(e);
          });
          addMultipleOwlItems.call(results.data('owlCarousel'), htmlStrings);
          // Auto-select the first comment.
          eb.trigger(eb.commentSelected, indexToTid[0]);
          // $(el_carouselSelector).find(".query_result_item").first().trigger("click");
        }, 0);
      }, 0);
  },
  initialize: function(options) {
    var that = this;
    this.collection = options.collection;
    this.collection.comparator = comparatorAgree;

    var getTidsForGroup = options.getTidsForGroup;

    this.fetcher = options.fetcher;

    // TODO MOVE THIS LOGIC TO THE GROUP-SPECIFIC CODE...
    // this is getting crappy.

    function doFetch(gid) {
      that.collection.firstFetchPromise.then(function() {
        if (gid >= 0) {
          that.$("#commentSearch").hide();
          that.$("#commentSort").hide();
          // that.$("#groupStats").show();
          that.sortEnabled = false;
          that.searchEnabled = false;
          getTidsForGroup(gid, NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW).then(function(o) {
            that.tidsForGroup = o.tids;
            that.renderWithCarousel(gid, o.tidToR);
            // that.selectFirst();
          });
        }
      });
    } // End doFetch

    if (!_.isUndefined(options.gid)) {
      doFetch(options.gid);
    } else {
      eb.on(eb.clusterClicked, function(gid) {
        doFetch(gid);
      });
    }
  }
});
<commit_msg>don't reorder repfull comments on client, will be done on worker<commit_after>var display = require("../util/display");
var eb = require("../eventBus");
var template = require("../tmpl/analyze-group");
var CommentModel = require("../models/comment");
var Handlebones = require("handlebones");
var Utils = require("../util/utils");

var NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW = 5;

var el_carouselSelector = "#carousel";

function addMultipleOwlItems(htmlStrings, targetPosition) {
    var base = this,
        i,
        position;

    if (!htmlStrings || !htmlStrings.length) {return false; }

    if (base.$elem.children().length === 0) {
        for (i = 0; i < htmlStrings.length; i++) {
          base.$elem.append(htmlStrings[i]);
        }
        base.setVars();
        return false;
    }
    base.unWrap();
    if (targetPosition === undefined || targetPosition === -1) {
        position = -1;
    } else {
        position = targetPosition;
    }
    if (position >= base.$userItems.length || position === -1) {
      for (i = 0; i < htmlStrings.length; i++) {
        base.$userItems.eq(-1).after(htmlStrings[i]);
      }
    } else {
      for (i = 0; i < htmlStrings.length; i++) {
        base.$userItems.eq(position).before(htmlStrings[i]);
      }
    }

    base.setVars();
}

function bbCompare(propertyName, a, b) {
  var x = b.get(propertyName) - a.get(propertyName);
  return x;
}
function bbCompareAscending(propertyName, a, b) {
  return -bbCompare(propertyName, a, b);
}
function compareTieBreaker(a, b) {
  var x = bbCompare("stars", a, b);
  x = x || a.get("txt").length - b.get("txt").length; // shorter comments first
  x = x || bbCompare("created", a, b);
  // x = x || (b.get("txt").toLowerCase() < a.get("txt").toLowerCase()) ? 1 : -1; // alphabetic
  return x;
}
function comparatorAgree(a, b) {
  var x = bbCompare("A", a, b);
  x = x || bbCompareAscending("D", a, b);
  return x || compareTieBreaker(a, b);
}
function comparatorDisagree(a, b) {
  var x = bbCompare("D", a, b);
  x = x || bbCompareAscending("A", a, b);
  return x || compareTieBreaker(a, b);
}
function comparatorDivisive(a, b) {
  var b_agrees = b.get("A");
  var b_disagrees = b.get("D");
  var a_agrees = a.get("A");
  var a_disagrees = a.get("D");
  var b_product = b_agrees * b_disagrees;
  var a_product = a_agrees * a_disagrees;
  var b_sum = b_agrees + b_disagrees + 1; // Add 1 to prevent divide-by-zero
  var a_sum = a_agrees + a_disagrees + 1; // Add 1 to prevent divide-by-zero
  var x = b_product/b_sum - a_product/a_sum;
  x = x || bbCompareAscending("A", a, b);
  return x || compareTieBreaker(a, b);
}

function comparatorStars(a, b) {
  var x = bbCompare("stars", a, b);
  return x || compareTieBreaker(a, b);
}

module.exports = Handlebones.View.extend({
    name: "analyze-global-view",
    template: template,
    tidsForGroup: null,
    events: {
      "click #sortAgree": "sortAgree",
      "click #sortDisagree": "sortDisagree",
      "click #sortDivisive": "sortDivisive",
      "keyup input": "updateSearch",
      "propertychange input": "updateSearch",
      submit: function(e) {
        e.preventDefault();
      },
      // "rendered:collection": function() {
      //   this.selectFirst();
      //   console.log('rendered:collection');
      // },
      rendered: function() {
        var that = this;
        var items = this.$(".query_result_item");
        items.on("mouseover", function() {
            $(this).addClass("hover");
        });
        items.on("mouseout", function() {
            $(this).removeClass("hover");
        });
      }
    },
    selectSortModes: function(chosenButtonSelector) {
      this.$("#sortAgree,#sortDisagree,#sortDivisive").removeClass("enabled");
      this.$(chosenButtonSelector).addClass("enabled");
    },
    selectFirst: function() {

    },
  searchEnabled: true,
  sortEnabled: true,

  groupInfo: function() {
    return this.parent.groupInfo();
  },

  // sort with the current comparator
  sort: function() {
    this.collection.sort();
    // this.selectFirst();
  },
  sortAgree: function(e) {
    this.collection.comparator = comparatorAgree;
    this.collection.sort();
    // this.selectFirst();
    this.selectSortModes("#sortAgree");
  },
  sortDisagree: function(e) {
    this.collection.comparator = comparatorDisagree;
    this.collection.sort();
    // this.selectFirst();
    this.selectSortModes("#sortDisagree");
  },
  sortDivisive: function(e) {
    this.collection.comparator = comparatorDivisive;
    this.collection.sort();
    // this.selectFirst();
    this.selectSortModes("#sortDivisive");
  },
  useCarousel: function() {
    return true;
    // return !Utils.isIE8() && display.xs();
  },
  hideCarousel: function() {
    // this.$("#commentListView").show();
    // this.$("#carousel").hide();
  },
  showCarousel: function() {
    this.$("#commentListView").hide();
    this.$("#carousel").show();
  },
  updateSearch: function(e) {
    this.searchString = e.target.value;
    this.deselectComments();
    // this.selectFirst();
  },
  deselectComments: function() {
    eb.trigger(eb.commentSelected, false);
  },
  renderWithCarousel: function(gid, tidToR) {
    var that = this;
    // let stack breathe
    setTimeout(function() {

      var indexToTid = [];

      var info = that.groupInfo();
      if (!info.repness || !info.repness.length) {
        console.error("missing repness");
        return;
      }
      var repnessInfo = info.repness.slice(0);
      // repnessInfo.sort(function(a, b) {
      //   if (a["repful-for"] === "agree" && b["repful-for"] === "disagree") {
      //     return -1;
      //   }
      //   if (b["repful-for"] === "agree" && a["repful-for"] === "disagree") {
      //     return 1;
      //   }
      //   // secondary sort is descending repness
      //   // TODO condider confidence values
      //   return b.repness - a.repness;
      // });

      var tids = _.pluck(repnessInfo, "tid");

      // Copy comments out of collection. don't want to sort collection, since it's shared with Analyze View.
      var comments = that.collection.models.slice(0);
      comments = _.filter(comments, function(comment) {
        return _.contains(tids, comment.get('tid'));
      });

      comments = _.indexBy(comments, "id"); // id is tid
      // use ordering of tids, but fetch out the comments we want.
      comments = _.map(tids, function(tid) {
        return comments[tid];
      });

      // // XXX HACK - should ideally be incorporated in the primary sort that we do before truncating the array.
      // comments.sort(function(a, b) {

      //     var vA = info.votes[a.get('tid')];
      //     var vB = info.votes[b.get('tid')];
      //     var percentA = (vA.gA_total / info.count * 100);
      //     var percentB = (vB.gA_total / info.count * 100);
      //     return percentB - percentA;
      // });

      var htmlStrings = _.map(comments, function(c) {
          var tid = c.get('tid');
          var repness = tidToR[tid];
          var repfullForAgree = repness["repful-for"] === "agree";
          indexToTid.push(tid);
          var header;
          var v = info.votes[tid];

          var percent = repfullForAgree ?
            "▲ " + ((v.gA_total / info.count * 100) >> 0) : // WARNING duplicated in analyze-comment.js
            "▼ " + ((v.gD_total / info.count * 100) >> 0); // WARNING duplicated in analyze-comment.js
          var leClass = repfullForAgree ?
            "a":
            "d";
          var count = repfullForAgree ?
            v.gA_total :
            v.gD_total;
          // L10N gods forgive me
          var word = repfullForAgree ?
            "agreed" :
            "disagreed";
          var bodyColor = "black"; //repfullForAgree ?
            // "#20442F" :
            // "rgb(68, 33, 33)";
          var backgroundColor = "none"; //repfullForAgree ?
            // "rgba(46, 204, 84, 0.07)" :
            // "rgba(231, 76, 60, 0.05)";
          header =
              "" + percent + "% " +
              "("+ count+"/"+info.count +") of this group "+ word + "";

          var html =
            "
" +
              "
" +
                header +
              "
" +
              c.get("txt") +
            "
";
          return html;
        });

        // let stack breathe
        setTimeout(function() {
          $(el_carouselSelector).html("");
          // $(el_carouselSelector).css("overflow", "hidden");

          // $(el_carouselSelector).append("

");
          $(el_carouselSelector).append("

");

          var results = $("#smallWindow");
          results.addClass("owl-carousel");
          // results.css('background-color', 'yellow');

          if (results.data('owlCarousel')) {
            results.data('owlCarousel').destroy();
          }
          results.owlCarousel({
            items : NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW, //3 items above 1000px browser width
            // itemsDesktop : [1000,5], //5 items between 1000px and 901px
            // itemsDesktopSmall : [900,3], // betweem 900px and 601px
            // itemsTablet: [600,2], //2 items between 600 and 0
            // itemsMobile : false // itemsMobile disabled - inherit from itemsTablet option
             singleItem : true,
             // autoHeight : true,
           //  transitionStyle: "fade", // this should enable CSS3 transitions
            afterInit : function(elem){
              if (!display.xs()) {
                this.owlControls.prependTo(elem);
              }
            },
             afterMove: (function() {return function() {
                var tid = indexToTid[this.currentItem];
                setTimeout(function() {
                    eb.trigger(eb.commentSelected, tid);
                }, 200);

            }}())
          });

          $(el_carouselSelector).on("click", function(e) {
            var owl = $("#smallWindow").data('owlCarousel');
            // var $comment = $(e);
            var index = $(e.target).data("idx");
            if (_.isNumber(index)) {
              owl.goTo(index, true);
            }
            // alert(e);
          });
          addMultipleOwlItems.call(results.data('owlCarousel'), htmlStrings);
          // Auto-select the first comment.
          eb.trigger(eb.commentSelected, indexToTid[0]);
          // $(el_carouselSelector).find(".query_result_item").first().trigger("click");
        }, 0);
      }, 0);
  },
  initialize: function(options) {
    var that = this;
    this.collection = options.collection;
    this.collection.comparator = comparatorAgree;

    var getTidsForGroup = options.getTidsForGroup;

    this.fetcher = options.fetcher;

    // TODO MOVE THIS LOGIC TO THE GROUP-SPECIFIC CODE...
    // this is getting crappy.

    function doFetch(gid) {
      that.collection.firstFetchPromise.then(function() {
        if (gid >= 0) {
          that.$("#commentSearch").hide();
          that.$("#commentSort").hide();
          // that.$("#groupStats").show();
          that.sortEnabled = false;
          that.searchEnabled = false;
          getTidsForGroup(gid, NUMBER_OF_REPRESENTATIVE_COMMENTS_TO_SHOW).then(function(o) {
            that.tidsForGroup = o.tids;
            that.renderWithCarousel(gid, o.tidToR);
            // that.selectFirst();
          });
        }
      });
    } // End doFetch

    if (!_.isUndefined(options.gid)) {
      doFetch(options.gid);
    } else {
      eb.on(eb.clusterClicked, function(gid) {
        doFetch(gid);
      });
    }
  }
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import styles from './settings.css';
import Checkbox from './Checkbox';
import Background from './Background';

const StandardSelect = ({ selectProps = {}, children }) => {
  const { style } = selectProps;
  return (

      <select
        className="form-control"
        {...selectProps}
        style={Object.assign({}, {
          color: '#6FDCFF',
          fontSize: '18px',
          lineHeight: '18px',
          fontWeight: 'bold',
          borderColor: '#0079A1'
        }, style)}
      >
        {children}
      </select>

          arrow_drop_down

  );
};
StandardSelect.propTypes = {
  selectProps: PropTypes.object,
  children: PropTypes.array
};

const BikeContext = ({ children }) => (

        {children}

);
BikeContext.propTypes = {
  children: PropTypes.number
};

const PlainContext = ({ children }) => (

        {children}

);
PlainContext.propTypes = {
  children: PropTypes.string
};

const RaceDistanceSetting = () => (

        Race Distance

            <input
              id="distance-input"
              style={{
                padding: '6px 10px'
              }}
              className="form-control context"
              type="text"
              defaultValue="100"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

          <StandardSelect
            selectProps={{
              style: {
                width: '130px'
              },
              onChange: () => {
              },
              defaultValue: 'meters'
            }}
          >
            <option value="feet">feet</option>
            <option value="meters">meters</option>
          </StandardSelect>

);

const RaceDurationSetting = () => (

        Trial Duration

            <PlainContext>H</PlainContext>
            <input
              style={{
                padding: '6px 20px'
              }}
              className="form-control context"
              type="text"
              defaultValue="00"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

            <PlainContext>M</PlainContext>
            <input
              style={{
                padding: '6px 20px'
              }}
              className="form-control context"
              type="text"
              defaultValue="00"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

            <PlainContext>S</PlainContext>
            <input
              style={{
                padding: '6px 20px'
              }}
              className="form-control context"
              type="text"
              defaultValue="00"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

);

const RaceScreenSettings = () => (

          Race Screen Background

        <Background />

          Race Clock Background

        <Background />

);

const IntermissionScreenSettings = () => (

          Intermission Screen Background

        <Background />

      add_circle
      Add an Intermission Screen Background

          Intermission Screen Options

        <Checkbox>Show standings</Checkbox>
        <Checkbox>Show upcoming races</Checkbox>
        <Checkbox>Show message (90 character max)</Checkbox>

          <textarea
            className="form-control"
            style={{
              fontSize: '24px',
              backgroundColor: 'transparent',
              color: '#6FDCFF',
              border: '1px solid #0079A1'
            }}
            defaultValue="We'd like to thank Wal-Mart and the Koch Brothers."
          />

);

export default class DefaultSettings extends Component {
  static propTypes = {
    bikes: PropTypes.array.isRequired,
    messages: PropTypes.object,
    updateBikesAvailable: PropTypes.func.isRequired,
    updateBikeConfiguration: PropTypes.func.isRequired
  }
  render() {
    const { bikes, updateBikesAvailable, updateBikeConfiguration } = this.props;
    const {
      PRE_COUNTDOWN_MESSAGE,
      COUNTDOWN_MESSAGE_3,
      COUNTDOWN_MESSAGE_2,
      COUNTDOWN_MESSAGE_1,
      COUNTDOWN_MESSAGE_GO,
      WINNER_MESSAGE,
      FALSE_START_MESSAGE
    } = this.props.messages;
    return (

Default Settings

        {/* First Horizontal Row of Settings */}

          {/* First Column of Settings */}

                Number of Bikes

                <StandardSelect
                  selectProps={{
                    id: 'select-bikes',
                    style: {
                      width: '90px'
                    },
                    onChange: (e) => { updateBikesAvailable(parseInt(e.target.value, 10)); },
                    value: bikes.length
                  }}
                >
                  {[2, 4].map((num) => (
                    <option key={`bikeNum-option-${num}`} value={num}>{num}</option>
                  ))}
                </StandardSelect>

                Roller Diameter

              {bikes.map((bike, i) => (
                <div className="input-group inline" key={`bike-${i}-rollerDiameter`}>

                      <BikeContext>{i + 1}</BikeContext>
                      <input
                        className="form-control context"
                        type="text"
                        value={bike.rollerDiameter.value}
                        onChange={(e) => {
                          // TODO add validation for input values
                          const newBike = Object.assign({}, bike);
                          newBike.rollerDiameter.value = e.target.value;
                          updateBikeConfiguration(i, newBike);
                        }}
                      />

                    <StandardSelect
                      selectProps={{
                        style: {
                          width: '130px'
                        },
                        onChange: (e) => {
                          const newBike = Object.assign({}, bike);
                          newBike.rollerDiameter.unit = e.target.value;
                          updateBikeConfiguration(i, newBike);
                        },
                        value: bike.rollerDiameter.unit
                      }}
                    >
                      <option value="inch">inches</option>
                      <option value="centimeter">centimeters</option>
                    </StandardSelect>

              ))}

                Racer Colors

                {bikes.map((bike, i) => (
                  <div
                    key={`colorPicker-${i}`}
                    className="col-xs-3"
                    style={{
                      paddingLeft: 0,
                      paddingRight: '10px'
                    }}
                  >

                      <BikeContext>{i + 1}</BikeContext>

                          arrow_drop_down

                ))}

                Racer Roster Options

              <Checkbox>Sex</Checkbox>
              <Checkbox>Racer Level</Checkbox>

          {/* Second Column of Settings */}

                  Distance/Speed Units

                  <StandardSelect
                    selectProps={{
                      id: 'select-metric-units',
                      style: {
                        width: '190px'
                      },
                      onChange: () => {
                        // TODO
                      }
                    }}
                  >
                    <option value="miles">Miles</option>
                    <option value="kilometers">Kilometers</option>
                  </StandardSelect>

            <RaceDistanceSetting />
            <RaceDurationSetting />

                <input
                  type="radio"
                  name="durationCountDirection"
                  id="durationCountDown"
                  value="down"
                />
                Count Down

                <input
                  type="radio"
                  name="durationCountDirection"
                  id="durationCountUp"
                  value="up"
                />
                Count Up

                  Pre Countdown Message

                <input
                  type="text"
                  className="form-control"
                  id="preCountdownMsgInput"
                  defaultValue={PRE_COUNTDOWN_MESSAGE}
                />

                  Countdown Messages

                  <PlainContext>3</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    defaultValue={COUNTDOWN_MESSAGE_3}
                    onChange={() => {
                      // TODO
                    }}
                  />

                  <PlainContext>2</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    defaultValue={COUNTDOWN_MESSAGE_2}
                    onChange={() => {
                      // TODO
                    }}
                  />

                  <PlainContext>1</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    defaultValue={COUNTDOWN_MESSAGE_1}
                    onChange={() => {
                      // TODO
                    }}
                  />

                  <PlainContext>GO</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    defaultValue={COUNTDOWN_MESSAGE_GO}
                    onChange={() => {
                      // TODO
                    }}
                  />

                  Winner Message

                <input
                  type="text"
                  className="form-control"
                  id="winnerMsgInput"
                  defaultValue={WINNER_MESSAGE}
                />

                  False Start Message

                <input
                  type="text"
                  className="form-control"
                  id="falseStartMsgInput"
                  defaultValue={FALSE_START_MESSAGE}
                />

        <RaceScreenSettings />

        <IntermissionScreenSettings />

    );
  }
}
<commit_msg>Wired up the race messages to update props when changed<commit_after>import React, { Component, PropTypes } from 'react';
import styles from './settings.css';
import Checkbox from './Checkbox';
import Background from './Background';

const StandardSelect = ({ selectProps = {}, children }) => {
  const { style } = selectProps;
  return (

      <select
        className="form-control"
        {...selectProps}
        style={Object.assign({}, {
          color: '#6FDCFF',
          fontSize: '18px',
          lineHeight: '18px',
          fontWeight: 'bold',
          borderColor: '#0079A1'
        }, style)}
      >
        {children}
      </select>

          arrow_drop_down

  );
};
StandardSelect.propTypes = {
  selectProps: PropTypes.object,
  children: PropTypes.array
};

const BikeContext = ({ children }) => (

        {children}

);
BikeContext.propTypes = {
  children: PropTypes.number
};

const PlainContext = ({ children }) => (

        {children}

);
PlainContext.propTypes = {
  children: PropTypes.string
};

const RaceDistanceSetting = () => (

        Race Distance

            <input
              id="distance-input"
              style={{
                padding: '6px 10px'
              }}
              className="form-control context"
              type="text"
              defaultValue="100"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

          <StandardSelect
            selectProps={{
              style: {
                width: '130px'
              },
              onChange: () => {
              },
              defaultValue: 'meters'
            }}
          >
            <option value="feet">feet</option>
            <option value="meters">meters</option>
          </StandardSelect>

);

const RaceDurationSetting = () => (

        Trial Duration

            <PlainContext>H</PlainContext>
            <input
              style={{
                padding: '6px 20px'
              }}
              className="form-control context"
              type="text"
              defaultValue="00"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

            <PlainContext>M</PlainContext>
            <input
              style={{
                padding: '6px 20px'
              }}
              className="form-control context"
              type="text"
              defaultValue="00"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

            <PlainContext>S</PlainContext>
            <input
              style={{
                padding: '6px 20px'
              }}
              className="form-control context"
              type="text"
              defaultValue="00"
              onChange={() => {
                // TODO add validation for input values
              }}
            />

);

const RaceScreenSettings = () => (

          Race Screen Background

        <Background />

          Race Clock Background

        <Background />

);

const IntermissionScreenSettings = () => (

          Intermission Screen Background

        <Background />

      add_circle
      Add an Intermission Screen Background

          Intermission Screen Options

        <Checkbox>Show standings</Checkbox>
        <Checkbox>Show upcoming races</Checkbox>
        <Checkbox>Show message (90 character max)</Checkbox>

          <textarea
            className="form-control"
            style={{
              fontSize: '24px',
              backgroundColor: 'transparent',
              color: '#6FDCFF',
              border: '1px solid #0079A1'
            }}
            defaultValue="We'd like to thank Wal-Mart and the Koch Brothers."
          />

);

export default class DefaultSettings extends Component {
  static propTypes = {
    bikes: PropTypes.array.isRequired,
    messages: PropTypes.object,
    updateBikesAvailable: PropTypes.func.isRequired,
    updateBikeConfiguration: PropTypes.func.isRequired,
    updateMessageText: PropTypes.func.isRequired
  }
  render() {
    const { bikes, updateBikesAvailable, updateBikeConfiguration, updateMessageText } = this.props;
    const {
      PRE_COUNTDOWN_MESSAGE,
      COUNTDOWN_MESSAGE_3,
      COUNTDOWN_MESSAGE_2,
      COUNTDOWN_MESSAGE_1,
      COUNTDOWN_MESSAGE_GO,
      WINNER_MESSAGE,
      FALSE_START_MESSAGE
    } = this.props.messages;

    const handleMessageChange = (key) => (e) => {
      updateMessageText(key, e.target.value);
    };

    return (

Default Settings

        {/* First Horizontal Row of Settings */}

          {/* First Column of Settings */}

                Number of Bikes

                <StandardSelect
                  selectProps={{
                    id: 'select-bikes',
                    style: {
                      width: '90px'
                    },
                    onChange: (e) => { updateBikesAvailable(parseInt(e.target.value, 10)); },
                    value: bikes.length
                  }}
                >
                  {[2, 4].map((num) => (
                    <option key={`bikeNum-option-${num}`} value={num}>{num}</option>
                  ))}
                </StandardSelect>

                Roller Diameter

              {bikes.map((bike, i) => (
                <div className="input-group inline" key={`bike-${i}-rollerDiameter`}>

                      <BikeContext>{i + 1}</BikeContext>
                      <input
                        className="form-control context"
                        type="text"
                        value={bike.rollerDiameter.value}
                        onChange={(e) => {
                          // TODO add validation for input values
                          const newBike = Object.assign({}, bike);
                          newBike.rollerDiameter.value = e.target.value;
                          updateBikeConfiguration(i, newBike);
                        }}
                      />

                    <StandardSelect
                      selectProps={{
                        style: {
                          width: '130px'
                        },
                        onChange: (e) => {
                          const newBike = Object.assign({}, bike);
                          newBike.rollerDiameter.unit = e.target.value;
                          updateBikeConfiguration(i, newBike);
                        },
                        value: bike.rollerDiameter.unit
                      }}
                    >
                      <option value="inch">inches</option>
                      <option value="centimeter">centimeters</option>
                    </StandardSelect>

              ))}

                Racer Colors

                {bikes.map((bike, i) => (
                  <div
                    key={`colorPicker-${i}`}
                    className="col-xs-3"
                    style={{
                      paddingLeft: 0,
                      paddingRight: '10px'
                    }}
                  >

                      <BikeContext>{i + 1}</BikeContext>

                          arrow_drop_down

                ))}

                Racer Roster Options

              <Checkbox>Sex</Checkbox>
              <Checkbox>Racer Level</Checkbox>

          {/* Second Column of Settings */}

                  Distance/Speed Units

                  <StandardSelect
                    selectProps={{
                      id: 'select-metric-units',
                      style: {
                        width: '190px'
                      },
                      onChange: () => {
                        // TODO
                      }
                    }}
                  >
                    <option value="miles">Miles</option>
                    <option value="kilometers">Kilometers</option>
                  </StandardSelect>

            <RaceDistanceSetting />
            <RaceDurationSetting />

                <input
                  type="radio"
                  name="durationCountDirection"
                  id="durationCountDown"
                  value="down"
                />
                Count Down

                <input
                  type="radio"
                  name="durationCountDirection"
                  id="durationCountUp"
                  value="up"
                />
                Count Up

                  Pre Countdown Message

                <input
                  type="text"
                  className="form-control"
                  id="preCountdownMsgInput"
                  value={PRE_COUNTDOWN_MESSAGE}
                  onChange={handleMessageChange('PRE_COUNTDOWN_MESSAGE')}
                />

                  Countdown Messages

                  <PlainContext>3</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    value={COUNTDOWN_MESSAGE_3}
                    onChange={handleMessageChange('COUNTDOWN_MESSAGE_3')}
                  />

                  <PlainContext>2</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    value={COUNTDOWN_MESSAGE_2}
                    onChange={handleMessageChange('COUNTDOWN_MESSAGE_2')}
                  />

                  <PlainContext>1</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    value={COUNTDOWN_MESSAGE_1}
                    onChange={handleMessageChange('COUNTDOWN_MESSAGE_1')}
                  />

                  <PlainContext>GO</PlainContext>
                  <input
                    style={{
                      padding: '6px 30px'
                    }}
                    className="form-control context"
                    type="text"
                    value={COUNTDOWN_MESSAGE_GO}
                    onChange={handleMessageChange('COUNTDOWN_MESSAGE_GO')}
                  />

                  Winner Message

                <input
                  type="text"
                  className="form-control"
                  id="winnerMsgInput"
                  value={WINNER_MESSAGE}
                  onChange={handleMessageChange('WINNER_MESSAGE')}
                />

                  False Start Message

                <input
                  type="text"
                  className="form-control"
                  id="falseStartMsgInput"
                  value={FALSE_START_MESSAGE}
                  onChange={handleMessageChange('FALSE_START_MESSAGE')}
                />

        <RaceScreenSettings />

        <IntermissionScreenSettings />

    );
  }
}
<|endoftext|>


<commit_before>var widgetTestHarnessInit = function(window, $, websiteUrl) {
    'use strict';

    var syndication, removeWidget, createWidget, update, initialiseToolsDropdown;
    var currentToolId, currentToolWidth;

    syndication = new window.PartnerMAS.ToolSyndication();

    removeWidget = function() {
        $('.mas-widget-container').remove();
    };

    createWidget = function (toolId, toolWidth) {
        var aElement = '';

        $('.js-test-harness-widget-container').append(aElement);
        syndication.renderWidgets()
        $('#widget_code').text(aElement);
    };

    update = function() {
        $('.settings .page :text').each(function() {
            var container_class = $(this).attr('id').replace('_width', '');
            $('.' + container_class).css('width', $(this).val());
        });
    };

    initialiseToolsDropdown = function() {
        var select = $('.settings .tool #tool_id');
        select.find('option').remove();
        $.each(masConfig.toolConfig, function(toolId, toolConfig) {
            if ('default' !== toolId) {
                select.append('<option value="' + toolId + '">' + toolId + '</option>');
            }
        });
    };

    $('.settings').on('submit', function(e) {
        var selectedToolId = $(this).find('#tool_id').val(),
            toolWidth = $('.settings .tool #tool_width').val();

        e.preventDefault();

        update();

        if ((selectedToolId !== currentToolId) ||
            (toolWidth !== currentToolWidth)) {
            currentToolId = selectedToolId;
            currentToolWidth = toolWidth;

            removeWidget();
            createWidget(selectedToolId, toolWidth);
        }
    });

    $(function() {
        initialiseToolsDropdown();
    });
};
<commit_msg>Sort the list of tools for easier selection<commit_after>var widgetTestHarnessInit = function(window, $, websiteUrl) {
    'use strict';

    var syndication, removeWidget, createWidget, update, initialiseToolsDropdown;
    var currentToolId, currentToolWidth;

    syndication = new window.PartnerMAS.ToolSyndication();

    removeWidget = function() {
        $('.mas-widget-container').remove();
    };

    createWidget = function (toolId, toolWidth) {
        var aElement = '';

        $('.js-test-harness-widget-container').append(aElement);
        syndication.renderWidgets()
        $('#widget_code').text(aElement);
    };

    update = function() {
        $('.settings .page :text').each(function() {
            var container_class = $(this).attr('id').replace('_width', '');
            $('.' + container_class).css('width', $(this).val());
        });
    };

    initialiseToolsDropdown = function() {
        var select = $('.settings .tool #tool_id'),
            sortedKeys = Object.keys(masConfig.toolConfig).sort();

        select.find('option').remove();

        $.each(sortedKeys, function(_, toolId) {
            if ('default' !== toolId) {
                select.append('<option value="' + toolId + '">' + toolId + '</option>');
            }
        });
    };

    $('.settings').on('submit', function(e) {
        var selectedToolId = $(this).find('#tool_id').val(),
            toolWidth = $('.settings .tool #tool_width').val();

        e.preventDefault();

        update();

        if ((selectedToolId !== currentToolId) ||
            (toolWidth !== currentToolWidth)) {
            currentToolId = selectedToolId;
            currentToolWidth = toolWidth;

            removeWidget();
            createWidget(selectedToolId, toolWidth);
        }
    });

    $(function() {
        initialiseToolsDropdown();
    });
};
<|endoftext|>


<commit_before><commit_msg>Removed unnecessary variable<commit_after><|endoftext|>