<commit_before>"use strict";

import React from "react";

class NoteBox extends React.Component {
  constructor(props) {
    super(props);

    this.onMouseDown = this.onMouseDown.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.noteIsValid = this.noteIsValid.bind(this);
    this.formatNote = this.formatNote.bind(this);
  };

  onMouseDown(e) {
    this.props.setSelectedPatternNoteIndex(this.props.rowIndex, this.props.noteIndex);
    e.preventDefault();
  };

  onTouchStart(e) {
    this.props.setSelectedPatternNoteIndex(this.props.rowIndex, this.props.noteIndex);
  };

  noteIsValid(rawNoteString) {
    return /^$|^-$|^ $|(^[A-G](@|@@|#|##){0,1}[0-7]$)/.test(rawNoteString);
  };

  formatNote(rawNoteString) {
    let formattedNoteName = rawNoteString;

    formattedNoteName = formattedNoteName.toUpperCase();
    formattedNoteName = formattedNoteName.replace("##", "𝄪");
    formattedNoteName = formattedNoteName.replace("#", "♯");
    formattedNoteName = formattedNoteName.replace("@@", "𝄫");
    formattedNoteName = formattedNoteName.replace("@", "♭");
    formattedNoteName = formattedNoteName.replace("-", "—");

    return formattedNoteName;
  };

  render() {
    let formattedNoteName = this.formatNote(this.props.note.name);
    let noteIsSelected = this.props.selectedPatternRowIndex === this.props.rowIndex &&
                         this.props.selectedPatternNoteIndex === this.props.noteIndex;
    let noteIsValid = noteIsSelected || this.noteIsValid(this.props.note.name);

    return {formattedNoteName}
  };
};

class NoteInput extends React.Component {
  constructor(props) {
    super(props);

    this.setNoteValue = this.setNoteValue.bind(this);
    this.extractNoteParts = this.extractNoteParts.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
  };

  setNoteValue(newNoteValue) {
    this.props.setNoteValue(newNoteValue, this.props.patternID, this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex);
  };

  extractNoteParts(noteString) {
    let noteNameMatches = noteString.match(/^[A-G]/);
    let noteName = (noteNameMatches === null) ? "" : noteNameMatches[0];
    let octaveMatches = noteString.match(/\d$/);
    let octave = (octaveMatches === null) ? "" : octaveMatches[0];

    let modifier = noteString;
    if (noteName !== "") {
      modifier = noteString.slice(1);
    }
    if (octave !== "") {
      modifier = modifier.slice(0, modifier.length - 1);
    }

    return {noteName: noteName, modifier: modifier, octave: octave};
  };

  onBlur(e) {
    if (!this.props.keyboardActive) {
      this.props.setSelectedPatternNoteIndex(undefined, undefined);
    }
  };

  onKeyDown(e) {
    const SPACE = 32;
    const DELETE = 46;
    const BACKSPACE = 8;
    const ZERO = 48;
    const TWO = 50;
    const THREE = 51;
    const FOUR = 52;
    const SIX = 54;
    const NINE = 57;
    const A = 65;
    const G = 71;
    const DASH = 189;
    const LEFT_ARROW = 37;
    const RIGHT_ARROW = 39;
    const UP_ARROW = 38;
    const DOWN_ARROW = 40;

    let element = e.target;
    let noteParts;

    if (e.keyCode === SPACE || e.keyCode === DELETE || e.keyCode === BACKSPACE) {
      this.setNoteValue("");
    }
    else if (e.keyCode === TWO && e.shiftKey) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "" || noteParts.modifier === "@") {
        noteParts.modifier += "@";
      }
      else if (noteParts.modifier === "@@") {
        noteParts.modifier = "";
      }
      else {
        noteParts.modifier = "@";
      }

      this.setNoteValue(noteParts.noteName + noteParts.modifier + noteParts.octave);
    }
    else if (e.keyCode === THREE && e.shiftKey) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "" || noteParts.modifier === "#") {
        noteParts.modifier += "#";
      }
      else if (noteParts.modifier === "##") {
        noteParts.modifier = "";
      }
      else {
        noteParts.modifier = "#";
      }

      this.setNoteValue(noteParts.noteName + noteParts.modifier + noteParts.octave);
    }
    else if (e.keyCode === FOUR && e.shiftKey) {
      this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, this.props.noteCount - 1);
    }
     else if (e.keyCode === SIX && e.shiftKey) {
      this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, 0);
    }
    else if (e.keyCode >= ZERO && e.keyCode <= NINE && !e.shiftKey) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "-") {
        noteParts.modifier = "";
      }

      this.setNoteValue(noteParts.noteName + noteParts.modifier + String.fromCharCode(e.keyCode));
    }
    else if (e.keyCode >= A && e.keyCode <= G) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "-") {
        noteParts.modifier = "";
      }

      this.setNoteValue(String.fromCharCode(e.keyCode) + noteParts.modifier + noteParts.octave);
    }
    else if (e.keyCode === DASH && !e.shiftKey) {
      this.setNoteValue("-");
    }
    else if (e.keyCode === LEFT_ARROW) {
      if (this.props.selectedPatternNoteIndex > 0) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex - 1);
      }
    }
    else if (e.keyCode === RIGHT_ARROW) {
      if (this.props.selectedPatternNoteIndex < this.props.noteCount - 1) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex + 1);
      }
    }
    else if (e.keyCode === UP_ARROW) {
      if (this.props.selectedPatternRowIndex > 0) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex - 1, this.props.selectedPatternNoteIndex);
      }
    }
    else if (e.keyCode === DOWN_ARROW) {
      if (this.props.selectedPatternRowIndex < this.props.rowCount - 1) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex + 1, this.props.selectedPatternNoteIndex);
      }
    }

    e.preventDefault();
  };

  componentDidUpdate() {
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      this.noteInput.focus();
    }
  };

  render() {
    return <input ref={(input) => { this.noteInput = input; }} className="hidden-input" type="text" readOnly={true} onKeyDown={this.onKeyDown} onBlur={this.onBlur} />
  };
};

class PatternRowRemoveButton extends React.PureComponent {
  constructor(props) {
    super(props);

    this.removePatternRow = this.removePatternRow.bind(this);
  }

  removePatternRow(e) {
    this.props.removePatternRow(this.props.patternID, this.props.rowIndex);
  };

  render() {
    return <button className="button-small button-hollow full-width round" onClick={this.removePatternRow}>X</button>;
  };
};

class PatternEditor extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      tipsAndTricksVisible: false,
    };

    this.close = this.close.bind(this);
    this.addPatternRow = this.addPatternRow.bind(this);
    this.removePatternRow = this.removePatternRow.bind(this);
    this.setTipsAndTricksVisible = this.setTipsAndTricksVisible.bind(this);
    this.eraseNote = this.eraseNote.bind(this);
    this.setNoteAsDash = this.setNoteAsDash.bind(this);
  };

  close(e) {
    this.props.setSelectedPattern(undefined);
  };

  addPatternRow(e) {
    this.props.addPatternRow(this.props.selectedPattern.id);
  };

  removePatternRow(e) {
    this.props.removePatternRow();
  };

  setTipsAndTricksVisible(e) {
    this.setState((prevState, props) => ({
      tipsAndTricksVisible: !prevState.tipsAndTricksVisible,
    }));
  };

  eraseNote(e) {
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      this.props.setNoteValue("", this.props.selectedPattern.id, this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex);

      // Prevent the currently selected note input from losing focus,
      // which will prevent the note from being set properly.
      e.preventDefault();
    }
  };

  setNoteAsDash(e) {
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      this.props.setNoteValue("-", this.props.selectedPattern.id, this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex);

      // Prevent the currently selected note input from losing focus,
      // which will prevent the note from being set properly.
      e.preventDefault();
    }
  };

  render() {
    const PATTERN_LENGTH = this.props.selectedPattern.stepCount;

    let selectedNote;
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      selectedNote = this.props.selectedPattern.rows[this.props.selectedPatternRowIndex].notes[this.props.selectedPatternNoteIndex];
    }
    else {
      selectedNote = { name: "" };
    }

    return

      <button className="button-link" onClick={this.close}>← Sequencer</button>

{this.props.selectedPattern.name}

        <button className="ml-half button-link" onClick={this.setTipsAndTricksVisible}>Tips and Tricks</button>

      {(this.state.tipsAndTricksVisible === true) &&

To enter a note, select a note box, and either play a note on the on-screen keyboard or type the note name.

A note is a letter between A and G plus an octave between 0 and 7. For example: A3, C♯4, E♭2

Use ‘#’ to enter a sharp, and ‘@’ to enter a flat. Press twice to double sharp/flat, thrice to remove the sharp/flat.

Use — to lengthen a note. For example, ‘A4 — — —’ will last for 4 steps, while ‘A4 —’ will last for two, and ‘A4’ will last for one.

Press SPACE, DELETE, or BACKSPACE to clear the current note.

Use the left/right arrow keys to move between notes, and the up/down arrow keys to move between rows.

      }
      <NoteInput note={selectedNote} patternID={this.props.selectedPattern.id} rowCount={this.props.selectedPattern.rows.length} noteCount={PATTERN_LENGTH} selectedPatternRowIndex={this.props.selectedPatternRowIndex} selectedPatternNoteIndex={this.props.selectedPatternNoteIndex} setSelectedPatternNoteIndex={this.props.setSelectedPatternNoteIndex} setNoteValue={this.props.setNoteValue} keyboardActive={this.props.keyboardActive} />

              {Array(PATTERN_LENGTH).fill(undefined).map((_, noteIndex) =>

{noteIndex + 1}

              )}

          {this.props.selectedPattern.rows.map((patternRow, rowIndex) =>

              {patternRow.notes.slice(0, PATTERN_LENGTH).map((note, noteIndex) =>

                <NoteBox note={note} rowIndex={rowIndex} noteIndex={noteIndex} selectedPatternRowIndex={this.props.selectedPatternRowIndex} selectedPatternNoteIndex={this.props.selectedPatternNoteIndex} setSelectedPatternNoteIndex={this.props.setSelectedPatternNoteIndex} />

              )}

          )}

          {this.props.selectedPattern.rows.map((patternRow, rowIndex) =>

            <PatternRowRemoveButton patternID={this.props.selectedPattern.id} rowIndex={rowIndex} removePatternRow={this.props.removePatternRow} />

          )}

        <button className="button-full button-hollow" onClick={this.addPatternRow}>Add Row</button>

          <button className="inline-block button-full button-hollow mr-half" disabled={this.props.selectedPatternRowIndex === undefined || this.props.selectedPatternNoteIndex === undefined} onMouseDown={this.eraseNote}>⌦</button>
          <button className="inline-block button-full button-hollow" disabled={this.props.selectedPatternRowIndex === undefined || this.props.selectedPatternNoteIndex === undefined} onMouseDown={this.setNoteAsDash}>—</button>

