<commit_before>import { alias, equal, or, and, mapBy } from '@ember/object/computed';
import { computed } from '@ember/object';
import Model from '@ember-data/model';
import { attr, belongsTo, hasMany } from '@ember-data/model';
import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';
import RSVP from 'rsvp';
import { assert } from '@ember/debug';
import classic from 'ember-classic-decorator';

const JOB_TYPES = ['service', 'batch', 'system', 'sysbatch'];

@classic
export default class Job extends Model {
  @attr('string') region;
  @attr('string') name;
  @attr('string') plainId;
  @attr('string') type;
  @attr('number') priority;
  @attr('boolean') allAtOnce;

  @attr('string') status;
  @attr('string') statusDescription;
  @attr('number') createIndex;
  @attr('number') modifyIndex;
  @attr('date') submitTime;

  @fragment('structured-attributes') meta;

  // True when the job is the parent periodic or parameterized jobs
  // Instances of periodic or parameterized jobs are false for both properties
  @attr('boolean') periodic;
  @attr('boolean') parameterized;
  @attr('boolean') dispatched;

  @attr() periodicDetails;
  @attr() parameterizedDetails;

  @computed('periodic', 'parameterized', 'dispatched')
  get hasChildren() {
    return this.periodic || (this.parameterized && !this.dispatched);
  }

  @computed('type')
  get hasClientStatus() {
    return this.type === 'system' || this.type === 'sysbatch';
  }

  @belongsTo('job', { inverse: 'children' }) parent;
  @hasMany('job', { inverse: 'parent' }) children;

  // The parent job name is prepended to child launch job names
  @computed('name', 'parent.content')
  get trimmedName() {
    return this.get('parent.content')
      ? this.name.replace(/.+?\//, '')
      : this.name;
  }

  // A composite of type and other job attributes to determine
  // a better type descriptor for human interpretation rather
  // than for scheduling.
  @computed('type', 'periodic', 'parameterized')
  get displayType() {
    if (this.periodic) {
      return 'periodic';
    } else if (this.parameterized) {
      return 'parameterized';
    }
    return this.type;
  }

  // A composite of type and other job attributes to determine
  // type for templating rather than scheduling
  @computed(
    'type',
    'periodic',
    'parameterized',
    'parent.{periodic,parameterized}'
  )
  get templateType() {
    const type = this.type;

    if (this.get('parent.periodic')) {
      return 'periodic-child';
    } else if (this.get('parent.parameterized')) {
      return 'parameterized-child';
    } else if (this.periodic) {
      return 'periodic';
    } else if (this.parameterized) {
      return 'parameterized';
    } else if (JOB_TYPES.includes(type)) {
      // Guard against the API introducing a new type before the UI
      // is prepared to handle it.
      return this.type;
    }

    // A fail-safe in the event the API introduces a new type.
    return 'service';
  }

  @attr() datacenters;
  @fragmentArray('task-group', { defaultValue: () => [] }) taskGroups;
  @belongsTo('job-summary') summary;

  // A job model created from the jobs list response will be lacking
  // task groups. This is an indicator that it needs to be reloaded
  // if task group information is important.
  @equal('taskGroups.length', 0) isPartial;

  // If a job has only been loaded through the list request, the task groups
  // are still unknown. However, the count of task groups is available through
  // the job-summary model which is embedded in the jobs list response.
  @or('taskGroups.length', 'taskGroupSummaries.length') taskGroupCount;

  // Alias through to the summary, as if there was no relationship
  @alias('summary.taskGroupSummaries') taskGroupSummaries;
  @alias('summary.queuedAllocs') queuedAllocs;
  @alias('summary.startingAllocs') startingAllocs;
  @alias('summary.runningAllocs') runningAllocs;
  @alias('summary.completeAllocs') completeAllocs;
  @alias('summary.failedAllocs') failedAllocs;
  @alias('summary.lostAllocs') lostAllocs;
  @alias('summary.totalAllocs') totalAllocs;
  @alias('summary.pendingChildren') pendingChildren;
  @alias('summary.runningChildren') runningChildren;
  @alias('summary.deadChildren') deadChildren;
  @alias('summary.totalChildren') totalChildren;

  @attr('number') version;

  @hasMany('job-versions') versions;
  @hasMany('allocations') allocations;
  @hasMany('deployments') deployments;
  @hasMany('evaluations') evaluations;
  @belongsTo('namespace') namespace;
  @belongsTo('job-scale') scaleState;

  @hasMany('recommendation-summary') recommendationSummaries;

  @computed('taskGroups.@each.drivers')
  get drivers() {
    return this.taskGroups
      .mapBy('drivers')
      .reduce((all, drivers) => {
        all.push(...drivers);
        return all;
      }, [])
      .uniq();
  }

  @mapBy('allocations', 'unhealthyDrivers') allocationsUnhealthyDrivers;

  // Getting all unhealthy drivers for a job can be incredibly expensive if the job
  // has many allocations. This can lead to making an API request for many nodes.
  @computed('allocations', 'allocationsUnhealthyDrivers.[]')
  get unhealthyDrivers() {
    return this.allocations
      .mapBy('unhealthyDrivers')
      .reduce((all, drivers) => {
        all.push(...drivers);
        return all;
      }, [])
      .uniq();
  }

  @computed('evaluations.@each.isBlocked')
  get hasBlockedEvaluation() {
    return this.evaluations
      .toArray()
      .some((evaluation) => evaluation.get('isBlocked'));
  }

  @and('latestFailureEvaluation', 'hasBlockedEvaluation') hasPlacementFailures;

  @computed('evaluations.{@each.modifyIndex,isPending}')
  get latestEvaluation() {
    const evaluations = this.evaluations;
    if (!evaluations || evaluations.get('isPending')) {
      return null;
    }
    return evaluations.sortBy('modifyIndex').get('lastObject');
  }

  @computed('evaluations.{@each.modifyIndex,isPending}')
  get latestFailureEvaluation() {
    const evaluations = this.evaluations;
    if (!evaluations || evaluations.get('isPending')) {
      return null;
    }

    const failureEvaluations = evaluations.filterBy('hasPlacementFailures');
    if (failureEvaluations) {
      return failureEvaluations.sortBy('modifyIndex').get('lastObject');
    }

    return undefined;
  }

  @equal('type', 'service') supportsDeployments;

  @belongsTo('deployment', { inverse: 'jobForLatest' }) latestDeployment;

  @computed('latestDeployment', 'latestDeployment.isRunning')
  get runningDeployment() {
    const latest = this.latestDeployment;
    if (latest.get('isRunning')) return latest;
    return undefined;
  }

  fetchRawDefinition() {
    return this.store.adapterFor('job').fetchRawDefinition(this);
  }

  forcePeriodic() {
    return this.store.adapterFor('job').forcePeriodic(this);
  }

  stop() {
    return this.store.adapterFor('job').stop(this);
  }

  plan() {
    assert('A job must be parsed before planned', this._newDefinitionJSON);
    return this.store.adapterFor('job').plan(this);
  }

  run() {
    assert('A job must be parsed before ran', this._newDefinitionJSON);
    return this.store.adapterFor('job').run(this);
  }

  update() {
    assert('A job must be parsed before updated', this._newDefinitionJSON);
    return this.store.adapterFor('job').update(this);
  }

  parse() {
    const definition = this._newDefinition;
    let promise;

    try {
      // If the definition is already JSON then it doesn't need to be parsed.
      const json = JSON.parse(definition);
      this.set('_newDefinitionJSON', json);

      // You can't set the ID of a record that already exists
      if (this.isNew) {
        this.setIdByPayload(json);
      }

      promise = RSVP.resolve(definition);
    } catch (err) {
      // If the definition is invalid JSON, assume it is HCL. If it is invalid
      // in anyway, the parse endpoint will throw an error.
      promise = this.store
        .adapterFor('job')
        .parse(this._newDefinition)
        .then((response) => {
          this.set('_newDefinitionJSON', response);
          this.setIdByPayload(response);
        });
    }

    return promise;
  }

  scale(group, count, message) {
    if (message == null)
      message = `Manually scaled to ${count} from the Nomad UI`;
    return this.store.adapterFor('job').scale(this, group, count, message);
  }

  dispatch(meta, payload) {
    return this.store.adapterFor('job').dispatch(this, meta, payload);
  }

  setIdByPayload(payload) {
    const namespace = payload.Namespace || 'default';
    const id = payload.Name;

    this.set('plainId', id);
    this.set('_idBeforeSaving', JSON.stringify([id, namespace]));

    const namespaceRecord = this.store.peekRecord('namespace', namespace);
    if (namespaceRecord) {
      this.set('namespace', namespaceRecord);
    }
  }

  resetId() {
    this.set(
      'id',
      JSON.stringify([this.plainId, this.get('namespace.name') || 'default'])
    );
  }

  @computed('status')
  get statusClass() {
    const classMap = {
      pending: 'is-pending',
      running: 'is-primary',
      dead: 'is-light',
    };

    return classMap[this.status] || 'is-dark';
  }

  @attr('string') payload;

  @computed('payload')
  get decodedPayload() {
    // Lazily decode the base64 encoded payload
    return window.atob(this.payload || '');
  }

  // An arbitrary HCL or JSON string that is used by the serializer to plan
  // and run this job. Used for both new job models and saved job models.
  @attr('string') _newDefinition;

  // The new definition may be HCL, in which case the API will need to parse the
  // spec first. In order to preserve both the original HCL and the parsed response
  // that will be submitted to the create job endpoint, another prop is necessary.
  @attr('string') _newDefinitionJSON;
}
<commit_msg>feat: add `idWithNamespace`-getter job model<commit_after>import { alias, equal, or, and, mapBy } from '@ember/object/computed';
import { computed } from '@ember/object';
import Model from '@ember-data/model';
import { attr, belongsTo, hasMany } from '@ember-data/model';
import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';
import RSVP from 'rsvp';
import { assert } from '@ember/debug';
import classic from 'ember-classic-decorator';

const JOB_TYPES = ['service', 'batch', 'system', 'sysbatch'];

@classic
export default class Job extends Model {
  @attr('string') region;
  @attr('string') name;
  @attr('string') plainId;
  @attr('string') type;
  @attr('number') priority;
  @attr('boolean') allAtOnce;