;
  };
};

export { PatternEditor };
<commit_msg>Fixing "-" key to work in pattern editor in Firefox<commit_after>"use strict";

import React from "react";

class NoteBox extends React.Component {
  constructor(props) {
    super(props);

    this.onMouseDown = this.onMouseDown.bind(this);
    this.onTouchStart = this.onTouchStart.bind(this);
    this.noteIsValid = this.noteIsValid.bind(this);
    this.formatNote = this.formatNote.bind(this);
  };

  onMouseDown(e) {
    this.props.setSelectedPatternNoteIndex(this.props.rowIndex, this.props.noteIndex);
    e.preventDefault();
  };

  onTouchStart(e) {
    this.props.setSelectedPatternNoteIndex(this.props.rowIndex, this.props.noteIndex);
  };

  noteIsValid(rawNoteString) {
    return /^$|^-$|^ $|(^[A-G](@|@@|#|##){0,1}[0-7]$)/.test(rawNoteString);
  };

  formatNote(rawNoteString) {
    let formattedNoteName = rawNoteString;

    formattedNoteName = formattedNoteName.toUpperCase();
    formattedNoteName = formattedNoteName.replace("##", "𝄪");
    formattedNoteName = formattedNoteName.replace("#", "♯");
    formattedNoteName = formattedNoteName.replace("@@", "𝄫");
    formattedNoteName = formattedNoteName.replace("@", "♭");
    formattedNoteName = formattedNoteName.replace("-", "—");

    return formattedNoteName;
  };

  render() {
    let formattedNoteName = this.formatNote(this.props.note.name);
    let noteIsSelected = this.props.selectedPatternRowIndex === this.props.rowIndex &&
                         this.props.selectedPatternNoteIndex === this.props.noteIndex;
    let noteIsValid = noteIsSelected || this.noteIsValid(this.props.note.name);

    return {formattedNoteName}
  };
};

class NoteInput extends React.Component {
  constructor(props) {
    super(props);

    this.setNoteValue = this.setNoteValue.bind(this);
    this.extractNoteParts = this.extractNoteParts.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
  };

  setNoteValue(newNoteValue) {
    this.props.setNoteValue(newNoteValue, this.props.patternID, this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex);
  };

  extractNoteParts(noteString) {
    let noteNameMatches = noteString.match(/^[A-G]/);
    let noteName = (noteNameMatches === null) ? "" : noteNameMatches[0];
    let octaveMatches = noteString.match(/\d$/);
    let octave = (octaveMatches === null) ? "" : octaveMatches[0];

    let modifier = noteString;
    if (noteName !== "") {
      modifier = noteString.slice(1);
    }
    if (octave !== "") {
      modifier = modifier.slice(0, modifier.length - 1);
    }

    return {noteName: noteName, modifier: modifier, octave: octave};
  };

  onBlur(e) {
    if (!this.props.keyboardActive) {
      this.props.setSelectedPatternNoteIndex(undefined, undefined);
    }
  };

  onKeyDown(e) {
    const SPACE = 32;
    const DELETE = 46;
    const BACKSPACE = 8;
    const ZERO = 48;
    const TWO = 50;
    const THREE = 51;
    const FOUR = 52;
    const SIX = 54;
    const NINE = 57;
    const A = 65;
    const G = 71;
    const DASH = 189;
    const DASH_FIREFOX = 173;  // Firefox has a different keycode from the "-" key than other browsers
    const LEFT_ARROW = 37;
    const RIGHT_ARROW = 39;
    const UP_ARROW = 38;
    const DOWN_ARROW = 40;

    let element = e.target;
    let noteParts;

    if (e.keyCode === SPACE || e.keyCode === DELETE || e.keyCode === BACKSPACE) {
      this.setNoteValue("");
    }
    else if (e.keyCode === TWO && e.shiftKey) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "" || noteParts.modifier === "@") {
        noteParts.modifier += "@";
      }
      else if (noteParts.modifier === "@@") {
        noteParts.modifier = "";
      }
      else {
        noteParts.modifier = "@";
      }

      this.setNoteValue(noteParts.noteName + noteParts.modifier + noteParts.octave);
    }
    else if (e.keyCode === THREE && e.shiftKey) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "" || noteParts.modifier === "#") {
        noteParts.modifier += "#";
      }
      else if (noteParts.modifier === "##") {
        noteParts.modifier = "";
      }
      else {
        noteParts.modifier = "#";
      }

      this.setNoteValue(noteParts.noteName + noteParts.modifier + noteParts.octave);
    }
    else if (e.keyCode === FOUR && e.shiftKey) {
      this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, this.props.noteCount - 1);
    }
     else if (e.keyCode === SIX && e.shiftKey) {
      this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, 0);
    }
    else if (e.keyCode >= ZERO && e.keyCode <= NINE && !e.shiftKey) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "-") {
        noteParts.modifier = "";
      }

      this.setNoteValue(noteParts.noteName + noteParts.modifier + String.fromCharCode(e.keyCode));
    }
    else if (e.keyCode >= A && e.keyCode <= G) {
      noteParts = this.extractNoteParts(this.props.note.name);

      if (noteParts.modifier === "-") {
        noteParts.modifier = "";
      }

      this.setNoteValue(String.fromCharCode(e.keyCode) + noteParts.modifier + noteParts.octave);
    }
    else if ((e.keyCode === DASH || e.keyCode === DASH_FIREFOX) && !e.shiftKey) {
      this.setNoteValue("-");
    }
    else if (e.keyCode === LEFT_ARROW) {
      if (this.props.selectedPatternNoteIndex > 0) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex - 1);
      }
    }
    else if (e.keyCode === RIGHT_ARROW) {
      if (this.props.selectedPatternNoteIndex < this.props.noteCount - 1) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex + 1);
      }
    }
    else if (e.keyCode === UP_ARROW) {
      if (this.props.selectedPatternRowIndex > 0) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex - 1, this.props.selectedPatternNoteIndex);
      }
    }
    else if (e.keyCode === DOWN_ARROW) {
      if (this.props.selectedPatternRowIndex < this.props.rowCount - 1) {
        this.props.setSelectedPatternNoteIndex(this.props.selectedPatternRowIndex + 1, this.props.selectedPatternNoteIndex);
      }
    }

    e.preventDefault();
  };

  componentDidUpdate() {
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      this.noteInput.focus();
    }
  };

  render() {
    return <input ref={(input) => { this.noteInput = input; }} className="hidden-input" type="text" readOnly={true} onKeyDown={this.onKeyDown} onBlur={this.onBlur} />
  };
};

class PatternRowRemoveButton extends React.PureComponent {
  constructor(props) {
    super(props);

    this.removePatternRow = this.removePatternRow.bind(this);
  }

  removePatternRow(e) {
    this.props.removePatternRow(this.props.patternID, this.props.rowIndex);
  };

  render() {
    return <button className="button-small button-hollow full-width round" onClick={this.removePatternRow}>X</button>;
  };
};

class PatternEditor extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      tipsAndTricksVisible: false,
    };

    this.close = this.close.bind(this);
    this.addPatternRow = this.addPatternRow.bind(this);
    this.removePatternRow = this.removePatternRow.bind(this);
    this.setTipsAndTricksVisible = this.setTipsAndTricksVisible.bind(this);
    this.eraseNote = this.eraseNote.bind(this);
    this.setNoteAsDash = this.setNoteAsDash.bind(this);
  };

  close(e) {
    this.props.setSelectedPattern(undefined);
  };

  addPatternRow(e) {
    this.props.addPatternRow(this.props.selectedPattern.id);
  };

  removePatternRow(e) {
    this.props.removePatternRow();
  };

  setTipsAndTricksVisible(e) {
    this.setState((prevState, props) => ({
      tipsAndTricksVisible: !prevState.tipsAndTricksVisible,
    }));
  };

  eraseNote(e) {
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      this.props.setNoteValue("", this.props.selectedPattern.id, this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex);

      // Prevent the currently selected note input from losing focus,
      // which will prevent the note from being set properly.
      e.preventDefault();
    }
  };

  setNoteAsDash(e) {
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      this.props.setNoteValue("-", this.props.selectedPattern.id, this.props.selectedPatternRowIndex, this.props.selectedPatternNoteIndex);

      // Prevent the currently selected note input from losing focus,
      // which will prevent the note from being set properly.
      e.preventDefault();
    }
  };

  render() {
    const PATTERN_LENGTH = this.props.selectedPattern.stepCount;

    let selectedNote;
    if (this.props.selectedPatternRowIndex !== undefined && this.props.selectedPatternNoteIndex !== undefined) {
      selectedNote = this.props.selectedPattern.rows[this.props.selectedPatternRowIndex].notes[this.props.selectedPatternNoteIndex];
    }
    else {
      selectedNote = { name: "" };
    }

    return

      <button className="button-link" onClick={this.close}>← Sequencer</button>

{this.props.selectedPattern.name}

        <button className="ml-half button-link" onClick={this.setTipsAndTricksVisible}>Tips and Tricks</button>

      {(this.state.tipsAndTricksVisible === true) &&

To enter a note, select a note box, and either play a note on the on-screen keyboard or type the note name.

A note is a letter between A and G plus an octave between 0 and 7. For example: A3, C♯4, E♭2

Use ‘#’ to enter a sharp, and ‘@’ to enter a flat. Press twice to double sharp/flat, thrice to remove the sharp/flat.

Use — to lengthen a note. For example, ‘A4 — — —’ will last for 4 steps, while ‘A4 —’ will last for two, and ‘A4’ will last for one.

Press SPACE, DELETE, or BACKSPACE to clear the current note.

Use the left/right arrow keys to move between notes, and the up/down arrow keys to move between rows.

      }
      <NoteInput note={selectedNote} patternID={this.props.selectedPattern.id} rowCount={this.props.selectedPattern.rows.length} noteCount={PATTERN_LENGTH} selectedPatternRowIndex={this.props.selectedPatternRowIndex} selectedPatternNoteIndex={this.props.selectedPatternNoteIndex} setSelectedPatternNoteIndex={this.props.setSelectedPatternNoteIndex} setNoteValue={this.props.setNoteValue} keyboardActive={this.props.keyboardActive} />

              {Array(PATTERN_LENGTH).fill(undefined).map((_, noteIndex) =>

{noteIndex + 1}

              )}

          {this.props.selectedPattern.rows.map((patternRow, rowIndex) =>

              {patternRow.notes.slice(0, PATTERN_LENGTH).map((note, noteIndex) =>

                <NoteBox note={note} rowIndex={rowIndex} noteIndex={noteIndex} selectedPatternRowIndex={this.props.selectedPatternRowIndex} selectedPatternNoteIndex={this.props.selectedPatternNoteIndex} setSelectedPatternNoteIndex={this.props.setSelectedPatternNoteIndex} />

              )}

          )}

          {this.props.selectedPattern.rows.map((patternRow, rowIndex) =>

            <PatternRowRemoveButton patternID={this.props.selectedPattern.id} rowIndex={rowIndex} removePatternRow={this.props.removePatternRow} />

          )}

        <button className="button-full button-hollow" onClick={this.addPatternRow}>Add Row</button>

          <button className="inline-block button-full button-hollow mr-half" disabled={this.props.selectedPatternRowIndex === undefined || this.props.selectedPatternNoteIndex === undefined} onMouseDown={this.eraseNote}>⌦</button>
          <button className="inline-block button-full button-hollow" disabled={this.props.selectedPatternRowIndex === undefined || this.props.selectedPatternNoteIndex === undefined} onMouseDown={this.setNoteAsDash}>—</button>

;
  };
};

export { PatternEditor };
<|endoftext|>


<commit_before>var should = require('should');
var exec = require('child_process').exec;

// Expected Output
// error: option `-e, --except <exception>' argument missing

function shouldBeCommandFailed(error) {
  error.message.includes('Command failed:').should.equal(true);
  error.message.includes('error:').should.equal(true);
}

function testErrorMessage(error, stdout, stderr) {
  shouldBeCommandFailed(error);
  stdout.should.equal('');
  error.message.includes("error: option `-e, --except <exception>' argument missing").should.equal(true);
}

var cmd = 'node index.js -e';

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
})

cmd = 'node index.js -i hello -e';

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
})

cmd = 'node index.js hello -i -e'

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
})

cmd = 'node index.js hello-man -e'

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
})
<commit_msg>jshint<commit_after>var should = require('should');
var exec = require('child_process').exec;

// Expected Output
// error: option `-e, --except <exception>' argument missing

function shouldBeCommandFailed(error) {
  error.message.includes('Command failed:').should.equal(true);
  error.message.includes('error:').should.equal(true);
}

function testErrorMessage(error, stdout) {
  shouldBeCommandFailed(error);
  stdout.should.equal('');
  error.message.includes("error: option `-e, --except <exception>' argument missing").should.equal(true);
}

var cmd = 'node index.js -e';

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
});

cmd = 'node index.js -i hello -e';

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
});

cmd = 'node index.js hello -i -e';

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
});

cmd = 'node index.js hello-man -e';

exec(cmd, function(error, stdout, stderr) {
  testErrorMessage(error, stdout, stderr);
});
<|endoftext|>


<commit_before>//////////////////////
// Main entry point //
//////////////////////

'use strict';

var pay = angular.module('pay', [
    'ngRoute',
    'ngResource'
]).run(function ($rootScope) {
    // This will removes everything under scripts : modal divs, autocompletion divs, etc.
    $rootScope.$on('$locationChangeStart', function (event, next, current) {
        $('body > script').last().nextUntil().remove();
    });
});

pay.config(['$routeProvider', function ($routeProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'ticketsList/ticketsList.html'
        })
        .when('/etuAuth', {
            templateUrl: 'etuAuth/etuAuth.html'
        })
        .when('/admin', {
            templateUrl: 'admin/admin.html'
        })
        .when('/admin/event/:eventId', {
            templateUrl: 'adminEvent/adminEvent.html'
        })
        .when('/buckuttAdmin', {
            templateUrl: 'buckuttAdmin/buckuttAdmin.html'
        })
        .when('/buckutt', {
            templateUrl: 'buckutt/buckutt.html'
        })
        .otherwise({
            redirectTo: '/'
        });
}]);

// Auth token
pay.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push(function () {
        return {
            request: function (config) {
                if (pay.auth && pay.auth.etu && pay.auth.etu.token) {
                    config.headers['Auth-User'] = pay.auth.etu.id;
                    config.headers['Auth-Token'] = pay.auth.etu.token;
                }
                return config;
            }
        };
    });
}]);

// Datepickers
// Set min date to now + 1 hour
var minDate = moment().add(1, 'hour').toDate();
$.extend($.fn.datetimepicker.defaults, {
    useCurrent: true,
    stepping: 15,
    minDate: minDate,
    showTodayButton: false,
    locale: 'fr',
    useStrict: true,
    sideBySide: true,
    daysOfWeekDisabled: [0, 7]
});
<commit_msg>Add jwt to headers<commit_after>//////////////////////
// Main entry point //
//////////////////////

'use strict';

var pay = angular.module('pay', [
    'ngRoute',
    'ngResource'
]).run(function ($rootScope) {
    // This will removes everything under scripts : modal divs, autocompletion divs, etc.
    $rootScope.$on('$locationChangeStart', function (event, next, current) {
        $('body > script').last().nextUntil().remove();
    });
});

pay.config(['$routeProvider', function ($routeProvider) {
    $routeProvider
        .when('/', {
            templateUrl: 'ticketsList/ticketsList.html'
        })
        .when('/etuAuth', {
            templateUrl: 'etuAuth/etuAuth.html'
        })
        .when('/admin', {
            templateUrl: 'admin/admin.html'
        })
        .when('/admin/event/:eventId', {
            templateUrl: 'adminEvent/adminEvent.html'
        })
        .when('/buckuttAdmin', {
            templateUrl: 'buckuttAdmin/buckuttAdmin.html'
        })
        .when('/buckutt', {
            templateUrl: 'buckutt/buckutt.html'
        })
        .otherwise({
            redirectTo: '/'
        });
}]);

// Auth token
pay.config(['$httpProvider', function ($httpProvider) {
    $httpProvider.interceptors.push(function () {
        return {
            request: function (config) {
                if (pay.auth && pay.auth.etu && pay.auth.etu.jwt) {
                    config.headers['Auth-JWT'] = pay.auth.etu.jwt;
                }
                return config;
            }
        };
    });
}]);

// Datepickers
// Set min date to now + 1 hour
var minDate = moment().add(1, 'hour').toDate();
$.extend($.fn.datetimepicker.defaults, {
    useCurrent: true,
    stepping: 15,
    minDate: minDate,
    showTodayButton: false,
    locale: 'fr',
    useStrict: true,
    sideBySide: true,
    daysOfWeekDisabled: [0, 7]
});
<|endoftext|>