  @attr('string') status;
  @attr('string') statusDescription;
  @attr('number') createIndex;
  @attr('number') modifyIndex;
  @attr('date') submitTime;

  @fragment('structured-attributes') meta;

  // True when the job is the parent periodic or parameterized jobs
  // Instances of periodic or parameterized jobs are false for both properties
  @attr('boolean') periodic;
  @attr('boolean') parameterized;
  @attr('boolean') dispatched;

  @attr() periodicDetails;
  @attr() parameterizedDetails;

  @computed('plainId')
  get idWithNamespace() {
    const namespaceId = this.belongsTo('namespace').id();

    if (!namespaceId || namespaceId === 'default') {
      return this.plainId;
    } else {
      return `${this.plainId}@${namespaceId}`;
    }
  }

  @computed('periodic', 'parameterized', 'dispatched')
  get hasChildren() {
    return this.periodic || (this.parameterized && !this.dispatched);
  }

  @computed('type')
  get hasClientStatus() {
    return this.type === 'system' || this.type === 'sysbatch';
  }

  @belongsTo('job', { inverse: 'children' }) parent;
  @hasMany('job', { inverse: 'parent' }) children;

  // The parent job name is prepended to child launch job names
  @computed('name', 'parent.content')
  get trimmedName() {
    return this.get('parent.content')
      ? this.name.replace(/.+?\//, '')
      : this.name;
  }

  // A composite of type and other job attributes to determine
  // a better type descriptor for human interpretation rather
  // than for scheduling.
  @computed('type', 'periodic', 'parameterized')
  get displayType() {
    if (this.periodic) {
      return 'periodic';
    } else if (this.parameterized) {
      return 'parameterized';
    }
    return this.type;
  }

  // A composite of type and other job attributes to determine
  // type for templating rather than scheduling
  @computed(
    'type',
    'periodic',
    'parameterized',
    'parent.{periodic,parameterized}'
  )
  get templateType() {
    const type = this.type;

    if (this.get('parent.periodic')) {
      return 'periodic-child';
    } else if (this.get('parent.parameterized')) {
      return 'parameterized-child';
    } else if (this.periodic) {
      return 'periodic';
    } else if (this.parameterized) {
      return 'parameterized';
    } else if (JOB_TYPES.includes(type)) {
      // Guard against the API introducing a new type before the UI
      // is prepared to handle it.
      return this.type;
    }

    // A fail-safe in the event the API introduces a new type.
    return 'service';
  }

  @attr() datacenters;
  @fragmentArray('task-group', { defaultValue: () => [] }) taskGroups;
  @belongsTo('job-summary') summary;

  // A job model created from the jobs list response will be lacking
  // task groups. This is an indicator that it needs to be reloaded
  // if task group information is important.
  @equal('taskGroups.length', 0) isPartial;

  // If a job has only been loaded through the list request, the task groups
  // are still unknown. However, the count of task groups is available through
  // the job-summary model which is embedded in the jobs list response.
  @or('taskGroups.length', 'taskGroupSummaries.length') taskGroupCount;

  // Alias through to the summary, as if there was no relationship
  @alias('summary.taskGroupSummaries') taskGroupSummaries;
  @alias('summary.queuedAllocs') queuedAllocs;
  @alias('summary.startingAllocs') startingAllocs;
  @alias('summary.runningAllocs') runningAllocs;
  @alias('summary.completeAllocs') completeAllocs;
  @alias('summary.failedAllocs') failedAllocs;
  @alias('summary.lostAllocs') lostAllocs;
  @alias('summary.totalAllocs') totalAllocs;
  @alias('summary.pendingChildren') pendingChildren;
  @alias('summary.runningChildren') runningChildren;
  @alias('summary.deadChildren') deadChildren;
  @alias('summary.totalChildren') totalChildren;

  @attr('number') version;

  @hasMany('job-versions') versions;
  @hasMany('allocations') allocations;
  @hasMany('deployments') deployments;
  @hasMany('evaluations') evaluations;
  @belongsTo('namespace') namespace;
  @belongsTo('job-scale') scaleState;

  @hasMany('recommendation-summary') recommendationSummaries;

  @computed('taskGroups.@each.drivers')
  get drivers() {
    return this.taskGroups
      .mapBy('drivers')
      .reduce((all, drivers) => {
        all.push(...drivers);
        return all;
      }, [])
      .uniq();
  }

  @mapBy('allocations', 'unhealthyDrivers') allocationsUnhealthyDrivers;

  // Getting all unhealthy drivers for a job can be incredibly expensive if the job
  // has many allocations. This can lead to making an API request for many nodes.
  @computed('allocations', 'allocationsUnhealthyDrivers.[]')
  get unhealthyDrivers() {
    return this.allocations
      .mapBy('unhealthyDrivers')
      .reduce((all, drivers) => {
        all.push(...drivers);
        return all;
      }, [])
      .uniq();
  }

  @computed('evaluations.@each.isBlocked')
  get hasBlockedEvaluation() {
    return this.evaluations
      .toArray()
      .some((evaluation) => evaluation.get('isBlocked'));
  }

  @and('latestFailureEvaluation', 'hasBlockedEvaluation') hasPlacementFailures;

  @computed('evaluations.{@each.modifyIndex,isPending}')
  get latestEvaluation() {
    const evaluations = this.evaluations;
    if (!evaluations || evaluations.get('isPending')) {
      return null;
    }
    return evaluations.sortBy('modifyIndex').get('lastObject');
  }

  @computed('evaluations.{@each.modifyIndex,isPending}')
  get latestFailureEvaluation() {
    const evaluations = this.evaluations;
    if (!evaluations || evaluations.get('isPending')) {
      return null;
    }

    const failureEvaluations = evaluations.filterBy('hasPlacementFailures');
    if (failureEvaluations) {
      return failureEvaluations.sortBy('modifyIndex').get('lastObject');
    }

    return undefined;
  }

  @equal('type', 'service') supportsDeployments;

  @belongsTo('deployment', { inverse: 'jobForLatest' }) latestDeployment;

  @computed('latestDeployment', 'latestDeployment.isRunning')
  get runningDeployment() {
    const latest = this.latestDeployment;
    if (latest.get('isRunning')) return latest;
    return undefined;
  }

  fetchRawDefinition() {
    return this.store.adapterFor('job').fetchRawDefinition(this);
  }

  forcePeriodic() {
    return this.store.adapterFor('job').forcePeriodic(this);
  }

  stop() {
    return this.store.adapterFor('job').stop(this);
  }

  plan() {
    assert('A job must be parsed before planned', this._newDefinitionJSON);
    return this.store.adapterFor('job').plan(this);
  }

  run() {
    assert('A job must be parsed before ran', this._newDefinitionJSON);
    return this.store.adapterFor('job').run(this);
  }

  update() {
    assert('A job must be parsed before updated', this._newDefinitionJSON);
    return this.store.adapterFor('job').update(this);
  }

  parse() {
    const definition = this._newDefinition;
    let promise;

    try {
      // If the definition is already JSON then it doesn't need to be parsed.
      const json = JSON.parse(definition);
      this.set('_newDefinitionJSON', json);

      // You can't set the ID of a record that already exists
      if (this.isNew) {
        this.setIdByPayload(json);
      }

      promise = RSVP.resolve(definition);
    } catch (err) {
      // If the definition is invalid JSON, assume it is HCL. If it is invalid
      // in anyway, the parse endpoint will throw an error.
      promise = this.store
        .adapterFor('job')
        .parse(this._newDefinition)
        .then((response) => {
          this.set('_newDefinitionJSON', response);
          this.setIdByPayload(response);
        });
    }

    return promise;
  }

  scale(group, count, message) {
    if (message == null)
      message = `Manually scaled to ${count} from the Nomad UI`;
    return this.store.adapterFor('job').scale(this, group, count, message);
  }

  dispatch(meta, payload) {
    return this.store.adapterFor('job').dispatch(this, meta, payload);
  }

  setIdByPayload(payload) {
    const namespace = payload.Namespace || 'default';
    const id = payload.Name;

    this.set('plainId', id);
    this.set('_idBeforeSaving', JSON.stringify([id, namespace]));

    const namespaceRecord = this.store.peekRecord('namespace', namespace);
    if (namespaceRecord) {
      this.set('namespace', namespaceRecord);
    }
  }

  resetId() {
    this.set(
      'id',
      JSON.stringify([this.plainId, this.get('namespace.name') || 'default'])
    );
  }

  @computed('status')
  get statusClass() {
    const classMap = {
      pending: 'is-pending',
      running: 'is-primary',
      dead: 'is-light',
    };

    return classMap[this.status] || 'is-dark';
  }

  @attr('string') payload;

  @computed('payload')
  get decodedPayload() {
    // Lazily decode the base64 encoded payload
    return window.atob(this.payload || '');
  }

  // An arbitrary HCL or JSON string that is used by the serializer to plan
  // and run this job. Used for both new job models and saved job models.
  @attr('string') _newDefinition;

  // The new definition may be HCL, in which case the API will need to parse the
  // spec first. In order to preserve both the original HCL and the parsed response
  // that will be submitted to the create job endpoint, another prop is necessary.
  @attr('string') _newDefinitionJSON;
}
<|endoftext|>


<commit_before>var m = require('mithril');
var winningChances = require('./winningChances');
var defined = require('common').defined;
var classSet = require('common').classSet;
var renderEval = require('chess').renderEval;
var pv2san = require('./pv2san');

var gaugeLast = 0;
var gaugeTicks = [];
for (var i = 1; i < 8; i++) gaugeTicks.push(m(i === 4 ? 'tick.zero' : 'tick', {
  style: {
    height: (i * 12.5) + '%'
  }
}));

function range(len) {
  var r = [];
  for (var i = 0; i < len; i++) r.push(i);
  return r;
}

function localEvalInfo(ctrl, evs) {
  var ceval = ctrl.getCeval();
  if (!evs.client) {
    if (evs.server && ctrl.nextNodeBest()) return 'Using server analysis';
    return 'Loading engine...';
  }
  if (evs.client.dict) return 'Book move';
  var t = ['Depth ' + (evs.client.depth || 0) + '/' + evs.client.maxDepth];
  if (ceval.pnaclSupported && evs.client.depth >= evs.client.maxDepth && !ceval.isDeeper())
    t.push(m('a.deeper', {
      onclick: function() {
        ceval.goDeeper();
      }
    }, 'Go deeper'))
  else if (evs.client.nps) t.push(', ' + Math.round(evs.client.nps / 1000) + ' knodes/s');
  return t;
}

function threatInfo(threat) {
  if (!threat) return 'Loading engine...';
  if (threat.dict) return 'Book move';
  var t = 'Depth ' + (threat.depth || 0) + '/' + threat.maxDepth;
  if (threat.nps) t += ', ' + Math.round(threat.nps / 1000) + ' knodes/s';
  return t;
}

function threatButton(ctrl) {
  return m('a', {
    class: classSet({
      'show-threat': true,
      active: ctrl.vm.threatMode,
      hidden: ctrl.vm.node.check || (ctrl.disableThreatMode && ctrl.disableThreatMode())
    }),
    'data-icon': '7',
    title: 'Show threat (x)',
    onclick: ctrl.toggleThreatMode
  });
}

function engineName(ctrl) {
  return [
    lichess.engineName,
    ctrl.pnaclSupported ? m('span.native', 'native') : m('span.asmjs', 'asmjs')
  ];
}

var serverNodes = 3.5e6;

function getBestEval(evs) {
  var serverEv = evs.server,
      localEv = evs.client;

  if (!serverEv) return localEv;
  if (!localEv) return serverEv;

  // Prefer localEv if it exeeds fishnet node limit or finds a better mate.
  if (localEv.nodes > serverNodes ||
      (localEv.mate && !(Math.abs(serverEv.mate) < Math.abs(localEv.mate))))
    return localEv;

  return serverEv;
}

module.exports = {
  getBestEval: getBestEval,
  renderGauge: function(ctrl) {
    if (ctrl.ongoing || !ctrl.showEvalGauge()) return;
    var eval, bestEv = getBestEval(ctrl.currentEvals());
    if (bestEv) {
      eval = winningChances.povChances('white', bestEv);
      gaugeLast = eval;
    } else eval = gaugeLast;
    var height = 100 - (eval + 1) * 50;
    return m('div', {
      class: classSet({
        eval_gauge: true,
        empty: eval === null,
        reverse: ctrl.getOrientation() === 'black'
      })
    }, [
      m('div', {
        class: 'black',
        style: {
          height: height + '%'
        }
      }),
      gaugeTicks
    ]);
  },
  renderCeval: function(ctrl) {
    var instance = ctrl.getCeval();
    if (!instance.allowed() || !instance.possible || !ctrl.vm.showComputer()) return;
    var enabled = instance.enabled();
    var evs = ctrl.currentEvals();
    var bestEv = getBestEval(evs);
    var threatMode = ctrl.vm.threatMode;
    var threat = threatMode && ctrl.vm.node.threat;
    var pearl, percent;
    if (bestEv && defined(bestEv.cp)) {
      pearl = renderEval(bestEv.cp);
      percent = ctrl.nextNodeBest() ?
        100 :
        (evs.client ? Math.min(100, Math.round(100 * evs.client.depth / evs.client.maxDepth)) : 0)
    } else if (bestEv && defined(bestEv.mate)) {
      pearl = '#' + bestEv.mate;
      percent = 100;
    } else if (ctrl.gameOver()) {
      pearl = '-';
      percent = 0;
    } else {
      pearl = m('span.cpu', 'CPU');
      percent = 0;
    }
    if (threatMode) {
      if (threat) percent = Math.min(100, Math.round(100 * threat.depth / threat.maxDepth));
      else percent = 0;
    }
    var mandatoryCeval = ctrl.mandatoryCeval && ctrl.mandatoryCeval();
    return m('div.ceval_box',
      enabled ? m('div.bar', m('span', {
        class: threatMode ? 'threat' : '',
        style: {
          width: percent + '%'
        },
        config: function(el, isUpdate, ctx) {
          // reinsert the node to avoid downward animation
          if (isUpdate && (ctx.percent > percent || ctx.threatMode !== threatMode)) {
            var p = el.parentNode;
            p.removeChild(el);
            p.appendChild(el);
          }
          ctx.percent = percent;
          ctx.threatMode = threatMode;
        }
      })) : null,
      enabled ? [
        m('pearl', pearl),
        m('div.engine', [
          threatMode ? 'Show threat' : engineName(instance),
          m('span.info', ctrl.gameOver() ? 'Game over.' : (
            threatMode ? threatInfo(threat) : localEvalInfo(ctrl, evs)
          ))
        ])
      ] : [
        pearl ? m('pearl', pearl) : null,
        m('help',
          engineName(instance),
          m('br'),
          'in local browser'
        )
      ],
      mandatoryCeval ? null : m('div.switch', {
        title: 'Toggle local evaluation (l)'
      }, [
        m('input', {
          id: 'analyse-toggle-ceval',
          class: 'cmn-toggle cmn-toggle-round',
          type: 'checkbox',
          checked: enabled,
          onchange: ctrl.toggleCeval
        }),
        m('label', {
          'for': 'analyse-toggle-ceval'
        })
      ]),
      threatButton(ctrl)
    )
  },
  renderPvs: function(ctrl) {
    var instance = ctrl.getCeval();
    if (!instance.allowed() || !instance.possible || !instance.enabled()) return;
    var multiPv = instance.multiPv();
    var pvs, threat = false;
    if (ctrl.vm.threatMode && ctrl.vm.node.threat && ctrl.vm.node.threat.pvs) {
      pvs = ctrl.vm.node.threat.pvs;
      threat = true;
    } else if (ctrl.vm.node.ceval && ctrl.vm.node.ceval.pvs)
      pvs = ctrl.vm.node.ceval.pvs;
    else
      pvs = [];
    return m('div.pv_box', {
      'data-fen': ctrl.vm.node.fen,
      config: function(el, isUpdate, ctx) {
        if (!isUpdate) {
          el.addEventListener('mouseover', function(e) {
            instance.setHovering($(el).attr('data-fen'), $(e.target).closest('div.pv').attr('data-uci'));
          });
          el.addEventListener('mouseout', function(e) {
            instance.setHovering($(el).attr('data-fen'), null);
          });
          el.addEventListener('mousedown', function(e) {
            var uci = $(e.target).closest('div.pv').attr('data-uci');
            if (uci) ctrl.playUci(uci);
          });
        }
        setTimeout(function() {
          instance.setHovering($(el).attr('data-fen'), $(el).find('div.pv:hover').attr('data-uci'));
        }, 100);
      }
    }, range(multiPv).map(function(i) {
      if (!pvs[i]) return m('div.pv');
      else return m('div.pv', threat ? {} : {
        'data-uci': pvs[i].best
      }, [
        multiPv > 1 ? m('strong', defined(pvs[i].mate) ? ('#' + pvs[i].mate) : renderEval(pvs[i].cp)) : null,
        m('span', pv2san(instance.variant.key, ctrl.vm.node.fen, threat, pvs[i].pv, pvs[i].mate))
      ]);
    }));
  }
};
<commit_msg>don't display the threat button in practice mode - closes #2608<commit_after>var m = require('mithril');
var winningChances = require('./winningChances');
var defined = require('common').defined;
var classSet = require('common').classSet;
var renderEval = require('chess').renderEval;
var pv2san = require('./pv2san');

var gaugeLast = 0;
var gaugeTicks = [];
for (var i = 1; i < 8; i++) gaugeTicks.push(m(i === 4 ? 'tick.zero' : 'tick', {
  style: {
    height: (i * 12.5) + '%'
  }
}));

function range(len) {
  var r = [];
  for (var i = 0; i < len; i++) r.push(i);
  return r;
}

function localEvalInfo(ctrl, evs) {
  var ceval = ctrl.getCeval();
  if (!evs.client) {
    if (evs.server && ctrl.nextNodeBest()) return 'Using server analysis';
    return 'Loading engine...';
  }
  if (evs.client.dict) return 'Book move';
  var t = ['Depth ' + (evs.client.depth || 0) + '/' + evs.client.maxDepth];
  if (ceval.pnaclSupported && evs.client.depth >= evs.client.maxDepth && !ceval.isDeeper())
    t.push(m('a.deeper', {
      onclick: function() {
        ceval.goDeeper();
      }
    }, 'Go deeper'))
  else if (evs.client.nps) t.push(', ' + Math.round(evs.client.nps / 1000) + ' knodes/s');
  return t;
}

function threatInfo(threat) {
  if (!threat) return 'Loading engine...';
  if (threat.dict) return 'Book move';
  var t = 'Depth ' + (threat.depth || 0) + '/' + threat.maxDepth;
  if (threat.nps) t += ', ' + Math.round(threat.nps / 1000) + ' knodes/s';
  return t;
}

function threatButton(ctrl) {
  if (!ctrl.disableThreatMode || !ctrl.disableThreatMode()) return m('a', {
    class: classSet({
      'show-threat': true,
      active: ctrl.vm.threatMode,
      hidden: ctrl.vm.node.check
    }),
    'data-icon': '7',
    title: 'Show threat (x)',
    onclick: ctrl.toggleThreatMode
  });
}

function engineName(ctrl) {
  return [
    lichess.engineName,
    ctrl.pnaclSupported ? m('span.native', 'native') : m('span.asmjs', 'asmjs')
  ];
}

var serverNodes = 3.5e6;

function getBestEval(evs) {
  var serverEv = evs.server,
      localEv = evs.client;

  if (!serverEv) return localEv;
  if (!localEv) return serverEv;

  // Prefer localEv if it exeeds fishnet node limit or finds a better mate.
  if (localEv.nodes > serverNodes ||
      (localEv.mate && !(Math.abs(serverEv.mate) < Math.abs(localEv.mate))))
    return localEv;

  return serverEv;
}

module.exports = {
  getBestEval: getBestEval,
  renderGauge: function(ctrl) {
    if (ctrl.ongoing || !ctrl.showEvalGauge()) return;
    var eval, bestEv = getBestEval(ctrl.currentEvals());
    if (bestEv) {
      eval = winningChances.povChances('white', bestEv);
      gaugeLast = eval;
    } else eval = gaugeLast;
    var height = 100 - (eval + 1) * 50;
    return m('div', {
      class: classSet({
        eval_gauge: true,
        empty: eval === null,
        reverse: ctrl.getOrientation() === 'black'
      })
    }, [
      m('div', {
        class: 'black',
        style: {
          height: height + '%'
        }
      }),
      gaugeTicks
    ]);
  },
  renderCeval: function(ctrl) {
    var instance = ctrl.getCeval();
    if (!instance.allowed() || !instance.possible || !ctrl.vm.showComputer()) return;
    var enabled = instance.enabled();
    var evs = ctrl.currentEvals();
    var bestEv = getBestEval(evs);
    var threatMode = ctrl.vm.threatMode;
    var threat = threatMode && ctrl.vm.node.threat;
    var pearl, percent;
    if (bestEv && defined(bestEv.cp)) {
      pearl = renderEval(bestEv.cp);
      percent = ctrl.nextNodeBest() ?
        100 :
        (evs.client ? Math.min(100, Math.round(100 * evs.client.depth / evs.client.maxDepth)) : 0)
    } else if (bestEv && defined(bestEv.mate)) {
      pearl = '#' + bestEv.mate;
      percent = 100;
    } else if (ctrl.gameOver()) {
      pearl = '-';
      percent = 0;
    } else {
      pearl = m('span.cpu', 'CPU');
      percent = 0;
    }
    if (threatMode) {
      if (threat) percent = Math.min(100, Math.round(100 * threat.depth / threat.maxDepth));
      else percent = 0;
    }
    var mandatoryCeval = ctrl.mandatoryCeval && ctrl.mandatoryCeval();
    return m('div.ceval_box',
      enabled ? m('div.bar', m('span', {
        class: threatMode ? 'threat' : '',
        style: {
          width: percent + '%'
        },
        config: function(el, isUpdate, ctx) {
          // reinsert the node to avoid downward animation
          if (isUpdate && (ctx.percent > percent || ctx.threatMode !== threatMode)) {
            var p = el.parentNode;
            p.removeChild(el);
            p.appendChild(el);
          }
          ctx.percent = percent;
          ctx.threatMode = threatMode;
        }
      })) : null,
      enabled ? [
        m('pearl', pearl),
        m('div.engine', [
          threatMode ? 'Show threat' : engineName(instance),
          m('span.info', ctrl.gameOver() ? 'Game over.' : (
            threatMode ? threatInfo(threat) : localEvalInfo(ctrl, evs)
          ))
        ])
      ] : [
        pearl ? m('pearl', pearl) : null,
        m('help',
          engineName(instance),
          m('br'),
          'in local browser'
        )
      ],
      mandatoryCeval ? null : m('div.switch', {
        title: 'Toggle local evaluation (l)'
      }, [
        m('input', {
          id: 'analyse-toggle-ceval',
          class: 'cmn-toggle cmn-toggle-round',
          type: 'checkbox',
          checked: enabled,
          onchange: ctrl.toggleCeval
        }),
        m('label', {
          'for': 'analyse-toggle-ceval'
        })
      ]),
      threatButton(ctrl)
    )
  },
  renderPvs: function(ctrl) {
    var instance = ctrl.getCeval();
    if (!instance.allowed() || !instance.possible || !instance.enabled()) return;
    var multiPv = instance.multiPv();
    var pvs, threat = false;
    if (ctrl.vm.threatMode && ctrl.vm.node.threat && ctrl.vm.node.threat.pvs) {
      pvs = ctrl.vm.node.threat.pvs;
      threat = true;
    } else if (ctrl.vm.node.ceval && ctrl.vm.node.ceval.pvs)
      pvs = ctrl.vm.node.ceval.pvs;
    else
      pvs = [];
    return m('div.pv_box', {
      'data-fen': ctrl.vm.node.fen,
      config: function(el, isUpdate, ctx) {
        if (!isUpdate) {
          el.addEventListener('mouseover', function(e) {
            instance.setHovering($(el).attr('data-fen'), $(e.target).closest('div.pv').attr('data-uci'));
          });
          el.addEventListener('mouseout', function(e) {
            instance.setHovering($(el).attr('data-fen'), null);
          });
          el.addEventListener('mousedown', function(e) {
            var uci = $(e.target).closest('div.pv').attr('data-uci');
            if (uci) ctrl.playUci(uci);
          });
        }
        setTimeout(function() {
          instance.setHovering($(el).attr('data-fen'), $(el).find('div.pv:hover').attr('data-uci'));
        }, 100);
      }
    }, range(multiPv).map(function(i) {
      if (!pvs[i]) return m('div.pv');
      else return m('div.pv', threat ? {} : {
        'data-uci': pvs[i].best
      }, [
        multiPv > 1 ? m('strong', defined(pvs[i].mate) ? ('#' + pvs[i].mate) : renderEval(pvs[i].cp)) : null,
        m('span', pv2san(instance.variant.key, ctrl.vm.node.fen, threat, pvs[i].pv, pvs[i].mate))
      ]);
    }));
  }
};
<|endoftext|>


<commit_before>/* globals user_pref */

user_pref("browser.newtab.url", "http://localhost/tab/");
// enable file:// link on my homepage
user_pref("capability.policy.policynames", "localfilelinks");
user_pref("capability.policy.localfilelinks.sites", "http://localhost/");
user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess");

// remove warning when accessing about:config
user_pref("general.warnOnAboutConfig", false);

// disable zoom on cmd + mousewheel
user_pref("mousewheel.with_meta.action", 0);

// Firefox Dev Edition skin
user_pref("browser.devedition.theme.enabled", true);

// light skin
user_pref("devtools.theme", "light");

// devtools for the chrome & add-ons
user_pref("devtools.chrome.enabled", true);

// remote debugging
user_pref("devtools.debugger.remote-enabled", true);
<commit_msg>Fixed: Firefox Dev Edition theme in Firefox Nightly >= 40<commit_after>/* globals user_pref */

user_pref("browser.newtab.url", "http://localhost/tab/");
// enable file:// link on my homepage
user_pref("capability.policy.policynames", "localfilelinks");
user_pref("capability.policy.localfilelinks.sites", "http://localhost/");
user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess");

// remove warning when accessing about:config
user_pref("general.warnOnAboutConfig", false);

// disable zoom on cmd + mousewheel
user_pref("mousewheel.with_meta.action", 0);

// Firefox Dev Edition theme
user_pref("lightweightThemes.selectedThemeID", "firefox-devedition@mozilla.org");

// light skin
user_pref("devtools.theme", "light");

// devtools for the chrome & add-ons
user_pref("devtools.chrome.enabled", true);

// remote debugging
user_pref("devtools.debugger.remote-enabled", true);
<|endoftext|>


<commit_before>var fs = require('fs');
var http = require('http');
var url = require('url');
var Promise = require('promise');
var signalR = require('signalr-client');
var path = require('path');
var pwm = require('pi-blaster.js');

var configFile = path.join(__dirname, 'config.json');
console.log('Loading configuration from ' + configFile);
var config = JSON.parse(fs.readFileSync(configFile));

var LedManager = require('./ledManager.js');
var led = new LedManager(config);

// test colors
led.setColor(0, 0, 0, 0);
led.setColor(1, 0, 0, 400);
setTimeout(function() {
    led.setColor(0, 1, 0, 400);
    setTimeout(function() {
        led.setColor(0, 0, 1, 400);
        setTimeout(function() {
            // and start
            led.setColor(0, 0, 0, 400);
            setTimeout(function() {
                updateIn(1);
                start();
            }, 500);
        }, 500);
    }, 500);
}, 500);

function getStatus() {
    return new Promise(function(resolve, reject) {
        var options = url.parse(config.apiServer + "/room/" + config.room + "/status");
        options.method = "GET";
        return http.request(options, function(e) {
            data = "";
            e.on('data', function(c) { data += String(c); });
            e.on('end', function() { resolve(data); });
        }).end();
    })
}

var applyInterval = null;
var updateTimeout = null;
function updateIn(delay) {
    if(null != updateTimeout){
        clearTimeout(updateTimeout);
    }
    updateTimeout = setTimeout(function() {
        getStatus().then(function(data) {
            var obj = JSON.parse(data);
            var status = obj.Status;
            if(null != applyInterval) {
                clearInterval(applyInterval);
                applyInterval = null;
            }
            var lastApply = new Date().getTime();
            function apply() {
                var thisApply = new Date().getTime();
                obj.RoomNextFreeInSeconds -= (thisApply - lastApply) / 1000;
                lastApply = thisApply;
                switch(status) {
                    case 0:
                        green();
                        break;
                    case 1:
                        if(obj.RoomNextFreeInSeconds < 600) {
                            orange();
                        } else {
                            red();
                        }
                        break;
                    case 2:
                        if(obj.CurrentMeeting && obj.CurrentMeeting.IsNotManaged) {
                            // non-managed meetings don't need to be started
                            red();
                        } else {
                            // this is a managed meeting that's on the verge of getting auto-cancelled - look wierd.
                            purple();
                        }
                        break;
                    default:
                        console.log('invalid status: ' + status);
                        break;
                }
            }
            apply();
            if(status == 1) {
                applyInterval = setInterval(apply, 10000);
            }

            if(obj.NextChangeSeconds) {
                updateIn((obj.NextChangeSeconds + 1) * 1000); // server advises us to check back at this time
            }
        });
    }, delay);
}

function purple() {
    console.log('purple');
    led.setCycle([
       { state: { red: 1, green: 0, blue: 0 }, duration: 200 },
       { state: { red: 1, green: 0, blue: 0 }, duration: 2000 },
       { state: { red: 0.625, green: 0.125, blue: 0.9375 }, duration: 200 }
    ]);
}

function orange() {
    console.log('orange');
    led.setCycle([
       { state: { red: 1, green: 0, blue: 0 }, duration: 200 },
       { state: { red: 1, green: 0, blue: 0 }, duration: 5000 },
       { state: { red: 1, green: 0.5, blue: 0 }, duration: 200 }
    ]);
}

function red() {
    console.log('red');
    led.setColor(1, 0, 0, 1000);
}
function green() {
    console.log('green');
    led.setColor(0, 1, 0, 1000);
}

function start() {
    setInterval(function() {
        updateIn(5000);
    }, 5 * 60 * 1000);
    var gotConnected = false;
    setTimeout(function() {
        if(!gotConnected) {
            console.log('Aborting - no connection');
            process.exit(-1);
        }
    }, 30000);

    var client  = new signalR.client(
        config.signalRServer,
        ['UpdateHub']
    );
    client.on('UpdateHub', 'Update', function(room) {
        console.log('got notification of change to ' + room);
        if(config.room == room) {
            updateIn(1);
        }
    });
    client.serviceHandlers.connected = function() {
        gotConnected = true;
        console.log('signalR connected');
    };
    client.serviceHandlers.connectionLost = function() {
        console.log('signalR connection lost');
    };
    client.serviceHandlers.connectFailed = function() {
        console.log('signalR connection failed - aborting');
        process.exit();
    };
    client.serviceHandlers.reconnecting = function() {
        console.log('signalR reconnecting');
        return true;
    };
    client.serviceHandlers.reconnected = function() {
        console.log('signalR reconnected');
    };
    client.serviceHandlers.onerror = function(error) {
        console.log('signalR error: ' + error);
        // signalR doesn't seem to detect and reconnect - so we'll force it by restarting
        console.log('restarting');
        process.exit();
    };
}
// wait
<commit_msg>Another case we need to restart form<commit_after>var fs = require('fs');
var http = require('http');
var url = require('url');
var Promise = require('promise');
var signalR = require('signalr-client');
var path = require('path');
var pwm = require('pi-blaster.js');

var configFile = path.join(__dirname, 'config.json');
console.log('Loading configuration from ' + configFile);
var config = JSON.parse(fs.readFileSync(configFile));

var LedManager = require('./ledManager.js');
var led = new LedManager(config);

// test colors
led.setColor(0, 0, 0, 0);
led.setColor(1, 0, 0, 400);
setTimeout(function() {
    led.setColor(0, 1, 0, 400);
    setTimeout(function() {
        led.setColor(0, 0, 1, 400);
        setTimeout(function() {
            // and start
            led.setColor(0, 0, 0, 400);
            setTimeout(function() {
                updateIn(1);
                start();
            }, 500);
        }, 500);
    }, 500);
}, 500);

function getStatus() {
    return new Promise(function(resolve, reject) {
        var options = url.parse(config.apiServer + "/room/" + config.room + "/status");
        options.method = "GET";
        return http.request(options, function(e) {
            data = "";
            e.on('data', function(c) { data += String(c); });
            e.on('end', function() { resolve(data); });
        }).end();
    })
}

var applyInterval = null;
var updateTimeout = null;
function updateIn(delay) {
    if(null != updateTimeout){
        clearTimeout(updateTimeout);
    }
    updateTimeout = setTimeout(function() {
        getStatus().then(function(data) {
            var obj = JSON.parse(data);
            var status = obj.Status;
            if(null != applyInterval) {
                clearInterval(applyInterval);
                applyInterval = null;
            }
            var lastApply = new Date().getTime();
            function apply() {
                var thisApply = new Date().getTime();
                obj.RoomNextFreeInSeconds -= (thisApply - lastApply) / 1000;
                lastApply = thisApply;
                switch(status) {
                    case 0:
                        green();
                        break;
                    case 1:
                        if(obj.RoomNextFreeInSeconds < 600) {
                            orange();
                        } else {
                            red();
                        }
                        break;
                    case 2:
                        if(obj.CurrentMeeting && obj.CurrentMeeting.IsNotManaged) {
                            // non-managed meetings don't need to be started
                            red();
                        } else {
                            // this is a managed meeting that's on the verge of getting auto-cancelled - look wierd.
                            purple();
                        }
                        break;
                    default:
                        console.log('invalid status: ' + status);
                        break;
                }
            }
            apply();
            if(status == 1) {
                applyInterval = setInterval(apply, 10000);
            }

            if(obj.NextChangeSeconds) {
                updateIn((obj.NextChangeSeconds + 1) * 1000); // server advises us to check back at this time
            }
        });
    }, delay);
}

function purple() {
    console.log('purple');
    led.setCycle([
       { state: { red: 1, green: 0, blue: 0 }, duration: 200 },
       { state: { red: 1, green: 0, blue: 0 }, duration: 2000 },
       { state: { red: 0.625, green: 0.125, blue: 0.9375 }, duration: 200 }
    ]);
}

function orange() {
    console.log('orange');
    led.setCycle([
       { state: { red: 1, green: 0, blue: 0 }, duration: 200 },
       { state: { red: 1, green: 0, blue: 0 }, duration: 5000 },
       { state: { red: 1, green: 0.5, blue: 0 }, duration: 200 }
    ]);
}

function red() {
    console.log('red');
    led.setColor(1, 0, 0, 1000);
}
function green() {
    console.log('green');
    led.setColor(0, 1, 0, 1000);
}

function start() {
    setInterval(function() {
        updateIn(5000);
    }, 5 * 60 * 1000);
    var gotConnected = false;
    setTimeout(function() {
        if(!gotConnected) {
            console.log('Aborting - no connection');
            process.exit(-1);
        }
    }, 30000);

    var client  = new signalR.client(
        config.signalRServer,
        ['UpdateHub']
    );
    client.on('UpdateHub', 'Update', function(room) {
        console.log('got notification of change to ' + room);
        if(config.room == room) {
            updateIn(1);
        }
    });
    client.serviceHandlers.connected = function() {
        gotConnected = true;
        console.log('signalR connected');
    };
    client.serviceHandlers.connectionLost = function() {
        console.log('signalR connection lost - aborting');
        process.exit();
    };
    client.serviceHandlers.connectFailed = function() {
        console.log('signalR connection failed - aborting');
        process.exit();
    };
    client.serviceHandlers.reconnecting = function() {
        console.log('signalR reconnecting');
        return true;
    };
    client.serviceHandlers.reconnected = function() {
        console.log('signalR reconnected');
    };
    client.serviceHandlers.onerror = function(error) {
        console.log('signalR error: ' + error);
        // signalR doesn't seem to detect and reconnect - so we'll force it by restarting
        console.log('restarting');
        process.exit();
    };
}
// wait
<|endoftext|>


<commit_before>import React, { useState } from 'react'
import PropTypes from 'prop-types'
import Dialog from '@material-ui/core/Dialog'
import DialogActions from '@material-ui/core/DialogActions'
import DialogContent from '@material-ui/core/DialogContent'
import FormGroup from '@material-ui/core/FormGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import Switch from '@material-ui/core/Switch'
import Button from '@material-ui/core/Button'

const init = {
  verseNumbers: false,
  extraLargeMargins: false,
  reference: false,
}

function Settings({ open, handleClose }) {
  const [initialSettings, setInitialSettings] = useState(init)
  const [settings, setSettings] = useState(init)
  const [confirmed, setConfirmed] = useState(false)

  const handleEnter = () => {
    setInitialSettings(settings)
  }

  const handleExited = () => {
    !confirmed && setSettings(initialSettings)
  }

  const handleChange = name => event => {
    setSettings({ ...settings, [name]: event.target.checked })
  }

  const handleCancel = () => {
    setConfirmed(false)
    handleClose(initialSettings)
  }

  const handleConfirm = () => {
    setConfirmed(true)
    handleClose(settings)
  }

  return (
    <Dialog open={open} onEnter={handleEnter} onExited={handleExited} aria-labelledby="settings">
      <DialogContent>
        <FormGroup>
          <FormControlLabel
            control={
              <Switch
                checked={settings.verseNumbers}
                onChange={handleChange('verseNumbers')}
                value="verseNumbers"
                color="primary"
              />
            }
            label="Verse numbers"
          />
          <FormControlLabel
            control={
              <Switch
                checked={settings.extraLargeMargins}
                onChange={handleChange('extraLargeMargins')}
                value="extraLargeMargins"
                color="primary"
              />
            }
            label="Extra large margins"
          />
          <FormControlLabel
            control={
              <Switch
                checked={settings.reference}
                onChange={handleChange('reference')}
                value="reference"
                color="primary"
              />
            }
            label="Passage title"
          />
        </FormGroup>
      </DialogContent>
      <DialogActions>
        <Button onClick={handleCancel} color="secondary">
          Cancel
        </Button>
        <Button onClick={handleConfirm} color="primary">
          Confirm
        </Button>
      </DialogActions>
    </Dialog>
  )
}

Settings.propTypes = {
  open: PropTypes.bool.isRequired,
  handleClose: PropTypes.func.isRequired,
}

export default Settings
<commit_msg>Use contained button is settings dialog<commit_after>import React, { useState } from 'react'
import PropTypes from 'prop-types'
import Dialog from '@material-ui/core/Dialog'
import DialogActions from '@material-ui/core/DialogActions'
import DialogContent from '@material-ui/core/DialogContent'
import FormGroup from '@material-ui/core/FormGroup'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import Switch from '@material-ui/core/Switch'
import Button from '@material-ui/core/Button'

const init = {
  verseNumbers: false,
  extraLargeMargins: false,
  reference: false,
}

function Settings({ open, handleClose }) {
  const [initialSettings, setInitialSettings] = useState(init)
  const [settings, setSettings] = useState(init)
  const [confirmed, setConfirmed] = useState(false)

  const handleEnter = () => {
    setInitialSettings(settings)
  }

  const handleExited = () => {
    !confirmed && setSettings(initialSettings)
  }

  const handleChange = name => event => {
    setSettings({ ...settings, [name]: event.target.checked })
  }

  const handleCancel = () => {
    setConfirmed(false)
    handleClose(initialSettings)
  }

  const handleConfirm = () => {
    setConfirmed(true)
    handleClose(settings)
  }

  return (
    <Dialog open={open} onEnter={handleEnter} onExited={handleExited} aria-labelledby="settings">
      <DialogContent>
        <FormGroup>
          <FormControlLabel
            control={
              <Switch
                checked={settings.verseNumbers}
                onChange={handleChange('verseNumbers')}
                value="verseNumbers"
                color="primary"
              />
            }
            label="Verse numbers"
          />
          <FormControlLabel
            control={
              <Switch
                checked={settings.extraLargeMargins}
                onChange={handleChange('extraLargeMargins')}
                value="extraLargeMargins"
                color="primary"
              />
            }
            label="Extra large margins"
          />
          <FormControlLabel
            control={
              <Switch
                checked={settings.reference}
                onChange={handleChange('reference')}
                value="reference"
                color="primary"
              />
            }
            label="Passage title"
          />
        </FormGroup>
      </DialogContent>
      <DialogActions>
        <Button onClick={handleCancel} variant="contained" color="secondary">
          Cancel
        </Button>
        <Button onClick={handleConfirm} variant="contained" color="primary">
          Confirm
        </Button>
      </DialogActions>
    </Dialog>
  )
}

Settings.propTypes = {
  open: PropTypes.bool.isRequired,
  handleClose: PropTypes.func.isRequired,
}

export default Settings
<|endoftext|>


<commit_before>myApp.controller('pupilsController', ['$scope', 'pupils', 'uiGridConstants', function ($scope, pupils, uiGridConstants) {
    $scope.text = "List of pupils:";

    $scope.pupilsGrid = {
        showGridFooter: true,
        enableSorting: true,
        enableFiltering: true,

        columnDefs: [
   {
       name: "numb",
       field: "№ ",
       cellTemplate: '
{{grid.renderContainers.body.visibleRowCache.indexOf(row)+1}}
',
       width: 40,
       enableSorting: false,
       enableFiltering: false,
   },
   {
       name: "LastName",
       field: "LastName",
       sort: {
           direction: uiGridConstants.ASC,
           priority: 1
       }
   },
   {
       field: "FirstName",
       enableSorting:false,
       enableFiltering: false
   },
   {
       field: "PhoneNumber",
       enableFiltering: false,
       enableSorting: false,
   },
   {
       field: "Address",
       enableFiltering: false,
       enableSorting: false,
   },
   {
       field: "Email",
       enableFiltering: false,
       enableSorting: false,
   },
   {
       field: "Edit",
       cellTemplate: '
<button ng-click="grid.appScope.editHandler(row.entity.LastName)">Edit</button>
',
       enableFiltering: false,
       enableSorting: false
   },
   {
       field: "Delete",
       cellTemplate: '
<button ng-click="grid.appScope.deleteHandler(row.entity.LastName)">Delete</button>
',
       enableFiltering: false,
       enableSorting: false
   }
     ],
        onRegisterApi: function (gridApi) {
            $scope.grid1Api = gridApi;
        }
    };

    $scope.editHandler = function (value) {
        alert('Editing ' + value + ' !');
    };

    $scope.deleteHandler = function (value) {
        alert('Wanna delete ' + value + ' ?');
    };

    pupils.success(function (data) {
        $scope.pupilsGrid.data = data;
    });

}
]);
<commit_msg>no message<commit_after>myApp.controller('pupilsController', ['$scope', 'pupils', 'uiGridConstants', function ($scope, pupils, uiGridConstants) {
    $scope.text = "List of pupils:";

    $scope.pupilsGrid = {
        showGridFooter: true,
        enableSorting: true,
        enableFiltering: true,

        columnDefs: [
   {
       name: "numb",
       field: "№ ",
       cellTemplate: '
{{grid.renderContainers.body.visibleRowCache.indexOf(row)+1}}
',
       width: 50,
       enableSorting: false,
       enableFiltering: false,
   },
   {
       name: "LastName",
       field: "LastName",
       sort: {
           direction: uiGridConstants.ASC,
           priority: 1
       }
   },
   {
       field: "FirstName",
       enableSorting:false,
       enableFiltering: false
   },
   {
       field: "PhoneNumber",
       enableFiltering: false,
       enableSorting: false,
   },
   {
       field: "Address",
       enableFiltering: false,
       enableSorting: false,
   },
   {
       field: "Email",
       enableFiltering: false,
       enableSorting: false,
   },
   {
       field: "Edit",
       cellTemplate: '
<button ng-click="grid.appScope.editHandler(row.entity.LastName)">Edit</button>
',
       enableFiltering: false,
       enableSorting: false
   },
   {
       field: "Delete",
       cellTemplate: '
<button ng-click="grid.appScope.deleteHandler(row.entity.LastName)">Delete</button>
',
       enableFiltering: false,
       enableSorting: false
   }
     ],
        onRegisterApi: function (gridApi) {
            $scope.grid1Api = gridApi;
        }
    };

    $scope.editHandler = function (value) {
        alert('Editing ' + value + ' !');
    };

    $scope.deleteHandler = function (value) {
        alert('Wanna delete ' + value + ' ?');
    };

    pupils.success(function (data) {
        $scope.pupilsGrid.data = data;
    });

}
]);
<|endoftext|>


<commit_before>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

"use strict";

let passwords = require("../passwords");
let {getPasswords, addAlias, removeAlias} = passwords;
let {getCurrentHost} = require("./utils");

module.exports = function(panel)
{
  function passwordOp(func, successMessage, data)
  {
    let promise = func(data);
    promise.then(passwords => panel.port.emit(successMessage, passwords));
    promise.catch(e => {
      if (e == "alreadyExists")
        panel.port.emit("passwordAlreadyExists");
      else if (e)
        console.error(e);
    });
  }

  panel.port.on("addGeneratedPassword", passwordOp.bind(null, passwords.addGenerated, "passwordAdded"));
  panel.port.on("addLegacyPassword", passwordOp.bind(null, passwords.addLegacy, "passwordAdded"));
  panel.port.on("removePassword", passwordOp.bind(null, passwords.removePassword, "passwordRemoved"));

  panel.port.on("getPasswords", function(host)
  {
    let [origSite, site, passwords] = getPasswords(host);
    console.error([host, site, passwords]);
    panel.port.emit("setPasswords", {origSite, site, passwords});
  });

  panel.port.on("addAlias", function({site, alias})
  {
    addAlias(site, alias);
    let [origSite, newSite, passwords] = getPasswords(getCurrentHost());
    panel.port.emit("setPasswords", {origSite, site: newSite, passwords});
  });

  panel.port.on("removeAlias", function(site)
  {
    removeAlias(site);
    let [origSite, newSite, passwords] = getPasswords(getCurrentHost());
    panel.port.emit("setPasswords", {origSite, site: newSite, passwords});
  });
};
<commit_msg>Removed unintentionally committed diagnostic message<commit_after>/*
 * This Source Code is subject to the terms of the Mozilla Public License
 * version 2.0 (the "License"). You can obtain a copy of the License at
 * http://mozilla.org/MPL/2.0/.
 */

"use strict";

let passwords = require("../passwords");
let {getPasswords, addAlias, removeAlias} = passwords;
let {getCurrentHost} = require("./utils");

module.exports = function(panel)
{
  function passwordOp(func, successMessage, data)
  {
    let promise = func(data);
    promise.then(passwords => panel.port.emit(successMessage, passwords));
    promise.catch(e => {
      if (e == "alreadyExists")
        panel.port.emit("passwordAlreadyExists");
      else if (e)
        console.error(e);
    });
  }

  panel.port.on("addGeneratedPassword", passwordOp.bind(null, passwords.addGenerated, "passwordAdded"));
  panel.port.on("addLegacyPassword", passwordOp.bind(null, passwords.addLegacy, "passwordAdded"));
  panel.port.on("removePassword", passwordOp.bind(null, passwords.removePassword, "passwordRemoved"));

  panel.port.on("getPasswords", function(host)
  {
    let [origSite, site, passwords] = getPasswords(host);
    panel.port.emit("setPasswords", {origSite, site, passwords});
  });

  panel.port.on("addAlias", function({site, alias})
  {
    addAlias(site, alias);
    let [origSite, newSite, passwords] = getPasswords(getCurrentHost());
    panel.port.emit("setPasswords", {origSite, site: newSite, passwords});
  });

  panel.port.on("removeAlias", function(site)
  {
    removeAlias(site);
    let [origSite, newSite, passwords] = getPasswords(getCurrentHost());
    panel.port.emit("setPasswords", {origSite, site: newSite, passwords});
  });
};
<|endoftext|>


<commit_before>(function() {
    var CONST_NUMBER_OF_COMPONENTS = 5; // how many components to show in the report
    var result = { bugs: [] };
    var limit = sizeOfResult = 500;
    var completed = 0;

    // shared parameters
    var sharedParameters = Object.entries({
          'chfieldfrom': '2016-06-01',
          'chfieldto': 'NOW',
          'f1': 'flagtypes.name',
          'f2': 'component',
          'f3': 'component',
          'o1': 'notequals'
          'o2': 'notequals',
          'o3': 'notequals',
          'resolution': '---',
          'v1': 'needinfo?'
          'v2': 'general',
          'v3': 'untriaged',
          'email1': 'intermittent-bug-filer@mozilla.bugs',
          'emailtype1': 'notequals',
          'emailreporter1': 1,
          'limit': limit }).reduce((str, [key, value]) => str + `&${key}=${encodeURIComponent(value)}`, '');

    // base bugzilla API query
    var baseAPIRequest = 'https://bugzilla.mozilla.org/rest/bug?include_fields=id,priority,product,component&chfield=[Bug%20creation]' + // have to leave out chfield because '[', ']' can't be escaped
        sharedParameters + '&o4=greaterthan&f4=bug_id&limit=' + limit;

    var reportDetailRequest = 'https://bugzilla.mozilla.org/buglist.cgi?chfield=[Bug%20creation]' +
        sharedParameters;

    // convenience method for making links
    function buglistLink(value, product, component, priority) {
        priority = priority || null;
        var url = `${reportDetailRequest}&product=${encodeURIComponent(product)}&component=${encodeURIComponent(component)}`;
        if (priority) {
            url = `${url}&priority=${priority}`;
        }
        var link = `<a target="_blank" href="${url}">${value}</a>`;
        return link;
    }

    var tmp = document.querySelector('.tmp');
    var tableOuter = document.querySelector('table thead');

    if (!fetch) {
        view.innerHTML = "Your browser does not support the fetch standard, which is needed to load this page.";
        return;
    }

    // This recursively fetches all the open bugs in Firefox related components opened since June 1st, 2016
    // which don't have a pending needinfo, and are not in the general and untriaged components
    // this does not include security filtered bugs

    function getBugs(last) {
        var newLast;
        fetch(baseAPIRequest + '&v4=' + last)
            .then(function(response) { // $DEITY, I can't wait for await
                if (response.ok) {
                    response.json()
                    .then(function(data) {
                        newLast = data.bugs[data.bugs.length - 1].id;
                        /*
                            There are two ways we can fall out of this recursion: if the total
                            number of bugs is evenly divisible by limit (edge case) then we'll
                            err on fetching a result set twice, but not adding it, or if the number
                            of bugs in the batch returned is less than the limit, we'll add the last
                            batch and stop
                        */
                        if (newLast != last) {
                            completed ++;
                            console.log("completed", completed, "fetches");
                            Array.prototype.push.apply(result.bugs, data.bugs); // call push on each result.bugs
                            if (data.bugs.length === limit) {
                                console.log("calling again with last", newLast);
                                getBugs(newLast); // recursively call using the id of the last bug in the results as last
                            } else {
                                console.log("less bugs than limit");
                                complete();
                            }
                        } else {
                            console.log("edge case");
                            complete();
                        }
                    });
                }
            });
    }

    function complete() {
        console.log("all fetched!");
        process(result);
        setLastRunDate();
    }

    function process(result) {

        // stuff to collect results into
        var data = {};
        var report = {};
        var reportRows = '';
        var reportTable = '';
        var all = { '--': 0, P1: 0, P2: 0, P3: 0, P4: 0, P5: 0, total: 0 };
        var top = { '--': 0, P1: 0, P2: 0, P3: 0, P4: 0, P5: 0, total: 0 };

        // count bugs by product, component, and priority
        result.bugs.forEach((bug, i) => {
            if (!data[bug.product]) {
                data[bug.product] = {}; // add new product
            }
            if (!data[bug.product][bug.component]) {
                data[bug.product][bug.component] = { // add new component
                    total: 0,
                    '--': 0,
                    P1: 0,
                    P2: 0,
                    P3: 0,
                    P4: 0,
                    P5: 0
                }
            }
            data[bug.product][bug.component].total ++;
            all.total ++;
            data[bug.product][bug.component][bug.priority] ++;
            all[bug.priority] ++;
        });

        // generate a report by product of the components sorted
        // by the most untriaged bugs, descending
        Object.keys(data).forEach(product => {
            var list = [];
            Object.keys(data[product]).forEach(component => {
               list.push({component: component, untriaged: data[product][component]['--']});
            });
            report[product] = list.sort((a, b) => {
                return b.untriaged - a.untriaged; // sort in descending order
            });
        });

        // print the top five untriaged components in each product
        // and hey, lookit those ES6 format strings!
        Object.keys(report).forEach(product => {
            reportRows = reportRows + `<tbody>`;
            report[product].slice(0, CONST_NUMBER_OF_COMPONENTS).forEach(item => { // use Array().slice to get the top components
                var component = item.component;
                reportRows = reportRows + `

                    ${product}: ${component}
                    ${buglistLink(data[product][component]['--'], product, component,'--')}
                    ${buglistLink(data[product][component].P1, product, component, 'P1')}
                    ${buglistLink(data[product][component].P2, product, component, 'P2')}
                    ${buglistLink(data[product][component].P3, product, component, 'P3')}
                    ${buglistLink(data[product][component].P4, product, component, 'P4')}
                    ${buglistLink(data[product][component].P5, product, component, 'P5')}
                    ${buglistLink(data[product][component].total, product, component)}

`;
                // add to top components total
                top['--'] += data[product][component]['--'];
                top.P1 += data[product][component].P1;
                top.P2 += data[product][component].P2;
                top.P3 += data[product][component].P3;
                top.P4 += data[product][component].P4;
                top.P5 += data[product][component].P5;
                top.total += data[product][component].total;
            });
            reportRows = reportRows + `</tbody>`;
        });

        // glue it all together
        reportTable = `${reportRows}

                        All Components
                        ${all['--']}
                        ${all.P1}
                        ${all.P2}
                        ${all.P3}
                        ${all.P4}
                        ${all.P5}
                        ${all.total}

                        Top Components
                        ${top['--']}
                        ${top.P1}
                        ${top.P2}
                        ${top.P3}
                        ${top.P4}
                        ${top.P5}
                        ${top.total}

`;

        // put the report in the document
        tmp.remove();
        tableOuter.insertAdjacentHTML('afterend', reportTable);

    }

    function setLastRunDate() {
        document.querySelector('.updated p').insertAdjacentText('afterbegin', `Last updated at ${new Date().toTimeString()}; reload page to update.`);
    }

    getBugs(0);

})();

<commit_msg>Fix typo and remove redundant value<commit_after>(function() {
    var CONST_NUMBER_OF_COMPONENTS = 5; // how many components to show in the report
    var result = { bugs: [] };
    var limit = sizeOfResult = 500;
    var completed = 0;

    // shared parameters
    var sharedParameters = Object.entries({
          'chfieldfrom': '2016-06-01',
          'chfieldto': 'NOW',
          'f1': 'flagtypes.name',
          'f2': 'component',
          'f3': 'component',
          'o1': 'notequals',
          'o2': 'notequals',
          'o3': 'notequals',
          'resolution': '---',
          'v1': 'needinfo?'
          'v2': 'general',
          'v3': 'untriaged',
          'email1': 'intermittent-bug-filer@mozilla.bugs',
          'emailtype1': 'notequals',
          'emailreporter1': 1
    }).reduce((str, [key, value]) => str + `&${key}=${encodeURIComponent(value)}`, '');

    // base bugzilla API query
    var baseAPIRequest = 'https://bugzilla.mozilla.org/rest/bug?include_fields=id,priority,product,component&chfield=[Bug%20creation]' + // have to leave out chfield because '[', ']' can't be escaped
        sharedParameters + '&o4=greaterthan&f4=bug_id&limit=' + limit;

    var reportDetailRequest = 'https://bugzilla.mozilla.org/buglist.cgi?chfield=[Bug%20creation]' +
        sharedParameters;

    // convenience method for making links
    function buglistLink(value, product, component, priority) {
        priority = priority || null;
        var url = `${reportDetailRequest}&product=${encodeURIComponent(product)}&component=${encodeURIComponent(component)}`;
        if (priority) {
            url = `${url}&priority=${priority}`;
        }
        var link = `<a target="_blank" href="${url}">${value}</a>`;
        return link;
    }

    var tmp = document.querySelector('.tmp');
    var tableOuter = document.querySelector('table thead');

    if (!fetch) {
        view.innerHTML = "Your browser does not support the fetch standard, which is needed to load this page.";
        return;
    }

    // This recursively fetches all the open bugs in Firefox related components opened since June 1st, 2016
    // which don't have a pending needinfo, and are not in the general and untriaged components
    // this does not include security filtered bugs

    function getBugs(last) {
        var newLast;
        fetch(baseAPIRequest + '&v4=' + last)
            .then(function(response) { // $DEITY, I can't wait for await
                if (response.ok) {
                    response.json()
                    .then(function(data) {
                        newLast = data.bugs[data.bugs.length - 1].id;
                        /*
                            There are two ways we can fall out of this recursion: if the total
                            number of bugs is evenly divisible by limit (edge case) then we'll
                            err on fetching a result set twice, but not adding it, or if the number
                            of bugs in the batch returned is less than the limit, we'll add the last
                            batch and stop
                        */
                        if (newLast != last) {
                            completed ++;
                            console.log("completed", completed, "fetches");
                            Array.prototype.push.apply(result.bugs, data.bugs); // call push on each result.bugs
                            if (data.bugs.length === limit) {
                                console.log("calling again with last", newLast);
                                getBugs(newLast); // recursively call using the id of the last bug in the results as last
                            } else {
                                console.log("less bugs than limit");
                                complete();
                            }
                        } else {
                            console.log("edge case");
                            complete();
                        }
                    });
                }
            });
    }

    function complete() {
        console.log("all fetched!");
        process(result);
        setLastRunDate();
    }

    function process(result) {

        // stuff to collect results into
        var data = {};
        var report = {};
        var reportRows = '';
        var reportTable = '';
        var all = { '--': 0, P1: 0, P2: 0, P3: 0, P4: 0, P5: 0, total: 0 };
        var top = { '--': 0, P1: 0, P2: 0, P3: 0, P4: 0, P5: 0, total: 0 };

        // count bugs by product, component, and priority
        result.bugs.forEach((bug, i) => {
            if (!data[bug.product]) {
                data[bug.product] = {}; // add new product
            }
            if (!data[bug.product][bug.component]) {
                data[bug.product][bug.component] = { // add new component
                    total: 0,
                    '--': 0,
                    P1: 0,
                    P2: 0,
                    P3: 0,
                    P4: 0,
                    P5: 0
                }
            }
            data[bug.product][bug.component].total ++;
            all.total ++;
            data[bug.product][bug.component][bug.priority] ++;
            all[bug.priority] ++;
        });

        // generate a report by product of the components sorted
        // by the most untriaged bugs, descending
        Object.keys(data).forEach(product => {
            var list = [];
            Object.keys(data[product]).forEach(component => {
               list.push({component: component, untriaged: data[product][component]['--']});
            });
            report[product] = list.sort((a, b) => {
                return b.untriaged - a.untriaged; // sort in descending order
            });
        });

        // print the top five untriaged components in each product
        // and hey, lookit those ES6 format strings!
        Object.keys(report).forEach(product => {
            reportRows = reportRows + `<tbody>`;
            report[product].slice(0, CONST_NUMBER_OF_COMPONENTS).forEach(item => { // use Array().slice to get the top components
                var component = item.component;
                reportRows = reportRows + `

                    ${product}: ${component}
                    ${buglistLink(data[product][component]['--'], product, component,'--')}
                    ${buglistLink(data[product][component].P1, product, component, 'P1')}
                    ${buglistLink(data[product][component].P2, product, component, 'P2')}
                    ${buglistLink(data[product][component].P3, product, component, 'P3')}
                    ${buglistLink(data[product][component].P4, product, component, 'P4')}
                    ${buglistLink(data[product][component].P5, product, component, 'P5')}
                    ${buglistLink(data[product][component].total, product, component)}

`;
                // add to top components total
                top['--'] += data[product][component]['--'];
                top.P1 += data[product][component].P1;
                top.P2 += data[product][component].P2;
                top.P3 += data[product][component].P3;
                top.P4 += data[product][component].P4;
                top.P5 += data[product][component].P5;
                top.total += data[product][component].total;
            });
            reportRows = reportRows + `</tbody>`;
        });

        // glue it all together
        reportTable = `${reportRows}

                        All Components
                        ${all['--']}
                        ${all.P1}
                        ${all.P2}
                        ${all.P3}
                        ${all.P4}
                        ${all.P5}
                        ${all.total}

                        Top Components
                        ${top['--']}
                        ${top.P1}
                        ${top.P2}
                        ${top.P3}
                        ${top.P4}
                        ${top.P5}
                        ${top.total}

`;

        // put the report in the document
        tmp.remove();
        tableOuter.insertAdjacentHTML('afterend', reportTable);

    }

    function setLastRunDate() {
        document.querySelector('.updated p').insertAdjacentText('afterbegin', `Last updated at ${new Date().toTimeString()}; reload page to update.`);
    }

    getBugs(0);

})();

<|endoftext|>