<commit_before>imagespace.views.SearchView = imagespace.View.extend({
    events: {
        'click .im-add-user-data': function (event) {
            var id = $(event.currentTarget).attr('im-id'),
                image = this.imageIdMap[id];
            imagespace.userDataView.addUserImage(image);
        },

        'click .im-details': function (event) {
            var id = $(event.currentTarget).attr('im-id'),
                image = this.imageIdMap[id];
            this.imageDetailWidget = new imagespace.views.ImageDetailWidget({
                el: $('#g-dialog-container'),
                image: image,
                parentView: this
            });
            this.imageDetailWidget.render();
        },

        'click .im-find-similar': function (event) {
            var id = $(event.currentTarget).attr('im-id'),
                image = this.imageIdMap[id];
            this.$('.im-find-similar')
                .addClass('btn-info disabled')
                .removeClass('btn-default')
                .html('');
            if (image.histogram) {
                this.findSimilarImages(image);
            } else {
                girder.restRequest({
                    path: 'imagefeatures',
                    data: {
                        url: image.imageUrl
                    },
                    method: 'POST'
                }).done(_.bind(function (features) {
                    image.histogram = features.histogram;
                    this.findSimilarImages(image);
                }, this));
            }
        },

        'mouseover .im-image-area': function (event) {
            $(event.currentTarget).find('.im-caption-content').removeClass('hidden');
        },

        'mouseout .im-image-area': function (event) {
            $(event.currentTarget).find('.im-caption-content').addClass('hidden');
        }
    },

    initialize: function (settings) {
        girder.cancelRestRequests('fetch');
        this.results = settings.results;
        this.imageIdMap = {};
        this.results.forEach(_.bind(function (result) {
            var parts = result.id.split('/'),
                file = parts[parts.length - 1];
            if (result.id.indexOf('cmuImages') !== -1) {
                file = 'cmuImages/' + file;
            }
            result.imageUrl = imagespace.prefix + file;
            this.imageIdMap[result.id] = result;
        }, this));

        // Place in order if ids are explicit
        var queryParts = $('.im-search').val().split(' '),
            idResults = [],
            idResultMap = {},
            remaining = [];
        queryParts.forEach(_.bind(function (part) {
            var partId = part.match(/id:\"(.*)\"/);
            if (partId && partId.length === 2 && this.imageIdMap[partId[1]]) {
                idResults.push(this.imageIdMap[partId[1]]);
                idResultMap[partId[1]] = true;
            }
        }, this));

        // Gather the rest
        this.results.forEach(function (result) {
            if (!idResultMap[result.id]) {
                remaining.push(result);
            }
        });

        // Construct new result list with id results first
        this.results = idResults.concat(remaining);

        this.render();
    },

    render: function () {
        this.$el.html(imagespace.templates.search({
            results: this.results,
            showText: true
        }));
        return this;
    },

    findSimilarImages: function(image) {
        girder.restRequest({
            path: 'imagesearch',
            data: {
                url: image.imageUrl,
                histogram: JSON.stringify(image.histogram || []),
                limit: 100
            }
        }).done(_.bind(function (results) {
            var query = '(', count = 0;
            results.forEach(_.bind(function (result, index) {
                 var parts = result.id.split('/'),
                     file = parts[parts.length - 1];
                 if (file.length < 30) {
                     return;
                 }
                 if (result.id.indexOf('cmuImages') !== -1) {
                     file = 'cmuImages/' + file;
                 }
                 file = '/data/roxyimages/' + file;
                 if (count < 100) {
                    query += 'id:"' + file + '" ';
                    count += 1;
                }
            }, this));
            query += ')';
            imagespace.router.navigate('search/' + encodeURIComponent(query), {trigger: true});
        }, this));
    }

});

imagespace.router.route('search/:query', 'search', function (query) {
    $('.im-search').val(query);
    girder.restRequest({
        path: 'imagesearch',
        data: {
            query: query,
            limit: 100
        }
    }).done(function (results) {
        girder.events.trigger('g:navigateTo', imagespace.views.SearchView, {
            results: results
        });
    });
});
<commit_msg>Only show spinner under searched image<commit_after>imagespace.views.SearchView = imagespace.View.extend({
    events: {
        'click .im-add-user-data': function (event) {
            var id = $(event.currentTarget).attr('im-id'),
                image = this.imageIdMap[id];
            imagespace.userDataView.addUserImage(image);
        },

        'click .im-details': function (event) {
            var id = $(event.currentTarget).attr('im-id'),
                image = this.imageIdMap[id];
            this.imageDetailWidget = new imagespace.views.ImageDetailWidget({
                el: $('#g-dialog-container'),
                image: image,
                parentView: this
            });
            this.imageDetailWidget.render();
        },

        'click .im-find-similar': function (event) {
            var id = $(event.currentTarget).attr('im-id'),
                image = this.imageIdMap[id];
            $(event.currentTarget).parent().find('.im-find-similar')
                .addClass('disabled')
                .html('');
            if (image.histogram) {
                this.findSimilarImages(image);
            } else {
                girder.restRequest({
                    path: 'imagefeatures',
                    data: {
                        url: image.imageUrl
                    },
                    method: 'POST'
                }).done(_.bind(function (features) {
                    image.histogram = features.histogram;
                    this.findSimilarImages(image);
                }, this));
            }
        },

        'mouseover .im-image-area': function (event) {
            $(event.currentTarget).find('.im-caption-content').removeClass('hidden');
        },

        'mouseout .im-image-area': function (event) {
            $(event.currentTarget).find('.im-caption-content').addClass('hidden');
        }
    },

    initialize: function (settings) {
        girder.cancelRestRequests('fetch');
        this.results = settings.results;
        this.imageIdMap = {};
        this.results.forEach(_.bind(function (result) {
            var parts = result.id.split('/'),
                file = parts[parts.length - 1];
            if (result.id.indexOf('cmuImages') !== -1) {
                file = 'cmuImages/' + file;
            }
            result.imageUrl = imagespace.prefix + file;
            this.imageIdMap[result.id] = result;
        }, this));

        // Place in order if ids are explicit
        var queryParts = $('.im-search').val().split(' '),
            idResults = [],
            idResultMap = {},
            remaining = [];
        queryParts.forEach(_.bind(function (part) {
            var partId = part.match(/id:\"(.*)\"/);
            if (partId && partId.length === 2 && this.imageIdMap[partId[1]]) {
                idResults.push(this.imageIdMap[partId[1]]);
                idResultMap[partId[1]] = true;
            }
        }, this));

        // Gather the rest
        this.results.forEach(function (result) {
            if (!idResultMap[result.id]) {
                remaining.push(result);
            }
        });

        // Construct new result list with id results first
        this.results = idResults.concat(remaining);

        this.render();
    },

    render: function () {
        this.$el.html(imagespace.templates.search({
            results: this.results,
            showText: true
        }));
        return this;
    },

    findSimilarImages: function(image) {
        girder.restRequest({
            path: 'imagesearch',
            data: {
                url: image.imageUrl,
                histogram: JSON.stringify(image.histogram || []),
                limit: 100
            }
        }).done(_.bind(function (results) {
            var query = '(', count = 0;
            results.forEach(_.bind(function (result, index) {
                 var parts = result.id.split('/'),
                     file = parts[parts.length - 1];
                 if (file.length < 30) {
                     return;
                 }
                 if (result.id.indexOf('cmuImages') !== -1) {
                     file = 'cmuImages/' + file;
                 }
                 file = '/data/roxyimages/' + file;
                 if (count < 100) {
                    query += 'id:"' + file + '" ';
                    count += 1;
                }
            }, this));
            query += ')';
            imagespace.router.navigate('search/' + encodeURIComponent(query), {trigger: true});
        }, this));
    }

});

imagespace.router.route('search/:query', 'search', function (query) {
    $('.im-search').val(query);
    girder.restRequest({
        path: 'imagesearch',
        data: {
            query: query,
            limit: 100
        }
    }).done(function (results) {
        girder.events.trigger('g:navigateTo', imagespace.views.SearchView, {
            results: results
        });
    });
});
<|endoftext|>


<commit_before>/* global ngDefine: false, console: false */
ngDefine('cockpit.directives', [
  'angular',
  'jquery',
  'bpmn/Bpmn',
  'jquery-overscroll',
  'jquery-mousewheel'
], function(module, angular, $, Bpmn) {
  'use strict';
  /* jshint unused: false */

  function DirectiveController($scope, $element, $attrs, $filter, $q, $window, $compile, Views) {

    var w = angular.element($window);

    $scope.overlayVars = { read: [ 'processData', 'bpmnElement' ] };
    $scope.overlayProviders = Views.getProviders({ component:  $scope.providerComponent });
    var overlay = '

';

    var bpmnElements,
        selection,
        scrollToBpmnElementId;

    var activityHighligtClass = 'activity-highlight';
    var bpmnRenderer = null;
    var zoomLevel = 1;

    $scope.$on('$destroy', function() {
      bpmnRenderer = null;
      $scope.processDiagram = null;
      $scope.overlayProviders = null;
    });

    /*------------------- Rendering of process diagram ---------------------*/

    /**
     * If the process diagram changes, then the diagram will be rendered.
     */
    $scope.$watch('processDiagram', function(newValue) {
      if (newValue && newValue.$loaded !== false) {
        try {
          bpmnElements = newValue.bpmnElements;
          bpmnRenderer = new Bpmn();
          renderDiagram();
          decorateDiagram(bpmnElements);
          initializeScrollAndZoomFunctions();

          // update selection in case it has been provided earlier
          updateSelection(selection);

          // update scroll to in case it has been provided earlier
          scrollToBpmnElement(scrollToBpmnElementId);
        } catch (exception) {
          console.log('Unable to render diagram for process definition ' + $scope.processDiagram.processDefinition.id + ', reason: ' + exception.message);
          $element.html('
Unable to render process diagram.
');
        }
      }
    });

    function renderDiagram() {

      // set the element id to processDiagram_*
      var elementId = 'processDiagram_' + $scope.processDiagram.processDefinition.id.replace(/[.|:]/g, '_');
      $element.attr('id', elementId);

      // clear innerHTML of element in case that the process diagram has changed
      // and the old one has been rendered.
      $element.empty();

      // set the render options
      $element.addClass('process-diagram');
      var options = {
        diagramElement : $element.attr('id')
      };

      // do the rendering
      bpmnRenderer.renderDiagram($scope.processDiagram.semantic, options);
    }

    /*------------------- Decorate diagram ---------------------*/

    function decorateDiagram(bpmnElements) {
      decorateProcessDiagramWithEventHandlers(bpmnElements);

      angular.forEach(bpmnElements, function (bpmnElement) {
        var activityId = bpmnElement.id,
            elem = bpmnRenderer.getOverlay(activityId);

        if (elem) {
          decorateBpmnElementWithOverlays(bpmnElement, elem);
          decorateBpmnElementWithEventHandlers(bpmnElement, elem);
        }

      });
    }

    function decorateProcessDiagramWithEventHandlers(bpmnElements) {

      var moved = false,
          mousedown = false;

      // register event handler on $element: mousedown, mousemove, mouseup
      $element
        // register mousedown event
        .mousedown(function() {
          mousedown = true;

          // indicates the timestamp, when
          // the mousemove event was
          // triggered.
          var mousemoveTimestamp = 0;

          // The mousemove event will be bind to the
          // $element only if a mousedown event happened
          // before.
          $element.mousemove(function($event) {
            var now = $event.timeStamp;
            // mousemoveTimestamp === 0 means,
            // that the mousemove event was triggered
            // but no mousemovement happened (this
            // is a workaround for chrome.)
            if (mousemoveTimestamp !== 0) {
              moved = true;
            }
            lastMouseMovement = now;
          });
        })

        // register mouseup event
        .mouseup(function($event) {
          var targetId = $($event.target).attr('data-activity-id'),
              bpmnElement = bpmnElements[targetId],
              ctrlKey = $event.ctrlKey;

          if (!ctrlKey) {

            if (!moved && mousedown && (!bpmnElement || !bpmnElement.isSelectable)) {
              // if the mouse have not moved, a mousedown happend and the bpmnElement is null
              // or is not selectable, then you have to deselect the current selection.
              if ($scope.onElementClick) {
                $scope.onElementClick({id: null, $event: $event});
                $scope.$apply();
              }
            }
          }

          // unbind the mousemove event
          $element.unbind('mousemove');

          // always reset the values
          moved = false;
          mousedown = false;
        });
    }

    function decorateBpmnElementWithOverlays(bpmnElement, htmlElement) {
      var childScope = $scope.$new();

      childScope.bpmnElement = bpmnElement;

      var newOverlay = angular.element(overlay);

      $compile(newOverlay)(childScope);
      htmlElement.html(newOverlay);
    }

    function decorateBpmnElementWithEventHandlers(bpmnElement, htmlElement) {
      var activityId = bpmnElement.id;
      $(htmlElement)

        // register click
        .click(activityId, function ($event) {
          if (bpmnElement.isSelectable) {
            $scope.onElementClick({
              id: $event.data, $event: $event
            });
            $scope.$apply();
          }
        })

        // mouseover
        .mouseover(activityId, function($event) {
          if (!bpmnElement.isSelected && bpmnElement.isSelectable) {

            // add css class to highlight activity
            bpmnRenderer.annotation($event.data).addClasses([ activityHighligtClass ]);
          }
        })

        // mouseout
        .mouseout(activityId, function($event){
          if (!bpmnElement.isSelected && bpmnElement.isSelectable) {
            // remove css class to highlight activity
            bpmnRenderer.annotation($event.data).removeClasses([ activityHighligtClass ]);
          }
        });
    }

    /*------------------- Handle scroll and zoom ---------------------*/

    $scope.$watch(function() { return zoomLevel; }, function(newZoomLevel) {
      if (!!newZoomLevel && !!bpmnRenderer) {
        zoom(newZoomLevel);
      }
    });

    function initializeScrollAndZoomFunctions() {
      zoom(zoomLevel);

      $element.mousewheel(function($event, delta) {
        $event.preventDefault();
        $scope.$apply(function() {
          zoomLevel = calculateZoomLevel(delta);
        });
      });
    }

    function overscroll() {
      $element.overscroll({captureWheel:false});
    }

    function removeOverscroll() {
      $element.removeOverscroll();
    }

    function zoom(zoomFactor) {
      removeOverscroll();
      bpmnRenderer.zoom(zoomFactor);
      overscroll();
    }

    function calculateZoomLevel (delta) {
      var minZoomLevelMin = 0.1;
      var maxZoomLevelMax = 5;
      var zoomSteps = 10;

      var newZoomLevel = zoomLevel + Math.round((delta * 100)/ zoomSteps) / 100;

      if (newZoomLevel > maxZoomLevelMax) {
        newZoomLevel = maxZoomLevelMax;
      } else if (newZoomLevel < minZoomLevelMin) {
        newZoomLevel = minZoomLevelMin;
      }

      return newZoomLevel;
    }

    /*------------------- Handle window resize ---------------------*/

    w.bind('resize', function () {
      $scope.$apply();
    });

    $scope.$watch(function () {
      return $element.width();
    }, function(newValue, oldValue) {
      if (bpmnRenderer) {
        zoom(zoomLevel);
      }
    });

    $scope.$watch(function () {
      return $element.height();
    }, function(newValue, oldValue) {
      if (bpmnRenderer) {
        zoom(zoomLevel);
      }
    });

    $scope.$on('resize', function () {
      $scope.$apply();
    });

    /*------------------- Handle selected activity id---------------------*/

    $scope.$watch('selection.activityIds', function(newValue, oldValue) {
      updateSelection(newValue);
    });

    function updateSelection(newSelection) {
      if (bpmnElements) {
        if (selection) {
          angular.forEach(selection, function(elementId) {
            var bpmnElement = bpmnElements[elementId];
            deselectActivity(bpmnElement);
          });
        }

        if (newSelection) {
          angular.forEach(newSelection, function(elementId) {
            var bpmnElement = bpmnElements[elementId];
            selectActivity(bpmnElement);
          });
        }
      }

      selection = newSelection;
    }

    function selectActivity(bpmnElement) {
      if (bpmnElement) {
        bpmnElement.isSelected = true;
        try {
          bpmnRenderer.annotation(bpmnElement.id).addClasses([ activityHighligtClass ]);
        } catch (error) {
          console.log('Could not add css class ' + activityHighligtClass + ' on element ' + bpmnElement.id + ': ' + error.message);
        }
      }
    }

    function deselectActivity(bpmnElement) {
      if (bpmnElement) {
        bpmnElement.isSelected = false;
        try {
          bpmnRenderer.annotation(bpmnElement.id).removeClasses([ activityHighligtClass ]);
        } catch (error) {
          console.log('Could not remove css class ' + activityHighligtClass + ' on element ' + bpmnElement.id + ': ' + error.message);
        }
      }
    }

    /*------------------- Handle scroll to bpmn element ---------------------*/

    $scope.$watch('selection.scrollToBpmnElement', function(newValue) {
      if (newValue) {
        scrollToBpmnElement(newValue);
      }
    });

    function scrollToBpmnElement(bpmnElementId) {
      if (bpmnElements) {
        var bpmnElement = bpmnElements[bpmnElementId];
        if (bpmnElement) {
          scrollTo(bpmnElement);
        }
      }
      scrollToBpmnElementId = bpmnElementId;
    }

    function scrollTo(element) {
      // parent size
      var parentElementHeight = $element.height();
      var parentElementWidth = $element.width();

      // get the bpmn element to scroll to
      var bpmnElement = bpmnRenderer.getOverlay(element.id);

      // get the height and width of the bpmn element
      var bpmnElementHeight = bpmnElement.height();
      var bpmnElementWidth = bpmnElement.width();

      // get the top and left position of the bpmn element
      var bpmnElementTop = parseInt(bpmnElement.css('top'));
      var bpmnElementLeft = parseInt(bpmnElement.css('left'));

      var scrollTop = (bpmnElementTop +  (bpmnElementHeight/2)) - parentElementHeight/2;
      var scrollLeft = (bpmnElementLeft +  (bpmnElementWidth/2)) - parentElementWidth/2;

      $element.animate({
        scrollTop: scrollTop,
        scrollLeft: scrollLeft
      });
    }

    this.getRenderer = function () {
      return bpmnRenderer;
    };

  }

  var Directive = function ($window, $compile, Views) {
    return {
      restrict: 'EAC',
      scope: {
        processDiagram: '=',
        processDiagramOverlay: '=',
        onElementClick: '&',
        selection: '=',
        processData: '=',
        providerComponent: '@'
      },
      controller: DirectiveController
    };
  };

  Directive.$inject = [ '$window', '$compile', 'Views'];

  module
    .directive('processDiagram', Directive);

});
<commit_msg>fix(cockpit/diagram): fix compile failure in directive<commit_after>/* global ngDefine: false, console: false */
ngDefine('cockpit.directives', [
  'angular',
  'jquery',
  'bpmn/Bpmn',
  'jquery-overscroll',
  'jquery-mousewheel'
], function(module, angular, $, Bpmn) {
  'use strict';
  /* jshint unused: false */

  function DirectiveController($scope, $element, $attrs, $filter, $q, $window, $compile, Views) {

    var w = angular.element($window);

    $scope.overlayVars = { read: [ 'processData', 'bpmnElement' ] };
    $scope.overlayProviders = Views.getProviders({ component:  $scope.providerComponent });
    var overlay = '

';

    var bpmnElements,
        selection,
        scrollToBpmnElementId;

    var activityHighligtClass = 'activity-highlight';
    var bpmnRenderer = null;
    var zoomLevel = 1;

    $scope.$on('$destroy', function() {
      bpmnRenderer = null;
      $scope.processDiagram = null;
      $scope.overlayProviders = null;
    });

    /*------------------- Rendering of process diagram ---------------------*/

    /**
     * If the process diagram changes, then the diagram will be rendered.
     */
    $scope.$watch('processDiagram', function(newValue) {
      if (newValue && newValue.$loaded !== false) {
        try {
          bpmnElements = newValue.bpmnElements;
          bpmnRenderer = new Bpmn();
          renderDiagram();
          decorateDiagram(bpmnElements);
          initializeScrollAndZoomFunctions();

          // update selection in case it has been provided earlier
          updateSelection(selection);

          // update scroll to in case it has been provided earlier
          scrollToBpmnElement(scrollToBpmnElementId);
        } catch (exception) {
          console.log('Unable to render diagram for process definition ' + $scope.processDiagram.processDefinition.id + ', reason: ' + exception.message);
          $element.html('
Unable to render process diagram.
');
        }
      }
    });

    function renderDiagram() {

      // set the element id to processDiagram_*
      var elementId = 'processDiagram_' + $scope.processDiagram.processDefinition.id.replace(/[.|:]/g, '_');
      $element.attr('id', elementId);

      // clear innerHTML of element in case that the process diagram has changed
      // and the old one has been rendered.
      $element.empty();

      // set the render options
      $element.addClass('process-diagram');
      var options = {
        diagramElement : $element.attr('id')
      };

      // do the rendering
      bpmnRenderer.renderDiagram($scope.processDiagram.semantic, options);
    }

    /*------------------- Decorate diagram ---------------------*/

    function decorateDiagram(bpmnElements) {
      decorateProcessDiagramWithEventHandlers(bpmnElements);

      angular.forEach(bpmnElements, function (bpmnElement) {
        var activityId = bpmnElement.id,
            elem = bpmnRenderer.getOverlay(activityId);

        if (elem) {
          decorateBpmnElementWithOverlays(bpmnElement, elem);
          decorateBpmnElementWithEventHandlers(bpmnElement, elem);
        }

      });
    }

    function decorateProcessDiagramWithEventHandlers(bpmnElements) {

      var moved = false,
          mousedown = false;

      // register event handler on $element: mousedown, mousemove, mouseup
      $element
        // register mousedown event
        .mousedown(function() {
          mousedown = true;

          // indicates the timestamp, when
          // the mousemove event was
          // triggered.
          var mousemoveTimestamp = 0;

          // The mousemove event will be bind to the
          // $element only if a mousedown event happened
          // before.
          $element.mousemove(function($event) {
            var now = $event.timeStamp;
            // mousemoveTimestamp === 0 means,
            // that the mousemove event was triggered
            // but no mousemovement happened (this
            // is a workaround for chrome.)
            if (mousemoveTimestamp !== 0) {
              moved = true;
            }
            mousemoveTimestamp = now;
          });
        })

        // register mouseup event
        .mouseup(function($event) {
          var targetId = $($event.target).attr('data-activity-id'),
              bpmnElement = bpmnElements[targetId],
              ctrlKey = $event.ctrlKey;

          if (!ctrlKey) {

            if (!moved && mousedown && (!bpmnElement || !bpmnElement.isSelectable)) {
              // if the mouse have not moved, a mousedown happend and the bpmnElement is null
              // or is not selectable, then you have to deselect the current selection.
              if ($scope.onElementClick) {
                $scope.onElementClick({id: null, $event: $event});
                $scope.$apply();
              }
            }
          }

          // unbind the mousemove event
          $element.unbind('mousemove');

          // always reset the values
          moved = false;
          mousedown = false;
        });
    }

    function decorateBpmnElementWithOverlays(bpmnElement, htmlElement) {
      var childScope = $scope.$new();

      childScope.bpmnElement = bpmnElement;

      var newOverlay = angular.element(overlay);

      $compile(newOverlay)(childScope);
      htmlElement.html(newOverlay);
    }

    function decorateBpmnElementWithEventHandlers(bpmnElement, htmlElement) {
      var activityId = bpmnElement.id;
      $(htmlElement)

        // register click
        .click(activityId, function ($event) {
          if (bpmnElement.isSelectable) {
            $scope.onElementClick({
              id: $event.data, $event: $event
            });
            $scope.$apply();
          }
        })

        // mouseover
        .mouseover(activityId, function($event) {
          if (!bpmnElement.isSelected && bpmnElement.isSelectable) {

            // add css class to highlight activity
            bpmnRenderer.annotation($event.data).addClasses([ activityHighligtClass ]);
          }
        })

        // mouseout
        .mouseout(activityId, function($event){
          if (!bpmnElement.isSelected && bpmnElement.isSelectable) {
            // remove css class to highlight activity
            bpmnRenderer.annotation($event.data).removeClasses([ activityHighligtClass ]);
          }
        });
    }

    /*------------------- Handle scroll and zoom ---------------------*/

    $scope.$watch(function() { return zoomLevel; }, function(newZoomLevel) {
      if (!!newZoomLevel && !!bpmnRenderer) {
        zoom(newZoomLevel);
      }
    });

    function initializeScrollAndZoomFunctions() {
      zoom(zoomLevel);

      $element.mousewheel(function($event, delta) {
        $event.preventDefault();
        $scope.$apply(function() {
          zoomLevel = calculateZoomLevel(delta);
        });
      });
    }

    function overscroll() {
      $element.overscroll({captureWheel:false});
    }

    function removeOverscroll() {
      $element.removeOverscroll();
    }

    function zoom(zoomFactor) {
      removeOverscroll();
      bpmnRenderer.zoom(zoomFactor);
      overscroll();
    }

    function calculateZoomLevel (delta) {
      var minZoomLevelMin = 0.1;
      var maxZoomLevelMax = 5;
      var zoomSteps = 10;

      var newZoomLevel = zoomLevel + Math.round((delta * 100)/ zoomSteps) / 100;

      if (newZoomLevel > maxZoomLevelMax) {
        newZoomLevel = maxZoomLevelMax;
      } else if (newZoomLevel < minZoomLevelMin) {
        newZoomLevel = minZoomLevelMin;
      }

      return newZoomLevel;
    }

    /*------------------- Handle window resize ---------------------*/

    w.bind('resize', function () {
      $scope.$apply();
    });

    $scope.$watch(function () {
      return $element.width();
    }, function(newValue, oldValue) {
      if (bpmnRenderer) {
        zoom(zoomLevel);
      }
    });

    $scope.$watch(function () {
      return $element.height();
    }, function(newValue, oldValue) {
      if (bpmnRenderer) {
        zoom(zoomLevel);
      }
    });

    $scope.$on('resize', function () {
      $scope.$apply();
    });

    /*------------------- Handle selected activity id---------------------*/

    $scope.$watch('selection.activityIds', function(newValue, oldValue) {
      updateSelection(newValue);
    });

    function updateSelection(newSelection) {
      if (bpmnElements) {
        if (selection) {
          angular.forEach(selection, function(elementId) {
            var bpmnElement = bpmnElements[elementId];
            deselectActivity(bpmnElement);
          });
        }

        if (newSelection) {
          angular.forEach(newSelection, function(elementId) {
            var bpmnElement = bpmnElements[elementId];
            selectActivity(bpmnElement);
          });
        }
      }

      selection = newSelection;
    }

    function selectActivity(bpmnElement) {
      if (bpmnElement) {
        bpmnElement.isSelected = true;
        try {
          bpmnRenderer.annotation(bpmnElement.id).addClasses([ activityHighligtClass ]);
        } catch (error) {
          console.log('Could not add css class ' + activityHighligtClass + ' on element ' + bpmnElement.id + ': ' + error.message);
        }
      }
    }

    function deselectActivity(bpmnElement) {
      if (bpmnElement) {
        bpmnElement.isSelected = false;
        try {
          bpmnRenderer.annotation(bpmnElement.id).removeClasses([ activityHighligtClass ]);
        } catch (error) {
          console.log('Could not remove css class ' + activityHighligtClass + ' on element ' + bpmnElement.id + ': ' + error.message);
        }
      }
    }

    /*------------------- Handle scroll to bpmn element ---------------------*/

    $scope.$watch('selection.scrollToBpmnElement', function(newValue) {
      if (newValue) {
        scrollToBpmnElement(newValue);
      }
    });

    function scrollToBpmnElement(bpmnElementId) {
      if (bpmnElements) {
        var bpmnElement = bpmnElements[bpmnElementId];
        if (bpmnElement) {
          scrollTo(bpmnElement);
        }
      }
      scrollToBpmnElementId = bpmnElementId;
    }

    function scrollTo(element) {
      // parent size
      var parentElementHeight = $element.height();
      var parentElementWidth = $element.width();

      // get the bpmn element to scroll to
      var bpmnElement = bpmnRenderer.getOverlay(element.id);

      // get the height and width of the bpmn element
      var bpmnElementHeight = bpmnElement.height();
      var bpmnElementWidth = bpmnElement.width();

      // get the top and left position of the bpmn element
      var bpmnElementTop = parseInt(bpmnElement.css('top'));
      var bpmnElementLeft = parseInt(bpmnElement.css('left'));

      var scrollTop = (bpmnElementTop +  (bpmnElementHeight/2)) - parentElementHeight/2;
      var scrollLeft = (bpmnElementLeft +  (bpmnElementWidth/2)) - parentElementWidth/2;

      $element.animate({
        scrollTop: scrollTop,
        scrollLeft: scrollLeft
      });
    }

    this.getRenderer = function () {
      return bpmnRenderer;
    };

  }

  var Directive = function ($window, $compile, Views) {
    return {
      restrict: 'EAC',
      scope: {
        processDiagram: '=',
        processDiagramOverlay: '=',
        onElementClick: '&',
        selection: '=',
        processData: '=',
        providerComponent: '@'
      },
      controller: DirectiveController
    };
  };

  Directive.$inject = [ '$window', '$compile', 'Views'];

  module
    .directive('processDiagram', Directive);

});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// TODO: support 'precision' in FloatView
foam.CLASS({
  package: 'com.google.foam.demos.sevenguis',
  name: 'TempConv',
  extends: 'foam.u2.Element',

  exports: [ 'as data' ],

  properties: [
    { class: 'Float', name: 'c', view: { class: 'foam.u2.TextField', onKey: true, precision: 4 } },
    { class: 'Float', name: 'f', view: { class: 'foam.u2.TextField', onKey: true, precision: 4 } }
  ],

  methods: [
    function initE() {
      this.nodeName = 'span';
      this.c$.relateTo(this.f$, this.c2f, this.f2c);
      this.add(this.C, ' Celsius = ', this.F, ' Fahrenheit');
    },
    function c2f(f) { return 9/5 * f + 32; },
    function f2c(c) { return 5/9 * ( c - 32 ); }
  ]
});
<commit_msg>Switch TempConv demo to use new FloatView.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'com.google.foam.demos.sevenguis',
  name: 'TempConv',
  extends: 'foam.u2.Element',

  exports: [ 'as data' ],

  properties: [
    [ 'nodeName', 'span' ],
    { class: 'Float', name: 'c', view: { class: 'foam.u2.FloatView', onKey: true, precision: 4 } },
    { class: 'Float', name: 'f', view: { class: 'foam.u2.FloatView', onKey: true, precision: 4 } }
  ],

  methods: [
    function initE() {
      this.c$.relateTo(this.f$, this.c2f, this.f2c);
      this.add(this.C, ' Celsius = ', this.F, ' Fahrenheit');
    },
    function c2f(f) { return 9/5 * f + 32; },
    function f2c(c) { return 5/9 * ( c - 32 ); }
  ]
});
<|endoftext|>


<commit_before>// SAGE2 is available for use under the SAGE2 Software License
//
// University of Illinois at Chicago's Electronic Visualization Laboratory (EVL)
// and University of Hawai'i at Manoa's Laboratory for Advanced Visualization and
// Applications (LAVA)
//
// See full text, terms and conditions in the LICENSE.txt included file
//
// Copyright (c) 2014

function addCSS(url, callback) {
	var fileref = document.createElement("link");

	if (callback) fileref.onload = callback;

	fileref.setAttribute("rel", "stylesheet");
	fileref.setAttribute("type", "text/css");
	fileref.setAttribute("href", url);
	document.head.appendChild(fileref);
}

var osm3d = SAGE2_App.extend({
	construct: function () {
		arguments.callee.superClass.construct.call(this);

		this.resizeEvents = "continuous"; //"onfinish";

		// Need to set this to true in order to tell SAGE2 that you will be needing widget controls for this app
		this.enableControls = true;

		// for SAGE2 interaction
		this.lastZoom = null;
		this.dragging = null;
		this.position = null;

		this.map = null;
		this.layer1 = null;

		this.g = null;

		this.allLoaded = 0;
	},

	init: function (id, width, height, resrc, date) {

		// call super-class 'init'
		arguments.callee.superClass.init.call(this, id, "div", width, height, resrc, date);

		var map_start_location = {
			'London': [51.508, -0.105, 15],
			'New York': [40.70531887544228, -74.00976419448853, 15],
			'Seattle': [47.609722, -122.333056, 15]
		}['New York'];

		console.log(map_start_location);

		// Get width height from the supporting div
		var width = this.element.clientWidth;
		var height = this.element.clientHeight;

		this.element.id = "div" + id;

		// for SAGE2
		this.lastZoom = date;
		this.dragging = false;
		this.position = {x: 0, y: 0};

		// Load the CSS file for leaflet.js
		var that = this;
		addCSS(this.resrcPath + "lib/leaflet/leaflet.css", function () {

			that.layer1 = Tangram.leafletLayer({
				scene: that.resrcPath + 'styles.yaml',
				attribution: '© OpenStreetMap | webgl by Mapbox Tangrams'
			});

			//that.layer1 = L.tileLayer('http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', {
			//	maxZoom: 18
			//});

			that.map = L.map(that.element.id, {
				layers: [that.layer1],
				zoomControl: false
			});

			that.map.setView(map_start_location.slice(0, 2), map_start_location[2]);

		});
	},

	zoomIn: function () {
		var z = this.map.getZoom();
		this.map.setZoom(z + 1, {animate: false});
		this.lastZoom = date;

		var z2 = this.map.getZoom();
	},

	zoomOut: function () {
		var z = this.map.getZoom();
		this.map.setZoom(z - 1, {animate: false});
		this.lastZoom = date;

		var z2 = this.map.getZoom();
	},

	load: function (state, date) {
		// create the widgets
		console.log("creating controls");
		this.controls.addButton({
			type: "next", sequenceNo: 4, action: function (date) {
				//This is executed after the button click animation occurs.
				this.changeMap();
			}.bind(this)
		});

		this.controls.addButton({
			type: "fastforward", sequenceNo: 6, action: function (date) {
				this.zoomIn();
			}.bind(this)
		});

		this.controls.addButton({
			type: "rewind", sequenceNo: 7, action: function (date) {
				//This is executed after the button click animation occurs.
				this.zoomOut();
			}.bind(this)
		});

		this.controls.finishedAddingControls(); // Important
	},

	draw: function (date) {
	},

	resize: function (date) {
		this.map.invalidateSize();
		this.refresh(date);
	},

	event: function (eventType, pos, user, data, date) {

		if (eventType === "pointerPress" && (data.button === "left")) {
			this.dragging = true;
			this.position.x = pos.x;
			this.position.y = pos.y;
		}
		if (eventType === "pointerMove" && this.dragging) {
			// need to turn animation off here or the pan stutters
			this.map.panBy([this.position.x - pos.x, this.position.y - pos.y], {animate: false});
			this.position.x = pos.x;
			this.position.y = pos.y;
		}
		if (eventType === "pointerRelease" && (data.button === "left")) {
			this.dragging = false;
			this.position.x = pos.x;
			this.position.y = pos.y;
		}

		// Scroll events for zoom
		if (eventType === "pointerScroll") {
			var amount = data.wheelDelta;
			var diff = date - this.lastZoom;

			console.log(data);

			if (amount >= 3 && (diff > 300)) {
				// zoom in
				this.zoomIn();

				//this.log("scroll: " + amount + ", diff: " + diff + ", zoom: " + z + "(" + z2 + ")");
			}
			else if (amount <= -3 && (diff > 300)) {
				// zoom out
				this.zoomOut();

				//this.log("scroll: " + amount + ", diff: " + diff + ", zoom: " + z + "(" + z2 + ")");
			}
		}

		this.refresh(date);
	}

});<commit_msg>Fixing compatibilty issue for sage2 2015-03 #2<commit_after>// SAGE2 is available for use under the SAGE2 Software License
//
// University of Illinois at Chicago's Electronic Visualization Laboratory (EVL)
// and University of Hawai'i at Manoa's Laboratory for Advanced Visualization and
// Applications (LAVA)
//
// See full text, terms and conditions in the LICENSE.txt included file
//
// Copyright (c) 2014

function addCSS(url, callback) {
	var fileref = document.createElement("link");

	if (callback) fileref.onload = callback;

	fileref.setAttribute("rel", "stylesheet");
	fileref.setAttribute("type", "text/css");
	fileref.setAttribute("href", url);
	document.head.appendChild(fileref);
}

var osm3d = SAGE2_App.extend({
	construct: function () {
		arguments.callee.superClass.construct.call(this);

		this.resizeEvents = "continuous"; //"onfinish";

		// Need to set this to true in order to tell SAGE2 that you will be needing widget controls for this app
		this.enableControls = true;

		// for SAGE2 interaction
		this.lastZoom = null;
		this.dragging = null;
		this.position = null;

		this.map = null;
		this.layer1 = null;

		this.g = null;

		this.allLoaded = 0;
	},

	init: function(data) {

		// call super-class 'init'
		arguments.callee.superClass.init.call(this, "div", data);

		var map_start_location = {
			'London': [51.508, -0.105, 15],
			'New York': [40.70531887544228, -74.00976419448853, 15],
			'Seattle': [47.609722, -122.333056, 15]
		}['New York'];

		console.log(map_start_location);

		// Get width height from the supporting div
		var width = this.element.clientWidth;
		var height = this.element.clientHeight;

		this.element.id = "div" + data.id;

		// for SAGE2
		this.lastZoom = data.date;
		this.dragging = false;
		this.position = {x: 0, y: 0};

		// Load the CSS file for leaflet.js
		var that = this;
		addCSS(this.resrcPath + "lib/leaflet/leaflet.css", function () {

			that.layer1 = Tangram.leafletLayer({
				scene: that.resrcPath + 'styles.yaml',
				attribution: '© OpenStreetMap | webgl by Mapbox Tangrams'
			});

			//that.layer1 = L.tileLayer('http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', {
			//	maxZoom: 18
			//});

			that.map = L.map(that.element.id, {
				layers: [that.layer1],
				zoomControl: false
			});

			that.map.setView(map_start_location.slice(0, 2), map_start_location[2]);

		});
	},

	zoomIn: function () {
		var z = this.map.getZoom();
		this.map.setZoom(z + 1, {animate: false});
		this.lastZoom = date;

		var z2 = this.map.getZoom();
	},

	zoomOut: function () {
		var z = this.map.getZoom();
		this.map.setZoom(z - 1, {animate: false});
		this.lastZoom = date;

		var z2 = this.map.getZoom();
	},

	load: function (state, date) {
		// create the widgets
		console.log("creating controls");
		this.controls.addButton({
			type: "next", sequenceNo: 4, action: function (date) {
				//This is executed after the button click animation occurs.
				this.changeMap();
			}.bind(this)
		});

		this.controls.addButton({
			type: "fastforward", sequenceNo: 6, action: function (date) {
				this.zoomIn();
			}.bind(this)
		});

		this.controls.addButton({
			type: "rewind", sequenceNo: 7, action: function (date) {
				//This is executed after the button click animation occurs.
				this.zoomOut();
			}.bind(this)
		});

		this.controls.finishedAddingControls(); // Important
	},

	draw: function (date) {
	},

	resize: function (date) {
		this.map.invalidateSize();
		this.refresh(date);
	},

	event: function (eventType, pos, user, data, date) {

		if (eventType === "pointerPress" && (data.button === "left")) {
			this.dragging = true;
			this.position.x = pos.x;
			this.position.y = pos.y;
		}
		if (eventType === "pointerMove" && this.dragging) {
			// need to turn animation off here or the pan stutters
			this.map.panBy([this.position.x - pos.x, this.position.y - pos.y], {animate: false});
			this.position.x = pos.x;
			this.position.y = pos.y;
		}
		if (eventType === "pointerRelease" && (data.button === "left")) {
			this.dragging = false;
			this.position.x = pos.x;
			this.position.y = pos.y;
		}

		// Scroll events for zoom
		if (eventType === "pointerScroll") {
			var amount = data.wheelDelta;
			var diff = date - this.lastZoom;

			console.log(data);

			if (amount >= 3 && (diff > 300)) {
				// zoom in
				this.zoomIn();

				//this.log("scroll: " + amount + ", diff: " + diff + ", zoom: " + z + "(" + z2 + ")");
			}
			else if (amount <= -3 && (diff > 300)) {
				// zoom out
				this.zoomOut();

				//this.log("scroll: " + amount + ", diff: " + diff + ", zoom: " + z + "(" + z2 + ")");
			}
		}

		this.refresh(date);
	}

});<|endoftext|>


<commit_before>'use strict';

const expect = require('chai').expect;
const sinon = require('sinon');
const compareVersions = require('../../src/compare-versions');
const utils = require('../../src/utils');

describe('Unit - compareVersions', function() {
  let sandbox;
  let opn;

  beforeEach(function() {
    sandbox = sinon.sandbox.create();

    opn = sandbox.stub(utils, 'opn');
  });

  it('works', function() {
    return compareVersions({
      remoteUrl: 'test-url',
      startTag: 'v2.18.2',
      endTag: 'v3.0.2'
    }).then(() => {
      expect(opn.calledOnce).to.be.ok;
      expect(opn.args[0][0]).to.equal('test-url/compare/v2.18.2...v3.0.2');
    });
  });
});
<commit_msg>forgot sandbox.restore<commit_after>'use strict';

const expect = require('chai').expect;
const sinon = require('sinon');
const compareVersions = require('../../src/compare-versions');
const utils = require('../../src/utils');

describe('Unit - compareVersions', function() {
  let sandbox;
  let opn;

  beforeEach(function() {
    sandbox = sinon.sandbox.create();

    opn = sandbox.stub(utils, 'opn');
  });

  afterEach(function() {
    sandbox.restore();
  });

  it('works', function() {
    return compareVersions({
      remoteUrl: 'test-url',
      startTag: 'v2.18.2',
      endTag: 'v3.0.2'
    }).then(() => {
      expect(opn.calledOnce).to.be.ok;
      expect(opn.args[0][0]).to.equal('test-url/compare/v2.18.2...v3.0.2');
    });
  });
});
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
"use strict";
/*global _GPF_EVENT_DATA*/ // gpf.events.EVENT_DATA
/*global _GPF_EVENT_END_OF_DATA*/ // gpf.events.EVENT_END_OF_DATA
/*global _GPF_EVENT_ERROR*/ // gpf.events.EVENT_ERROR
/*global _GPF_EVENT_READY*/ // gpf.events.EVENT_READY
/*global _gpfAssert*/ // Assertion method
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEventsFire*/ // gpf.events.fire (internal, parameters must match)
/*global _gpfI*/ // gpf.interfaces
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfInNode*/ // The current host is a nodeJS like
/*exported _GpfNodeStream*/
/*#endif*/

if (_gpfInNode) {

    /**
     * Base class of GPF node streams
     */
    var _GpfNodeStream = _gpfDefine("gpf.node.Stream", Object, {

        public: {

            /**
             * @param {Object} stream
             * @constructor
             */
            constructor: function (stream) {
                this._stream = stream;
            },

            /**
             * Close the current stream
             *
             * @param {gpf.events.Handler} eventsHandler
             *
             * @event gpf.events.EVENT_READY
             */
            close: function (eventsHandler) {
                _gpfIgnore(eventsHandler);
                throw gpf.Error.abstract();
            }

        },

        protected: {

            /**
             * @type {Object}
             * @private
             */
            _stream: null

        }

    });

    /**
     * Wraps a readable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeReadable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.ReadableStream", _GpfNodeStream, {

        "[Class]": [gpf.$InterfaceImplement(_gpfI.IReadableStream)],

        public: {

            /**
             * @param {stream.Readable} stream
             * @constructor
             */
            constructor: function (stream) {
                this._super(stream);
                stream.on("end", this._onEnd.bind(this));
                stream.on("error", this._onError.bind(this));
            },

            /**
             * @inheritdoc gpf.node.Stream#close
             */
            close: function (eventsHandler) {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_READY,
                    {},
                    eventsHandler
                ]);
            },

            /**
             * @inheritdoc gpf.interfaces.IReadableStream#read
             */
            "[read]": [gpf.$ClassEventHandler()],
            read: function (size, eventsHandler) {
                var chunk;
                // Used as a critical section to prevent concurrent reads
                if (null !== this._eventsHandler) {
                    throw gpf.Error.ReadInProgress();
                }
                this._eventsHandler = eventsHandler;
                // If we received the "readable" event
                if (this._readable) {
                    // We try to read a chunk
                    chunk = this._stream.read(size);
                    if (chunk) {
                        this._onData(chunk);
                        return;
                    }
                    // No chunk means we must wait for next "readable"
                    this._readable = false;
                }
                this._size = size;
                this._stream.once("readable", this._onReadable.bind(this));
            }

        },

        protected: {

            /**
             * Before doing any read on the stream, we wait for the readable
             * event to be thrown
             *
             * @type {Boolean}
             * @protected
             */
            _readable: false,

            /**
             * Last read eventsHandler
             * Also used as a critical section to prevent concurrent reads
             *
             * @type {gpf.events.Handler}
             * @protected
             */
            _eventsHandler: null,

            /**
             * Last read size (if pending)
             *
             * @type {Number}
             * @protected
             */
            _size: 0,

            /**
             * Provides an atomic access to the _eventsHandler variable
             * (that is immediately cleared)
             *
             * @return {gpf.events.Handler}
             * @private
             */
            _getEventsHandler: function () {
                var result = this._eventsHandler;
                _gpfAssert(null !== result, "Event handler expected");
                this._eventsHandler = null;
                return result;
            },

            /**
             * Handles "readable" stream event
             * NOTE that it was registered with once
             *
             * @private
             */
            _onReadable: function() {
                this._readable = true;
                this._onData(this._stream.read(this._size));
            },

            /**
             * Handles "data" stream event
             *
             * @param {Buffer} chunk
             * @private
             */
            _onData: function(chunk) {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_DATA,
                    {
                        buffer: gpf.node.buffer2JsArray(chunk)
                    },
                    this._getEventsHandler()
                ]);
            },

            _onEnd: function () {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_END_OF_DATA,
                    {},
                    this._getEventsHandler()
                ]);
            },

            _onError: function (error) {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_ERROR,
                    {
                        error: error
                    },
                    this._getEventsHandler()
                ]);
            }

        }

    });

    /**
     * Wraps a writable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeWritable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.WritableStream", _GpfNodeStream, {

        "[Class]": [gpf.$InterfaceImplement(_gpfI.IWritableStream)],

        public: {

            /**
             * @param {stream.Writable} stream
             * @constructor
             */
            constructor: function (stream) {
                this._super(stream);
            },

            /**
             * @inheritdoc gpf.node.Stream#close
             */
            close: function (eventsHandler) {
                this._stream.end(function () {
                    _gpfEventsFire.apply(this, [
                        _GPF_EVENT_READY,
                        {},
                        eventsHandler
                    ]);
                });
            },

            /**
             * @inheritdoc gpf.interfaces.IWritableStream#write
             */
            "[write]": [gpf.$ClassEventHandler()],
            write: function (buffer, eventsHandler) {
                _gpfIgnore(buffer, eventsHandler);
                throw gpf.Error.notImplemented();
            }

        }

    });

}
<commit_msg>ESLint fixes<commit_after>/*#ifndef(UMD)*/
"use strict";
/*global _GPF_EVENT_DATA*/ // gpf.events.EVENT_DATA
/*global _GPF_EVENT_END_OF_DATA*/ // gpf.events.EVENT_END_OF_DATA
/*global _GPF_EVENT_ERROR*/ // gpf.events.EVENT_ERROR
/*global _GPF_EVENT_READY*/ // gpf.events.EVENT_READY
/*global _gpfAssert*/ // Assertion method
/*global _gpfDefine*/ // Shortcut for gpf.define
/*global _gpfEventsFire*/ // gpf.events.fire (internal, parameters must match)
/*global _gpfI*/ // gpf.interfaces
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*global _gpfInNode*/ // The current host is a nodeJS like
/*exported _GpfNodeStream*/
/*#endif*/

if (_gpfInNode) {

    /**
     * Base class of GPF node streams
     */
    var _GpfNodeStream = _gpfDefine("gpf.node.Stream", Object, {

        "+": {

            /**
             * @param {Object} stream
             * @constructor
             */
            constructor: function (stream) {
                this._stream = stream;
            },

            /**
             * Close the current stream
             *
             * @param {gpf.events.Handler} eventsHandler
             *
             * @event gpf.events.EVENT_READY
             */
            close: function (eventsHandler) {
                _gpfIgnore(eventsHandler);
                throw gpf.Error.abstract();
            }

        },

        "#": {

            /**
             * @type {Object}
             * @private
             */
            _stream: null

        }

    });

    /**
     * Wraps a readable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeReadable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.ReadableStream", _GpfNodeStream, {

        "[Class]": [gpf.$InterfaceImplement(_gpfI.IReadableStream)],

        "+": {

            /**
             * @param {stream.Readable} stream
             * @constructor
             */
            constructor: function (stream) {
                this._super(stream);
                stream.on("end", this._onEnd.bind(this));
                stream.on("error", this._onError.bind(this));
            },

            /**
             * @inheritdoc gpf.node.Stream#close
             */
            close: function (eventsHandler) {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_READY,
                    {},
                    eventsHandler
                ]);
            },

            /**
             * @inheritdoc gpf.interfaces.IReadableStream#read
             */
            "[read]": [gpf.$ClassEventHandler()],
            read: function (size, eventsHandler) {
                var chunk;
                // Used as a critical section to prevent concurrent reads
                if (null !== this._eventsHandler) {
                    throw gpf.Error.ReadInProgress();
                }
                this._eventsHandler = eventsHandler;
                // If we received the "readable" event
                if (this._readable) {
                    // We try to read a chunk
                    chunk = this._stream.read(size);
                    if (chunk) {
                        this._onData(chunk);
                        return;
                    }
                    // No chunk means we must wait for next "readable"
                    this._readable = false;
                }
                this._size = size;
                this._stream.once("readable", this._onReadable.bind(this));
            }

        },

        "#": {

            /**
             * Before doing any read on the stream, we wait for the readable
             * event to be thrown
             *
             * @type {Boolean}
             * @protected
             */
            _readable: false,

            /**
             * Last read eventsHandler
             * Also used as a critical section to prevent concurrent reads
             *
             * @type {gpf.events.Handler}
             * @protected
             */
            _eventsHandler: null,

            /**
             * Last read size (if pending)
             *
             * @type {Number}
             * @protected
             */
            _size: 0,

            /**
             * Provides an atomic access to the _eventsHandler variable
             * (that is immediately cleared)
             *
             * @return {gpf.events.Handler}
             * @private
             */
            _getEventsHandler: function () {
                var result = this._eventsHandler;
                _gpfAssert(null !== result, "Event handler expected");
                this._eventsHandler = null;
                return result;
            },

            /**
             * Handles "readable" stream event
             * NOTE that it was registered with once
             *
             * @private
             */
            _onReadable: function() {
                this._readable = true;
                this._onData(this._stream.read(this._size));
            },

            /**
             * Handles "data" stream event
             *
             * @param {Buffer} chunk
             * @private
             */
            _onData: function(chunk) {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_DATA,
                    {
                        buffer: gpf.node.buffer2JsArray(chunk)
                    },
                    this._getEventsHandler()
                ]);
            },

            _onEnd: function () {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_END_OF_DATA,
                    {},
                    this._getEventsHandler()
                ]);
            },

            _onError: function (error) {
                _gpfEventsFire.apply(this, [
                    _GPF_EVENT_ERROR,
                    {
                        error: error
                    },
                    this._getEventsHandler()
                ]);
            }

        }

    });

    /**
     * Wraps a writable stream from NodeJS into a IReadableStream
     *
     * @class gpf.stream.NodeWritable
     * @extends {gpf.node.Stream}
     * @implements gpf.interfaces.IReadableStream
     */
    _gpfDefine("gpf.node.WritableStream", _GpfNodeStream, {

        "[Class]": [gpf.$InterfaceImplement(_gpfI.IWritableStream)],

        "+": {

            /**
             * @param {stream.Writable} stream
             * @constructor
             */
            constructor: function (stream) {
                this._super(stream);
            },

            /**
             * @inheritdoc gpf.node.Stream#close
             */
            close: function (eventsHandler) {
                this._stream.end(function () {
                    _gpfEventsFire.apply(this, [
                        _GPF_EVENT_READY,
                        {},
                        eventsHandler
                    ]);
                });
            },

            /**
             * @inheritdoc gpf.interfaces.IWritableStream#write
             */
            "[write]": [gpf.$ClassEventHandler()],
            write: function (buffer, eventsHandler) {
                _gpfIgnore(buffer, eventsHandler);
                throw gpf.Error.notImplemented();
            }

        }

    });

}
<|endoftext|>


<commit_before>require("babel-polyfill")
import React, { Component } from 'react'
import { observer } from "mobx-react"
import TodoStore from '../../stores/todo_store'
import { todoForm, todoFormSet, todoFormGet } from '../../forms/todo_form'
import { pageUpdateRecord, pageAddRecord } from '../../actions/page_actions'
import { popupClose } from '../../actions/popup_actions'
import { dispatch } from '../../helpers/dispatcher'
import ReactDOM from 'react-dom'

@observer
export default class TodoView extends Component {
  async handleSend(form) {
    await form.validate();
    if (form.valid) {
      var todo = this.props.todo
      todoFormGet(todo, form.fields)
      if (todo._id) {
        dispatch(pageUpdateRecord(this.store, todo))
      } else {
        dispatch(pageAddRecord(this.store, todo))
      }
      dispatch(popupClose())
    }
  }

  componentWillMount() {
    this.form = this.props.form ? this.props.form : todoForm
    todoFormSet(this.props.todo, this.form.fields)
    this.store = this.props.store
  }

  componentDidMount() {
    this.refs.nameInput.focus();
  }

  render() {
    const form = this.form
    return (

Todo

        <fieldset className="form-group">
          Description:
          { form.renderError(form.fields.description.errorMessage) }
          <input  ref="nameInput"
                  name="description"
                  className="form-control"
                  value={form.fields.description.value}
                  onChange={(e) => form.fields.description.value = e.target.value}/>
        </fieldset>

        <fieldset className="form-group">
          Status:
          { form.renderError(form.fields.status.errorMessage) }
          <select name="status"
                  className="form-control"
                  value={form.fields.status.value}
                  onChange={(e) => form.fields.description.value = e.target.value} >
            <option value="waiting">Waiting</option>
            <option value="suspend">Suspend</option>
            <option value="freeze">Freeze</option>
            <option value="completed">Completed</option>
          </select>
        </fieldset>
        {form.renderAlert(this.store.error)}
        <button onClick={ () => this.handleSend(form) }
                className="btn btn-primary"
                disabled={!form.valid} >Save</button>
        <button onClick={ (event) => dispatch(popupClose()) } className="btn btn-danger">Cancel</button>

    );
  }
}

<commit_msg>typo on set value<commit_after>require("babel-polyfill")
import React, { Component } from 'react'
import { observer } from "mobx-react"
import TodoStore from '../../stores/todo_store'
import { todoForm, todoFormSet, todoFormGet } from '../../forms/todo_form'
import { pageUpdateRecord, pageAddRecord } from '../../actions/page_actions'
import { popupClose } from '../../actions/popup_actions'
import { dispatch } from '../../helpers/dispatcher'
import ReactDOM from 'react-dom'

@observer
export default class TodoView extends Component {
  async handleSend(form) {
    await form.validate();
    if (form.valid) {
      var todo = this.props.todo
      todoFormGet(todo, form.fields)
      if (todo._id) {
        dispatch(pageUpdateRecord(this.store, todo))
      } else {
        dispatch(pageAddRecord(this.store, todo))
      }
      dispatch(popupClose())
    }
  }

  componentWillMount() {
    this.form = this.props.form ? this.props.form : todoForm
    todoFormSet(this.props.todo, this.form.fields)
    this.store = this.props.store
  }

  componentDidMount() {
    this.refs.nameInput.focus();
  }

  render() {
    const form = this.form
    return (

Todo

        <fieldset className="form-group">
          Description:
          { form.renderError(form.fields.description.errorMessage) }
          <input  ref="nameInput"
                  name="description"
                  className="form-control"
                  value={form.fields.description.value}
                  onChange={(e) => form.fields.description.value = e.target.value}/>
        </fieldset>

        <fieldset className="form-group">
          Status:
          { form.renderError(form.fields.status.errorMessage) }
          <select name="status"
                  className="form-control"
                  value={form.fields.status.value}
                  onChange={(e) => form.fields.status.value = e.target.value} >
            <option value="waiting">Waiting</option>
            <option value="suspend">Suspend</option>
            <option value="freeze">Freeze</option>
            <option value="completed">Completed</option>
          </select>
        </fieldset>
        {form.renderAlert(this.store.error)}
        <button onClick={ () => this.handleSend(form) }
                className="btn btn-primary"
                disabled={!form.valid} >Save</button>
        <button onClick={ (event) => dispatch(popupClose()) } className="btn btn-danger">Cancel</button>

    );
  }
}

<|endoftext|>


<commit_before>'use strict';

// Reload client for Chrome Apps & Extensions.
// The reload client has a compatibility with livereload.
// WARNING: only supports reload command.

var LIVERELOAD_HOST = 'localhost:';
var LIVERELOAD_PORT = 35729;
var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload');

connection.onerror = function (error) {
  console.log('reload connection got error' + JSON.stringify(error));
};

connection.onmessage = function (e) {
  if (e.data) {
    var data = JSON.parse(e.data);
    if (data && data.command === 'reload') {
      chrome.runtime.reload();
    }
  }
};
<commit_msg>Avoid deprecated message from WebSocket #88<commit_after>'use strict';

// Reload client for Chrome Apps & Extensions.
// The reload client has a compatibility with livereload.
// WARNING: only supports reload command.

var LIVERELOAD_HOST = 'localhost:';
var LIVERELOAD_PORT = 35729;
var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload');

connection.onerror = function (error) {
  console.log('reload connection got error:', error);
};

connection.onmessage = function (e) {
  if (e.data) {
    var data = JSON.parse(e.data);
    if (data && data.command === 'reload') {
      chrome.runtime.reload();
    }
  }
};
<|endoftext|>