<commit_before>import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'div',
  classNames: ['easy-datatable-container'],
  classNameBindings: ['isFullScreen:full-screen'],

  selectedCellPosition: null,
  previouslySelectedCell : null,

  editAfterInsertion: false,
  showEditorForSelectedCell: false,
  addNewRowLabel: 'Add new row',
  showAddFirstRow: false,
  showAddLastRow: false,
  isFullScreen: false,
  tableClasses: '',
  showToolBar: false,
  showDuplicateRow: false,

  actions: {
    navigate: function (direction) {
      this.send(direction);
    },

    navigateLeft: function () {
      this.set('selectedCellPosition', this.computeNavigateLeftPosition());
    },

    navigateUp: function () {
      this.set('selectedCellPosition', this.computeNavigateUpPosition());
    },

    navigateRight: function () {
      this.set('selectedCellPosition', this.computeNavigateRightPosition());
    },

    navigateDown: function () {
      this.set('selectedCellPosition', this.computeNavigateDownPosition());
    },

    addFirstRow: function () {
      var index = this.get('table').getIndexForFirstInsertableRow();
      this.insertRowAt(index, {row: index, column: 0});
    },

    addLastRow: function () {
      var index = this.get('table').getIndexForLastInsertableRow();
      this.insertRowAt(index, {row: index, column: 0});
    },

    insertRowAfter: function (index) {
      if (this.get('table').rowCanBeInserted(index)) {
        this.insertRowAt(index, this.computeNavigateDownPosition);
      }
    },

    manipulate: function (label, index) {
      this.send(label, index);
    },

    removeRow: function (index) {
      if (this.get('table').rowCanBeRemoved(index)) {
        this.get('table').removeRow(index);

        if (this.get('selectedCellPosition.row') === this.get('table.body.length')) {
          this.send('navigateUp');
        } else {
          this.notifyPropertyChange('selectedCellPosition');
        }
      }
    },

    addFirstColumn: function () {
      var index = this.get('table').getIndexForFirstInsertableColumn();
      this.insertColumnAt(index, {row: -1, column: index});
    },

    addLastColumn: function () {
      var index = this.get('table').getIndexForLastInsertableColumn();
      this.insertColumnAt(index, {row: -1, column: index});
    },

    insertColumnAfter: function (index) {
      if (this.get('table').columnCanBeInserted(index)) {
        this.insertColumnAt(index, this.computeNavigateRightPosition);
      }
    },

    removeColumn: function (index) {
      if (this.get('table').columnCanBeRemoved(index)) {
        this.get('table').removeColumn(index);
        this.notifyPropertyChange('selectedCellPosition');
      }
    },

    moveRowUp: function (index) {
      if (this.get('table').rowCanMoveUp(index)) {
        this.get('table').moveRow(index, index - 1);
        this.send('navigateUp');
      }
    },

    moveRowDown: function (index) {
      if (this.get('table').rowCanMoveDown(index)) {
        this.get('table').moveRow(index, index + 1);
        this.send('navigateDown');
      }
    },

    duplicateRow : function (index) {
      if (this.get('table').rowCanBeInserted(index+1)) {
        this.insertRowAt(index+1, {row: index+1, column: 0});
        this.get('table').populateClonedRowCells(index);
      }
    },

    moveColumnLeft: function (index) {
      if (this.get('table').columnCanMoveLeft(index)) {
        this.get('table').moveColumn(index, index - 1);
        this.send('navigateLeft');
      }
    },

    moveColumnRight: function (index) {
      if (this.get('table').columnCanMoveRight(index)) {
        this.get('table').moveColumn(index, index + 1);
        this.send('navigateRight');
      }
    }
  },

  insertRowAt: function (index, nextPosition) {
    if (Ember.isNone(index)) {
      return;
    }

    this.get('table').insertRow(index);

    if (typeof(nextPosition) === 'function') {
      nextPosition = nextPosition.apply(this);
    }

    this.set('selectedCellPosition', nextPosition);
    if (this.get('editAfterInsertion')) {
      this.navigateToFirstEditableCellInRow();
      this.set('showEditorForSelectedCell', true);
    }
  },

  firstEditableCellIndexInColumn: function (columnIndex) {
    var index;

    if (this.get('table.headers.cells')[columnIndex].get('isEditable')) {
      return -1;
    }
    for (index = 0; index < this.get('table.body.length'); index++) {
      if (this.get('table.body')[index].get('cells')[columnIndex].get('isEditable')) {
        return index;
      }
    }
  },

  navigateToFirstEditableCellInColumn: function () {
    var columnIndex = this.get('selectedCellPosition.column'),
       rowIndex = this.firstEditableCellIndexInColumn(columnIndex);

    if (!Ember.isNone(rowIndex)) {
      this.set('selectedCellPosition', {row: rowIndex, column: columnIndex});
    }
  },

  firstEditableCellIndexInRow: function (rowIndex) {
    var index, row = this.get('table.body')[rowIndex].get('cells');

    for (index = 0; index < row.length; index++) {
      if (row[index].get('isEditable')) {
        return index;
      }
    }
  },

  navigateToFirstEditableCellInRow: function () {
    var rowIndex = this.get('selectedCellPosition.row'),
      columnIndex = this.firstEditableCellIndexInRow(rowIndex);

    if (!Ember.isNone(columnIndex)) {
      this.set('selectedCellPosition', {row: rowIndex, column: columnIndex});
    }
  },

  highlightedColumn: Ember.computed('selectedCellPosition', function () {
    var position = this.get('selectedCellPosition');
    if (Ember.isNone(position) || position.row !== -1) {
      return;
    }
    return position.column;
  }),

  highlightedRow: Ember.computed('selectedCellPosition', function () {
    var position = this.get('selectedCellPosition'),
      cell = this.get('selectedCell');

    if (Ember.isNone(cell) || !cell.get('isHeader') || position.row < 0) {
      return;
    }
    return position.row;
  }),

  fixPosition: function (position) {
    if (!this.isRowValid(position)) {
      position = this.fixRowPosition(position);
    } else if (!(this.isColumnValid(position))) {
      position = this.fixColumnPosition(position);
    }

    if (!this.isRowValid(position) || !this.isColumnValid(position)) {
      position.row = null;
      position.column = null;
    }

    return position;
  },

  isRowValid: function (position) {
    var rowCount = this.get('table.body.length');
    return position.row >= -1 && position.row < rowCount;
  },

  isColumnValid: function (position) {
    var columnCount = this.get('table.headers.cells.length');
    return position.column >= 0 && position.column < columnCount;
  },

  fixRowPosition: function (position) {
    var rowCount = this.get('table.body.length');

    if (position.row < - 1) {
      position.row = rowCount - 1;
      position.column -= 1;
    }

    if (position.row >= rowCount) {
      position.row = -1;
      position.column += 1;
    }
    return position;
  },

  fixColumnPosition: function (position) {
    var columnCount = this.get('table.body.firstObject.cells.length');

    if (position.column < 0) {
      position.column = columnCount - 1;
      position.row -= 1;
    }

    if (position.column >= columnCount) {
      position.column = 0;
      position.row += 1;
    }

    return position;
  },

  computeNavigateUpPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition(this.giveValidRowPosition(current, current.row - 1));
  },

  computeNavigateDownPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition(this.giveValidRowPosition(current, current.row + 1));
  },

  giveValidRowPosition: function (current, new_row) {
    var new_position = {row: new_row, column: current.column};
    if (!this.isRowValid(new_position)) {
      new_position = {row: current.row, column: current.column};
    }
    return new_position;
  },

  computeNavigateRightPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition({row: current.row, column: current.column + 1});
  },

  computeNavigateLeftPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition({row: current.row, column: current.column - 1});
  },

  insertColumnAt: function (index, nextPosition) {
    if (Ember.isNone(index)) {
      return;
    }

    this.get('table').insertColumn(index);
    if (typeof(nextPosition) === 'function') {
      nextPosition = nextPosition.apply(this);
    }

    this.set('selectedCellPosition', nextPosition);
    if (this.get('editAfterInsertion')) {
      this.navigateToFirstEditableCellInColumn();
      this.set('showEditorForSelectedCell', true);
    }
  },

  selectedCell: Ember.computed('selectedCellPosition', function () {
    var position = this.get('selectedCellPosition');
    if (Ember.isNone(position) || Ember.isNone(position.row) || Ember.isNone(position.column)) {
      return;
    }
    if (position.row === -1) {
      return this.get('table.headers.cells')[position.column];
    }
    return this.get('table.body')[position.row].get('cells')[position.column];
  }),

	updateSelection: Ember.observer('selectedCell', function () {
    var previous = this.get('previouslySelectedCell'),
      cell = this.get('selectedCell');

    if (!Ember.isNone(previous)  && !previous.get('isDestroying')) {
      previous.set('isSelected', false);
    }

    if (Ember.isNone(cell)) {
      this.set('previouslySelectedCell', null);
    } else {
      cell.set('isSelected', true);
      this.set('previouslySelectedCell', cell);
    }
  })
});
<commit_msg>initialize the selectedCellPosition to {} to avoid javascript error<commit_after>import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'div',
  classNames: ['easy-datatable-container'],
  classNameBindings: ['isFullScreen:full-screen'],

  selectedCellPosition: {},
  previouslySelectedCell : null,

  editAfterInsertion: false,
  showEditorForSelectedCell: false,
  addNewRowLabel: 'Add new row',
  showAddFirstRow: false,
  showAddLastRow: false,
  isFullScreen: false,
  tableClasses: '',
  showToolBar: false,
  showDuplicateRow: false,

  actions: {
    navigate: function (direction) {
      this.send(direction);
    },

    navigateLeft: function () {
      this.set('selectedCellPosition', this.computeNavigateLeftPosition());
    },

    navigateUp: function () {
      this.set('selectedCellPosition', this.computeNavigateUpPosition());
    },

    navigateRight: function () {
      this.set('selectedCellPosition', this.computeNavigateRightPosition());
    },

    navigateDown: function () {
      this.set('selectedCellPosition', this.computeNavigateDownPosition());
    },

    addFirstRow: function () {
      var index = this.get('table').getIndexForFirstInsertableRow();
      this.insertRowAt(index, {row: index, column: 0});
    },

    addLastRow: function () {
      var index = this.get('table').getIndexForLastInsertableRow();
      this.insertRowAt(index, {row: index, column: 0});
    },

    insertRowAfter: function (index) {
      if (this.get('table').rowCanBeInserted(index)) {
        this.insertRowAt(index, this.computeNavigateDownPosition);
      }
    },

    manipulate: function (label, index) {
      this.send(label, index);
    },

    removeRow: function (index) {
      if (this.get('table').rowCanBeRemoved(index)) {
        this.get('table').removeRow(index);

        if (this.get('selectedCellPosition.row') === this.get('table.body.length')) {
          this.send('navigateUp');
        } else {
          this.notifyPropertyChange('selectedCellPosition');
        }
      }
    },

    addFirstColumn: function () {
      var index = this.get('table').getIndexForFirstInsertableColumn();
      this.insertColumnAt(index, {row: -1, column: index});
    },

    addLastColumn: function () {
      var index = this.get('table').getIndexForLastInsertableColumn();
      this.insertColumnAt(index, {row: -1, column: index});
    },

    insertColumnAfter: function (index) {
      if (this.get('table').columnCanBeInserted(index)) {
        this.insertColumnAt(index, this.computeNavigateRightPosition);
      }
    },

    removeColumn: function (index) {
      if (this.get('table').columnCanBeRemoved(index)) {
        this.get('table').removeColumn(index);
        this.notifyPropertyChange('selectedCellPosition');
      }
    },

    moveRowUp: function (index) {
      if (this.get('table').rowCanMoveUp(index)) {
        this.get('table').moveRow(index, index - 1);
        this.send('navigateUp');
      }
    },

    moveRowDown: function (index) {
      if (this.get('table').rowCanMoveDown(index)) {
        this.get('table').moveRow(index, index + 1);
        this.send('navigateDown');
      }
    },

    duplicateRow : function (index) {
      if (this.get('table').rowCanBeInserted(index+1)) {
        this.insertRowAt(index+1, {row: index+1, column: 0});
        this.get('table').populateClonedRowCells(index);
      }
    },

    moveColumnLeft: function (index) {
      if (this.get('table').columnCanMoveLeft(index)) {
        this.get('table').moveColumn(index, index - 1);
        this.send('navigateLeft');
      }
    },

    moveColumnRight: function (index) {
      if (this.get('table').columnCanMoveRight(index)) {
        this.get('table').moveColumn(index, index + 1);
        this.send('navigateRight');
      }
    }
  },

  insertRowAt: function (index, nextPosition) {
    if (Ember.isNone(index)) {
      return;
    }

    this.get('table').insertRow(index);

    if (typeof(nextPosition) === 'function') {
      nextPosition = nextPosition.apply(this);
    }

    this.set('selectedCellPosition', nextPosition);
    if (this.get('editAfterInsertion')) {
      this.navigateToFirstEditableCellInRow();
      this.set('showEditorForSelectedCell', true);
    }
  },

  firstEditableCellIndexInColumn: function (columnIndex) {
    var index;

    if (this.get('table.headers.cells')[columnIndex].get('isEditable')) {
      return -1;
    }
    for (index = 0; index < this.get('table.body.length'); index++) {
      if (this.get('table.body')[index].get('cells')[columnIndex].get('isEditable')) {
        return index;
      }
    }
  },

  navigateToFirstEditableCellInColumn: function () {
    var columnIndex = this.get('selectedCellPosition.column'),
       rowIndex = this.firstEditableCellIndexInColumn(columnIndex);

    if (!Ember.isNone(rowIndex)) {
      this.set('selectedCellPosition', {row: rowIndex, column: columnIndex});
    }
  },

  firstEditableCellIndexInRow: function (rowIndex) {
    var index, row = this.get('table.body')[rowIndex].get('cells');

    for (index = 0; index < row.length; index++) {
      if (row[index].get('isEditable')) {
        return index;
      }
    }
  },

  navigateToFirstEditableCellInRow: function () {
    var rowIndex = this.get('selectedCellPosition.row'),
      columnIndex = this.firstEditableCellIndexInRow(rowIndex);

    if (!Ember.isNone(columnIndex)) {
      this.set('selectedCellPosition', {row: rowIndex, column: columnIndex});
    }
  },

  highlightedColumn: Ember.computed('selectedCellPosition', function () {
    var position = this.get('selectedCellPosition');
    if (Ember.isNone(position) || position.row !== -1) {
      return;
    }
    return position.column;
  }),

  highlightedRow: Ember.computed('selectedCellPosition', function () {
    var position = this.get('selectedCellPosition'),
      cell = this.get('selectedCell');

    if (Ember.isNone(cell) || !cell.get('isHeader') || position.row < 0) {
      return;
    }
    return position.row;
  }),

  fixPosition: function (position) {
    if (!this.isRowValid(position)) {
      position = this.fixRowPosition(position);
    } else if (!(this.isColumnValid(position))) {
      position = this.fixColumnPosition(position);
    }

    if (!this.isRowValid(position) || !this.isColumnValid(position)) {
      position.row = null;
      position.column = null;
    }

    return position;
  },

  isRowValid: function (position) {
    var rowCount = this.get('table.body.length');
    return position.row >= -1 && position.row < rowCount;
  },

  isColumnValid: function (position) {
    var columnCount = this.get('table.headers.cells.length');
    return position.column >= 0 && position.column < columnCount;
  },

  fixRowPosition: function (position) {
    var rowCount = this.get('table.body.length');

    if (position.row < - 1) {
      position.row = rowCount - 1;
      position.column -= 1;
    }

    if (position.row >= rowCount) {
      position.row = -1;
      position.column += 1;
    }
    return position;
  },

  fixColumnPosition: function (position) {
    var columnCount = this.get('table.body.firstObject.cells.length');

    if (position.column < 0) {
      position.column = columnCount - 1;
      position.row -= 1;
    }

    if (position.column >= columnCount) {
      position.column = 0;
      position.row += 1;
    }

    return position;
  },

  computeNavigateUpPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition(this.giveValidRowPosition(current, current.row - 1));
  },

  computeNavigateDownPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition(this.giveValidRowPosition(current, current.row + 1));
  },

  giveValidRowPosition: function (current, new_row) {
    var new_position = {row: new_row, column: current.column};
    if (!this.isRowValid(new_position)) {
      new_position = {row: current.row, column: current.column};
    }
    return new_position;
  },

  computeNavigateRightPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition({row: current.row, column: current.column + 1});
  },

  computeNavigateLeftPosition: function () {
    var current = this.get('selectedCellPosition');
    return this.fixPosition({row: current.row, column: current.column - 1});
  },

  insertColumnAt: function (index, nextPosition) {
    if (Ember.isNone(index)) {
      return;
    }

    this.get('table').insertColumn(index);
    if (typeof(nextPosition) === 'function') {
      nextPosition = nextPosition.apply(this);
    }

    this.set('selectedCellPosition', nextPosition);
    if (this.get('editAfterInsertion')) {
      this.navigateToFirstEditableCellInColumn();
      this.set('showEditorForSelectedCell', true);
    }
  },

  selectedCell: Ember.computed('selectedCellPosition', function () {
    var position = this.get('selectedCellPosition');
    if (Ember.isNone(position) || Ember.isNone(position.row) || Ember.isNone(position.column)) {
      return;
    }
    if (position.row === -1) {
      return this.get('table.headers.cells')[position.column];
    }
    return this.get('table.body')[position.row].get('cells')[position.column];
  }),

	updateSelection: Ember.observer('selectedCell', function () {
    var previous = this.get('previouslySelectedCell'),
      cell = this.get('selectedCell');

    if (!Ember.isNone(previous)  && !previous.get('isDestroying')) {
      previous.set('isSelected', false);
    }

    if (Ember.isNone(cell)) {
      this.set('previouslySelectedCell', null);
    } else {
      cell.set('isSelected', true);
      this.set('previouslySelectedCell', cell);
    }
  })
});
<|endoftext|>


<commit_before>import _ from 'lodash'
import Ember from 'ember'
const {Component, run} = Ember
import computed, {readOnly} from 'ember-computed-decorators'
import layout from '../templates/components/frost-textarea'

export default Component.extend({
  // ==========================================================================
  // Dependencies
  // ==========================================================================

  // ==========================================================================
  // Properties
  // ==========================================================================

  attributeBindings: [
    'autofocus',
    'cols',
    'disabled',
    'placeholder',
    'readonly',
    'rows'
  ],
  classNames: ['frost-textarea'],
  layout,
  tabindex: 0,

  // ==========================================================================
  // Computed Properties
  // ==========================================================================

  @readOnly
  @computed('disabled', 'value')
  /**
   * Determine whether or not to show button for clearing out text field
   * @param {Boolean} disabled - whether or not input is disabled
   * @param {String} value - value of text field
   * @returns {Boolean} whether or not to show button for clearing out text field
   */
  showClear (disabled, value) {
    return !disabled && Boolean(value)
  },

  // ==========================================================================
  // Functions
  // ==========================================================================

  // ==========================================================================
  // Events
  // ==========================================================================

  oninput: Ember.on('input', function (e) {
    if (_.isFunction(this.attrs['onInput'])) {
      run.next(this, function () {
        this.attrs['onInput']({
          id: this.get('id'),
          value: e.target.value
        })
      })
    }
  }),

  _onFocus: Ember.on('focusIn', function () {
    // If an onFocus handler is defined, call it
    if (this.attrs.onFocus) {
      this.attrs.onFocus()
    }
  }),

  // ==========================================================================
  // Actions
  // ==========================================================================

  actions: {
    clear: function () {
      this.set('value', '')
      this.$('textarea').focus()
      this.$('textarea').val('')
      this.$('textarea').trigger('input')
    },

    onBlur () {
      const onBlur = this.get('onBlur')

      if (onBlur) {
        onBlur()
      }
    }
  }
})
<commit_msg>Stop adding extra run loop for textarea input<commit_after>import _ from 'lodash'
import Ember from 'ember'
const {Component} = Ember
import computed, {readOnly} from 'ember-computed-decorators'
import layout from '../templates/components/frost-textarea'

export default Component.extend({
  // ==========================================================================
  // Dependencies
  // ==========================================================================

  // ==========================================================================
  // Properties
  // ==========================================================================

  attributeBindings: [
    'autofocus',
    'cols',
    'disabled',
    'placeholder',
    'readonly',
    'rows'
  ],
  classNames: ['frost-textarea'],
  layout,
  tabindex: 0,

  // ==========================================================================
  // Computed Properties
  // ==========================================================================

  @readOnly
  @computed('disabled', 'value')
  /**
   * Determine whether or not to show button for clearing out text field
   * @param {Boolean} disabled - whether or not input is disabled
   * @param {String} value - value of text field
   * @returns {Boolean} whether or not to show button for clearing out text field
   */
  showClear (disabled, value) {
    return !disabled && Boolean(value)
  },

  // ==========================================================================
  // Functions
  // ==========================================================================

  // ==========================================================================
  // Events
  // ==========================================================================

  oninput: Ember.on('input', function (e) {
    const onInput = this.attrs['onInput']

    if (_.isFunction(onInput)) {
      onInput({
        id: this.get('id'),
        value: e.target.value
      })
    }
  }),

  _onFocus: Ember.on('focusIn', function () {
    // If an onFocus handler is defined, call it
    if (this.attrs.onFocus) {
      this.attrs.onFocus()
    }
  }),

  // ==========================================================================
  // Actions
  // ==========================================================================

  actions: {
    clear: function () {
      this.set('value', '')
      this.$('textarea').focus()
      this.$('textarea').val('')
      this.$('textarea').trigger('input')
    },

    onBlur () {
      const onBlur = this.get('onBlur')

      if (onBlur) {
        onBlur()
      }
    }
  }
})
<|endoftext|>


<commit_before>Abstractor = {};

Abstractor.AbstractionUI = function(){
  $(document).on('click', '.abstractor_abstraction_value a.edit_link', function(e){
    e.preventDefault();
    parent_div = $(this).closest('.abstractor_abstraction');
    parent_div.load($(this).attr('href'), function(){
      parent_div.find('.combobox').combobox({watermark:'a value'});
      parent_div.find('input[type="submit"], button, a.button').button();
    });
    parent_div.addClass('highlighted');
    //parent_div.siblings('.abstractor_abstraction').block({ message: null, overlayCSS: { opacity: .2 }});
  });
  $(document).on('ajax:success', 'form.edit_abstractor_abstraction', function(e, data, status, xhr){
    parent_div = $(this).closest('.abstractor_abstraction');
    parent_div.html(xhr.responseText);
    //parent_div.siblings('.abstractor_abstraction').unblock();
    parent_div.removeClass('highlighted');
  });
  $(document).on('click', ".edit_abstractor_abstraction input[type='radio']", function(){
    $(this).siblings("input[type='checkbox']").prop('checked',false);
  });
  $(document).on('click', ".edit_abstractor_abstraction input[type='checkbox']", function(){
    $(this).siblings("input[type='checkbox']").prop('checked',false);
    $(this).siblings("input[type='text']").prop('value','');
    var autocompleters = $(this).siblings('select.combobox');
    autocompleters.combobox('setValue', '');
    autocompleters.change();

    $.each($(this).siblings("input[type='radio']"), function(){
      if ($(this).prop('value') === '') {
        $(this).prop('checked',true);
      } else {
        $(this).prop('checked',false);
      }
    });
  });
  $(document).on('change', '.edit_abstractor_abstraction select.combobox', function () {
    if ($(this).find('option:selected').prop('value').length){
      $(this).siblings("input[type='checkbox']").prop('checked',false);
    }
  });
  $(document).on('change', ".edit_abstractor_abstraction input[type='text']", function(){
    $(this).siblings("input[type='checkbox']").prop('checked',false);
  });
  $(document.body).append('

');

  $(document).on('click','.abstractor_abstraction_source_tooltip_img', function (evt) {
    var target = $(this).attr('rel'),
        html = $(target).html(),
        title = $(this).attr('title');

    evt.preventDefault();
    $('#abstractor_abstraction_dialog_tooltip').html(html);
    $('#abstractor_abstraction_dialog_tooltip').dialog('option', 'title', title);
    $('#abstractor_abstraction_dialog_tooltip').dialog('option', 'width', ($(window).width() * 0.80));
    $('#abstractor_abstraction_dialog_tooltip').dialog('open');
  });

  $('#abstractor_abstraction_dialog_tooltip').dialog({
    maxHeight: 400,
    autoOpen: false,
    width: 600,
    zIndex: 40000
  });
};

Abstractor.AbstractionSuggestionUI = function(){
  $(document).on('change', 'form.edit_abstractor_suggestion select', function() {
    $(this).closest('form').submit();
  });
  $(document).on('ajax:success', 'form.edit_abstractor_suggestion', function(e, data, status, xhr){
    $(this).closest('.abstractor_abstraction').html(xhr.responseText);
  });
};

Abstractor.AbstractionGroupUI = function(){
  $(document).on('ajax:success', '.abstractor_abstraction_group .delete_link', function(e, data, status, xhr){
    parent_div = $(this).closest('.abstractor_abstraction_group');
    parent_div.html(xhr.responseText);
  });

  $(document).on('ajax:success', '.abstractor_subject_groups_container .add_link', function(e, data, status, xhr){
    parent_div = $(this).closest('.abstractor_subject_groups_container');
    parent_div.find('.abstractor_subject_groups').append(xhr.responseText);
  });
};<commit_msg>Add specific class to jQuery UI dialog to help with testing later.<commit_after>Abstractor = {};

Abstractor.AbstractionUI = function(){
  $(document).on('click', '.abstractor_abstraction_value a.edit_link', function(e){
    e.preventDefault();
    parent_div = $(this).closest('.abstractor_abstraction');
    parent_div.load($(this).attr('href'), function(){
      parent_div.find('.combobox').combobox({watermark:'a value'});
      parent_div.find('input[type="submit"], button, a.button').button();
    });
    parent_div.addClass('highlighted');
    //parent_div.siblings('.abstractor_abstraction').block({ message: null, overlayCSS: { opacity: .2 }});
  });
  $(document).on('ajax:success', 'form.edit_abstractor_abstraction', function(e, data, status, xhr){
    parent_div = $(this).closest('.abstractor_abstraction');
    parent_div.html(xhr.responseText);
    //parent_div.siblings('.abstractor_abstraction').unblock();
    parent_div.removeClass('highlighted');
  });
  $(document).on('click', ".edit_abstractor_abstraction input[type='radio']", function(){
    $(this).siblings("input[type='checkbox']").prop('checked',false);
  });
  $(document).on('click', ".edit_abstractor_abstraction input[type='checkbox']", function(){
    $(this).siblings("input[type='checkbox']").prop('checked',false);
    $(this).siblings("input[type='text']").prop('value','');
    var autocompleters = $(this).siblings('select.combobox');
    autocompleters.combobox('setValue', '');
    autocompleters.change();

    $.each($(this).siblings("input[type='radio']"), function(){
      if ($(this).prop('value') === '') {
        $(this).prop('checked',true);
      } else {
        $(this).prop('checked',false);
      }
    });
  });
  $(document).on('change', '.edit_abstractor_abstraction select.combobox', function () {
    if ($(this).find('option:selected').prop('value').length){
      $(this).siblings("input[type='checkbox']").prop('checked',false);
    }
  });
  $(document).on('change', ".edit_abstractor_abstraction input[type='text']", function(){
    $(this).siblings("input[type='checkbox']").prop('checked',false);
  });
  $(document.body).append('

');

  $(document).on('click','.abstractor_abstraction_source_tooltip_img', function (evt) {
    var target = $(this).attr('rel'),
        html = $(target).html(),
        title = $(this).attr('title');

    evt.preventDefault();
    $('#abstractor_abstraction_dialog_tooltip').html(html);
    $('#abstractor_abstraction_dialog_tooltip').dialog('option', 'title', title);
    $('#abstractor_abstraction_dialog_tooltip').dialog('option', 'width', ($(window).width() * 0.80));
    $('#abstractor_abstraction_dialog_tooltip').dialog('open');
  });

  $('#abstractor_abstraction_dialog_tooltip').dialog({
    maxHeight: 400,
    autoOpen: false,
    width: 600,
    zIndex: 40000,
    dialogClass: 'ui-dialog_encounter_note'
  });
};

Abstractor.AbstractionSuggestionUI = function(){
  $(document).on('change', 'form.edit_abstractor_suggestion select', function() {
    $(this).closest('form').submit();
  });
  $(document).on('ajax:success', 'form.edit_abstractor_suggestion', function(e, data, status, xhr){
    $(this).closest('.abstractor_abstraction').html(xhr.responseText);
  });
};

Abstractor.AbstractionGroupUI = function(){
  $(document).on('ajax:success', '.abstractor_abstraction_group .delete_link', function(e, data, status, xhr){
    parent_div = $(this).closest('.abstractor_abstraction_group');
    parent_div.html(xhr.responseText);
  });

  $(document).on('ajax:success', '.abstractor_subject_groups_container .add_link', function(e, data, status, xhr){
    parent_div = $(this).closest('.abstractor_subject_groups_container');
    parent_div.find('.abstractor_subject_groups').append(xhr.responseText);
  });
};<|endoftext|>


<commit_before>angular.module('app')
  .directive("notesSection", function(){
    return {
      scope: {
        addNew: "&",
        selectionMade: "&",
        tag: "="
      },

      templateUrl: 'notes.html',
      replace: true,
      controller: 'NotesCtrl',
      controllerAs: 'ctrl',
      bindToController: true,

      link:function(scope, elem, attrs, ctrl) {
        scope.$watch('ctrl.tag', function(tag, oldTag){
          if(tag) {
            if(tag.needsLoad) {
              scope.$watch('ctrl.tag.didLoad', function(didLoad){
                if(didLoad) {
                  tag.needsLoad = false;
                  ctrl.tagDidChange(tag, oldTag);
                }
              });
            } else {
              ctrl.tagDidChange(tag, oldTag);
            }
          }
        });
      }
    }
  })
  .controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager, desktopManager) {

    this.panelController = {};

    $rootScope.$on("user-preferences-changed", () => {
      this.loadPreferences();
    });

    this.loadPreferences = function() {
      let prevSortValue = this.sortBy;

      this.sortBy = authManager.getUserPrefValue("sortBy", "created_at");

      if(this.sortBy == "updated_at") {
        // use client_updated_at instead
        this.sortBy = "client_updated_at";
      }

      if(prevSortValue && prevSortValue != this.sortBy) {
        $timeout(() => {
          this.selectFirstNote();
        })
      }
      this.sortDescending = this.sortBy != "title";

      this.showArchived = authManager.getUserPrefValue("showArchived", false);
      this.hidePinned = authManager.getUserPrefValue("hidePinned", false);
      this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false);
      this.hideDate = authManager.getUserPrefValue("hideDate", false);
      this.hideTags = authManager.getUserPrefValue("hideTags", false);

      let width = authManager.getUserPrefValue("notesPanelWidth");
      if(width) {
        this.panelController.setWidth(width);
      }
    }

    this.loadPreferences();

    this.onPanelResize = function(newWidth) {
      authManager.setUserPrefValue("notesPanelWidth", newWidth);
      authManager.syncUserPreferences();
    }

    angular.element(document).ready(() => {
      this.loadPreferences();
    });

    $rootScope.$on("editorFocused", function(){
      this.showMenu = false;
    }.bind(this))

    $rootScope.$on("noteDeleted", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this))

    $rootScope.$on("noteArchived", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this));

    // When a note is removed from the list
    this.onNoteRemoval = function() {
      let visibleNotes = this.visibleNotes();
      if(this.selectedIndex < visibleNotes.length) {
        this.selectNote(visibleNotes[Math.max(this.selectedIndex, 0)]);
      } else {
        this.selectNote(visibleNotes[visibleNotes.length - 1]);
      }
    }

    let MinNoteHeight = 51.0; // This is the height of a note cell with nothing but the title, which *is* a display option
    this.DefaultNotesToDisplayValue = (document.documentElement.clientHeight / MinNoteHeight) || 20;

    this.paginate = function() {
      this.notesToDisplay += this.DefaultNotesToDisplayValue
    }

    this.resetPagination = function() {
      this.notesToDisplay = this.DefaultNotesToDisplayValue;
    }

    this.resetPagination();

    this.panelTitle = function() {
      if(this.isFiltering()) {
        return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
      } else if(this.tag) {
        return `${this.tag.title} notes`;
      }
    }

    this.optionsSubtitle = function() {
      var base = "";
      if(this.sortBy == "created_at") {
        base += " Date Added";
      } else if(this.sortBy == "client_updated_at") {
        base += " Date Modified";
      } else if(this.sortBy == "title") {
        base += " Title";
      }

      if(this.showArchived && (!this.tag || !this.tag.archiveTag)) {
        base += " | + Archived"
      }

      if(this.hidePinned) {
        base += " | – Pinned"
      }

      return base;
    }

    this.toggleKey = function(key) {
      this[key] = !this[key];
      authManager.setUserPrefValue(key, this[key]);
      authManager.syncUserPreferences();
    }

    this.tagDidChange = function(tag, oldTag) {
      var scrollable = document.getElementById("notes-scrollable");
      if(scrollable) {
        scrollable.scrollTop = 0;
        scrollable.scrollLeft = 0;
      }

      this.resetPagination();

      this.showMenu = false;

      if(this.selectedNote && this.selectedNote.dummy) {
        if(oldTag) {
          _.remove(oldTag.notes, this.selectedNote);
        }
      }

      this.noteFilter.text = "";
      this.setNotes(tag.notes);
    }

    this.setNotes = function(notes) {
      notes.forEach(function(note){
        note.visible = true;
      })

      var createNew = notes.length == 0;
      this.selectFirstNote(createNew);
    }

    this.visibleNotes = function() {
      return this.sortedNotes.filter(function(note){
        return note.visible;
      });
    }

    this.selectFirstNote = function(createNew) {
      var visibleNotes = this.visibleNotes();

      if(visibleNotes.length > 0) {
        this.selectNote(visibleNotes[0]);
      } else if(createNew) {
        this.createNewNote();
      }
    }

    this.selectNote = function(note, viaClick = false) {
      if(!note) {
        this.createNewNote();
        return;
      }
      this.selectedNote = note;
      note.conflict_of = null; // clear conflict
      this.selectionMade()(note);
      this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);

      if(viaClick && this.isFiltering()) {
        desktopManager.searchText(this.noteFilter.text);
      }
    }

    this.isFiltering = function() {
      return this.noteFilter.text && this.noteFilter.text.length > 0;
    }

    this.createNewNote = function() {
      var title = "New Note" + (this.tag.notes ? (" " + (this.tag.notes.length + 1)) : "");
      let newNote = modelManager.createItem({content_type: "Note", content: {text: "", title: title}});
      newNote.dummy = true;
      this.newNote = newNote;
      this.selectNote(this.newNote);
      this.addNew()(this.newNote);
    }

    this.noteFilter = {text : ''};

    this.filterNotes = function(note) {
      if((note.archived && !this.showArchived && !this.tag.archiveTag) || (note.pinned && this.hidePinned)) {
        note.visible = false;
        return note.visible;
      }

      var filterText = this.noteFilter.text.toLowerCase();
      if(filterText.length == 0) {
        note.visible = true;
      } else {
        var words = filterText.split(" ");
        var matchesTitle = words.every(function(word) { return  note.safeTitle().toLowerCase().indexOf(word) >= 0; });
        var matchesBody = words.every(function(word) { return  note.safeText().toLowerCase().indexOf(word) >= 0; });
        note.visible = matchesTitle || matchesBody;
      }

      if(this.tag.archiveTag) {
        note.visible = note.visible && note.archived;
      }

      return note.visible;
    }.bind(this)

    this.onFilterEnter = function() {
      // For Desktop, performing a search right away causes input to lose focus.
      // We wait until user explicity hits enter before highlighting desktop search results.
      desktopManager.searchText(this.noteFilter.text);
    }

    this.clearFilterText = function() {
      this.noteFilter.text = '';
      this.onFilterEnter();
      this.filterTextChanged();

      // Reset loaded notes
      this.resetPagination();
    }

    this.filterTextChanged = function() {
      $timeout(function(){
        if(!this.selectedNote.visible) {
          this.selectFirstNote(false);
        }
      }.bind(this), 100)
    }

    this.selectedMenuItem = function($event) {
      this.showMenu = false;
    }

    this.selectedSortByCreated = function() {
      this.setSortBy("created_at");
      this.sortDescending = true;
    }

    this.selectedSortByUpdated = function() {
      this.setSortBy("client_updated_at");
      this.sortDescending = true;
    }

    this.selectedSortByTitle = function() {
      this.setSortBy("title");
      this.sortDescending = false;
    }

    this.setSortBy = function(type) {
      this.sortBy = type;
      authManager.setUserPrefValue("sortBy", this.sortBy);
      authManager.syncUserPreferences();
    }

    this.shouldShowTags = function(note) {
      if(this.hideTags) {
        return false;
      }

      if(this.tag.all) {
        return true;
      }

      // Inside a tag, only show tags string if note contains tags other than this.tag
      return note.tags && note.tags.length > 1;
    }

  });
<commit_msg>Filtering<commit_after>angular.module('app')
  .directive("notesSection", function(){
    return {
      scope: {
        addNew: "&",
        selectionMade: "&",
        tag: "="
      },

      templateUrl: 'notes.html',
      replace: true,
      controller: 'NotesCtrl',
      controllerAs: 'ctrl',
      bindToController: true,

      link:function(scope, elem, attrs, ctrl) {
        scope.$watch('ctrl.tag', function(tag, oldTag){
          if(tag) {
            if(tag.needsLoad) {
              scope.$watch('ctrl.tag.didLoad', function(didLoad){
                if(didLoad) {
                  tag.needsLoad = false;
                  ctrl.tagDidChange(tag, oldTag);
                }
              });
            } else {
              ctrl.tagDidChange(tag, oldTag);
            }
          }
        });
      }
    }
  })
  .controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager, desktopManager) {

    this.panelController = {};

    $rootScope.$on("user-preferences-changed", () => {
      this.loadPreferences();
    });

    this.loadPreferences = function() {
      let prevSortValue = this.sortBy;

      this.sortBy = authManager.getUserPrefValue("sortBy", "created_at");

      if(this.sortBy == "updated_at") {
        // use client_updated_at instead
        this.sortBy = "client_updated_at";
      }

      if(prevSortValue && prevSortValue != this.sortBy) {
        $timeout(() => {
          this.selectFirstNote();
        })
      }
      this.sortDescending = this.sortBy != "title";

      this.showArchived = authManager.getUserPrefValue("showArchived", false);
      this.hidePinned = authManager.getUserPrefValue("hidePinned", false);
      this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false);
      this.hideDate = authManager.getUserPrefValue("hideDate", false);
      this.hideTags = authManager.getUserPrefValue("hideTags", false);

      let width = authManager.getUserPrefValue("notesPanelWidth");
      if(width) {
        this.panelController.setWidth(width);
      }
    }

    this.loadPreferences();

    this.onPanelResize = function(newWidth) {
      authManager.setUserPrefValue("notesPanelWidth", newWidth);
      authManager.syncUserPreferences();
    }

    angular.element(document).ready(() => {
      this.loadPreferences();
    });

    $rootScope.$on("editorFocused", function(){
      this.showMenu = false;
    }.bind(this))

    $rootScope.$on("noteDeleted", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this))

    $rootScope.$on("noteArchived", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this));

    // When a note is removed from the list
    this.onNoteRemoval = function() {
      let visibleNotes = this.visibleNotes();
      if(this.selectedIndex < visibleNotes.length) {
        this.selectNote(visibleNotes[Math.max(this.selectedIndex, 0)]);
      } else {
        this.selectNote(visibleNotes[visibleNotes.length - 1]);
      }
    }

    let MinNoteHeight = 51.0; // This is the height of a note cell with nothing but the title, which *is* a display option
    this.DefaultNotesToDisplayValue = (document.documentElement.clientHeight / MinNoteHeight) || 20;

    this.paginate = function() {
      this.notesToDisplay += this.DefaultNotesToDisplayValue
    }

    this.resetPagination = function() {
      this.notesToDisplay = this.DefaultNotesToDisplayValue;
    }

    this.resetPagination();

    this.panelTitle = function() {
      if(this.isFiltering()) {
        return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
      } else if(this.tag) {
        return `${this.tag.title} notes`;
      }
    }

    this.optionsSubtitle = function() {
      var base = "";
      if(this.sortBy == "created_at") {
        base += " Date Added";
      } else if(this.sortBy == "client_updated_at") {
        base += " Date Modified";
      } else if(this.sortBy == "title") {
        base += " Title";
      }

      if(this.showArchived && (!this.tag || !this.tag.archiveTag)) {
        base += " | + Archived"
      }

      if(this.hidePinned) {
        base += " | – Pinned"
      }

      return base;
    }

    this.toggleKey = function(key) {
      this[key] = !this[key];
      authManager.setUserPrefValue(key, this[key]);
      authManager.syncUserPreferences();
    }

    this.tagDidChange = function(tag, oldTag) {
      var scrollable = document.getElementById("notes-scrollable");
      if(scrollable) {
        scrollable.scrollTop = 0;
        scrollable.scrollLeft = 0;
      }

      this.resetPagination();

      this.showMenu = false;

      if(this.selectedNote && this.selectedNote.dummy) {
        if(oldTag) {
          _.remove(oldTag.notes, this.selectedNote);
        }
      }

      this.noteFilter.text = "";
      this.setNotes(tag.notes);
    }

    this.setNotes = function(notes) {
      notes.forEach(function(note){
        note.visible = true;
      })

      var createNew = notes.length == 0;
      this.selectFirstNote(createNew);
    }

    this.visibleNotes = function() {
      return this.sortedNotes.filter(function(note){
        return note.visible;
      });
    }

    this.selectFirstNote = function(createNew) {
      var visibleNotes = this.visibleNotes();

      if(visibleNotes.length > 0) {
        this.selectNote(visibleNotes[0]);
      } else if(createNew) {
        this.createNewNote();
      }
    }

    this.selectNote = function(note, viaClick = false) {
      if(!note) {
        this.createNewNote();
        return;
      }
      this.selectedNote = note;
      note.conflict_of = null; // clear conflict
      this.selectionMade()(note);
      this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);

      if(viaClick && this.isFiltering()) {
        desktopManager.searchText(this.noteFilter.text);
      }
    }

    this.isFiltering = function() {
      return this.noteFilter.text && this.noteFilter.text.length > 0;
    }

    this.createNewNote = function() {
      var title = "New Note" + (this.tag.notes ? (" " + (this.tag.notes.length + 1)) : "");
      let newNote = modelManager.createItem({content_type: "Note", content: {text: "", title: title}});
      newNote.dummy = true;
      this.newNote = newNote;
      this.selectNote(this.newNote);
      this.addNew()(this.newNote);
    }

    this.noteFilter = {text : ''};

    this.filterNotes = function(note) {
      var isSmartTag = this.tag.isSmartTag();

      if((!isSmartTag && note.archived && !this.showArchived && !this.tag.archiveTag) || (note.pinned && this.hidePinned)) {
        note.visible = false;
        return note.visible;
      }

      var filterText = this.noteFilter.text.toLowerCase();
      if(filterText.length == 0) {
        note.visible = true;
      } else {
        var words = filterText.split(" ");
        var matchesTitle = words.every(function(word) { return  note.safeTitle().toLowerCase().indexOf(word) >= 0; });
        var matchesBody = words.every(function(word) { return  note.safeText().toLowerCase().indexOf(word) >= 0; });
        note.visible = matchesTitle || matchesBody;
      }

      if(this.tag.archiveTag) {
        note.visible = note.visible && note.archived;
      }

      return note.visible;
    }.bind(this)

    this.onFilterEnter = function() {
      // For Desktop, performing a search right away causes input to lose focus.
      // We wait until user explicity hits enter before highlighting desktop search results.
      desktopManager.searchText(this.noteFilter.text);
    }

    this.clearFilterText = function() {
      this.noteFilter.text = '';
      this.onFilterEnter();
      this.filterTextChanged();

      // Reset loaded notes
      this.resetPagination();
    }

    this.filterTextChanged = function() {
      $timeout(function(){
        if(!this.selectedNote.visible) {
          this.selectFirstNote(false);
        }
      }.bind(this), 100)
    }

    this.selectedMenuItem = function($event) {
      this.showMenu = false;
    }

    this.selectedSortByCreated = function() {
      this.setSortBy("created_at");
      this.sortDescending = true;
    }

    this.selectedSortByUpdated = function() {
      this.setSortBy("client_updated_at");
      this.sortDescending = true;
    }

    this.selectedSortByTitle = function() {
      this.setSortBy("title");
      this.sortDescending = false;
    }

    this.setSortBy = function(type) {
      this.sortBy = type;
      authManager.setUserPrefValue("sortBy", this.sortBy);
      authManager.syncUserPreferences();
    }

    this.shouldShowTags = function(note) {
      if(this.hideTags) {
        return false;
      }

      if(this.tag.all) {
        return true;
      }

      // Inside a tag, only show tags string if note contains tags other than this.tag
      return note.tags && note.tags.length > 1;
    }

  });
<|endoftext|>


<commit_before>angular.module('app')
  .directive("notesSection", function(){
    return {
      scope: {
        addNew: "&",
        selectionMade: "&",
        tag: "="
      },

      templateUrl: 'notes.html',
      replace: true,
      controller: 'NotesCtrl',
      controllerAs: 'ctrl',
      bindToController: true,

      link:function(scope, elem, attrs, ctrl) {
        scope.$watch('ctrl.tag', (tag, oldTag) => {
          if(tag) {
            if(tag.needsLoad) {
              scope.$watch('ctrl.tag.didLoad', function(didLoad){
                if(didLoad) {
                  tag.needsLoad = false;
                  ctrl.tagDidChange(tag, oldTag);
                }
              });
            } else {
              ctrl.tagDidChange(tag, oldTag);
            }
          }
        });
      }
    }
  })
  .controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager, desktopManager) {

    this.panelController = {};

    $rootScope.$on("user-preferences-changed", () => {
      this.loadPreferences();
    });

    this.loadPreferences = function() {
      let prevSortValue = this.sortBy;

      this.sortBy = authManager.getUserPrefValue("sortBy", "created_at");

      if(this.sortBy == "updated_at") {
        // use client_updated_at instead
        this.sortBy = "client_updated_at";
      }

      if(prevSortValue && prevSortValue != this.sortBy) {
        $timeout(() => {
          this.selectFirstNote();
        })
      }
      this.sortDescending = this.sortBy != "title";

      this.showArchived = authManager.getUserPrefValue("showArchived", false);
      this.hidePinned = authManager.getUserPrefValue("hidePinned", false);
      this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false);
      this.hideDate = authManager.getUserPrefValue("hideDate", false);
      this.hideTags = authManager.getUserPrefValue("hideTags", false);

      let width = authManager.getUserPrefValue("notesPanelWidth");
      if(width) {
        this.panelController.setWidth(width);
      }
    }

    this.loadPreferences();

    this.onPanelResize = function(newWidth) {
      authManager.setUserPrefValue("notesPanelWidth", newWidth);
      authManager.syncUserPreferences();
    }

    angular.element(document).ready(() => {
      this.loadPreferences();
    });

    $rootScope.$on("editorFocused", function(){
      this.showMenu = false;
    }.bind(this))

    $rootScope.$on("noteDeleted", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this))

    $rootScope.$on("noteArchived", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this));

    // When a note is removed from the list
    this.onNoteRemoval = function() {
      let visibleNotes = this.visibleNotes();
      if(this.selectedIndex < visibleNotes.length) {
        this.selectNote(visibleNotes[Math.max(this.selectedIndex, 0)]);
      } else {
        this.selectNote(visibleNotes[visibleNotes.length - 1]);
      }
    }

    let MinNoteHeight = 51.0; // This is the height of a note cell with nothing but the title, which *is* a display option
    this.DefaultNotesToDisplayValue = (document.documentElement.clientHeight / MinNoteHeight) || 20;

    this.paginate = function() {
      this.notesToDisplay += this.DefaultNotesToDisplayValue
    }

    this.resetPagination = function() {
      this.notesToDisplay = this.DefaultNotesToDisplayValue;
    }

    this.resetPagination();

    this.panelTitle = function() {
      if(this.isFiltering()) {
        return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
      } else if(this.tag) {
        return `${this.tag.title} notes`;
      }
    }

    this.optionsSubtitle = function() {
      var base = "";
      if(this.sortBy == "created_at") {
        base += " Date Added";
      } else if(this.sortBy == "client_updated_at") {
        base += " Date Modified";
      } else if(this.sortBy == "title") {
        base += " Title";
      }

      if(!this.tag || !this.tag.isSmartTag()) {
        // These rules don't apply for smart tags
        if(this.showArchived) {
          base += " | + Archived"
        }
        if(this.hidePinned) {
          base += " | – Pinned"
        }
      }

      return base;
    }

    this.toggleKey = function(key) {
      this[key] = !this[key];
      authManager.setUserPrefValue(key, this[key]);
      authManager.syncUserPreferences();
    }

    this.tagDidChange = function(tag, oldTag) {
      var scrollable = document.getElementById("notes-scrollable");
      if(scrollable) {
        scrollable.scrollTop = 0;
        scrollable.scrollLeft = 0;
      }

      this.resetPagination();

      this.showMenu = false;

      if(this.selectedNote && this.selectedNote.dummy) {
        if(oldTag) {
          _.remove(oldTag.notes, this.selectedNote);
        }
      }

      this.noteFilter.text = "";
      this.setNotes(tag.notes);
    }

    this.setNotes = function(notes) {
      notes.forEach((note) => {
        note.visible = true;
      })

      var createNew = this.visibleNotes().length == 0;
      this.selectFirstNote(createNew);
    }

    this.visibleNotes = function() {
      return this.sortedNotes.filter(function(note){
        return note.visible;
      });
    }

    this.selectFirstNote = function(createNew) {
      var visibleNotes = this.visibleNotes();

      if(visibleNotes.length > 0) {
        this.selectNote(visibleNotes[0]);
      } else if(createNew) {
        this.createNewNote();
      }
    }

    this.selectNote = function(note, viaClick = false) {
      if(!note) {
        this.createNewNote();
        return;
      }

      this.selectedNote = note;
      note.conflict_of = null; // clear conflict
      this.selectionMade()(note);
      this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);

      if(viaClick && this.isFiltering()) {
        desktopManager.searchText(this.noteFilter.text);
      }
    }

    this.isFiltering = function() {
      return this.noteFilter.text && this.noteFilter.text.length > 0;
    }

    this.createNewNote = function() {
      // The "Note X" counter is based off this.tag.notes.length, but sometimes, what you see in the list is only a subset.
      // We can use this.visibleNotes().length, but that only accounts for non-paginated results, so first 15 or so.
      var title = "Note" + (this.tag.notes ? (" " + (this.tag.notes.length + 1)) : "");
      let newNote = modelManager.createItem({content_type: "Note", content: {text: "", title: title}});
      newNote.dummy = true;
      this.newNote = newNote;
      this.selectNote(this.newNote);
      this.addNew()(this.newNote);
    }

    this.noteFilter = {text : ''};

    this.filterNotes = function(note) {
      var canShowArchived = false, canShowPinned = true;
      var isSmartTag = this.tag.isSmartTag();
      if(isSmartTag) {
        canShowArchived = this.tag.isReferencingArchivedNotes();
      } else {
        canShowArchived = this.showArchived;
        canShowPinned = !this.hidePinned;
      }

      if((note.archived && !canShowArchived) || (note.pinned && !canShowPinned)) {
        note.visible = false;
        return note.visible;
      }

      var filterText = this.noteFilter.text.toLowerCase();
      if(filterText.length == 0) {
        note.visible = true;
      } else {
        var words = filterText.split(" ");
        var matchesTitle = words.every(function(word) { return  note.safeTitle().toLowerCase().indexOf(word) >= 0; });
        var matchesBody = words.every(function(word) { return  note.safeText().toLowerCase().indexOf(word) >= 0; });
        note.visible = matchesTitle || matchesBody;
      }

      return note.visible;
    }.bind(this)

    this.onFilterEnter = function() {
      // For Desktop, performing a search right away causes input to lose focus.
      // We wait until user explicity hits enter before highlighting desktop search results.
      desktopManager.searchText(this.noteFilter.text);
    }

    this.clearFilterText = function() {
      this.noteFilter.text = '';
      this.onFilterEnter();
      this.filterTextChanged();

      // Reset loaded notes
      this.resetPagination();
    }

    this.filterTextChanged = function() {
      $timeout(function(){
        if(!this.selectedNote.visible) {
          this.selectFirstNote(false);
        }
      }.bind(this), 100)
    }

    this.selectedMenuItem = function() {
      this.showMenu = false;
    }

    this.selectedSortByCreated = function() {
      this.setSortBy("created_at");
      this.sortDescending = true;
    }

    this.selectedSortByUpdated = function() {
      this.setSortBy("client_updated_at");
      this.sortDescending = true;
    }

    this.selectedSortByTitle = function() {
      this.setSortBy("title");
      this.sortDescending = false;
    }

    this.setSortBy = function(type) {
      this.sortBy = type;
      authManager.setUserPrefValue("sortBy", this.sortBy);
      authManager.syncUserPreferences();
    }

    this.shouldShowTags = function(note) {
      if(this.hideTags) {
        return false;
      }

      if(this.tag.all) {
        return true;
      }

      // Inside a tag, only show tags string if note contains tags other than this.tag
      return note.tags && note.tags.length > 1;
    }

  });
<commit_msg>fix(desktop): search highlight on pagination<commit_after>angular.module('app')
  .directive("notesSection", function(){
    return {
      scope: {
        addNew: "&",
        selectionMade: "&",
        tag: "="
      },

      templateUrl: 'notes.html',
      replace: true,
      controller: 'NotesCtrl',
      controllerAs: 'ctrl',
      bindToController: true,

      link:function(scope, elem, attrs, ctrl) {
        scope.$watch('ctrl.tag', (tag, oldTag) => {
          if(tag) {
            if(tag.needsLoad) {
              scope.$watch('ctrl.tag.didLoad', function(didLoad){
                if(didLoad) {
                  tag.needsLoad = false;
                  ctrl.tagDidChange(tag, oldTag);
                }
              });
            } else {
              ctrl.tagDidChange(tag, oldTag);
            }
          }
        });
      }
    }
  })
  .controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager, storageManager, desktopManager) {

    this.panelController = {};
    this.searchEntered = false;

    $rootScope.$on("user-preferences-changed", () => {
      this.loadPreferences();
    });

    this.loadPreferences = function() {
      let prevSortValue = this.sortBy;

      this.sortBy = authManager.getUserPrefValue("sortBy", "created_at");

      if(this.sortBy == "updated_at") {
        // use client_updated_at instead
        this.sortBy = "client_updated_at";
      }

      if(prevSortValue && prevSortValue != this.sortBy) {
        $timeout(() => {
          this.selectFirstNote();
        })
      }
      this.sortDescending = this.sortBy != "title";

      this.showArchived = authManager.getUserPrefValue("showArchived", false);
      this.hidePinned = authManager.getUserPrefValue("hidePinned", false);
      this.hideNotePreview = authManager.getUserPrefValue("hideNotePreview", false);
      this.hideDate = authManager.getUserPrefValue("hideDate", false);
      this.hideTags = authManager.getUserPrefValue("hideTags", false);

      let width = authManager.getUserPrefValue("notesPanelWidth");
      if(width) {
        this.panelController.setWidth(width);
      }
    }

    this.loadPreferences();

    this.onPanelResize = function(newWidth) {
      authManager.setUserPrefValue("notesPanelWidth", newWidth);
      authManager.syncUserPreferences();
    }

    angular.element(document).ready(() => {
      this.loadPreferences();
    });

    $rootScope.$on("editorFocused", function(){
      this.showMenu = false;
    }.bind(this))

    $rootScope.$on("noteDeleted", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this))

    $rootScope.$on("noteArchived", function() {
      $timeout(this.onNoteRemoval.bind(this));
    }.bind(this));

    // When a note is removed from the list
    this.onNoteRemoval = function() {
      let visibleNotes = this.visibleNotes();
      if(this.selectedIndex < visibleNotes.length) {
        this.selectNote(visibleNotes[Math.max(this.selectedIndex, 0)]);
      } else {
        this.selectNote(visibleNotes[visibleNotes.length - 1]);
      }
    }

    let MinNoteHeight = 51.0; // This is the height of a note cell with nothing but the title, which *is* a display option
    this.DefaultNotesToDisplayValue = (document.documentElement.clientHeight / MinNoteHeight) || 20;

    this.paginate = function() {
      if (this.searchEntered) {
        desktopManager.searchText(this.noteFilter.text);
      }

      this.notesToDisplay += this.DefaultNotesToDisplayValue
    }

    this.resetPagination = function() {
      this.notesToDisplay = this.DefaultNotesToDisplayValue;
    }

    this.resetPagination();

    this.panelTitle = function() {
      if(this.isFiltering()) {
        return `${this.tag.notes.filter((i) => {return i.visible;}).length} search results`;
      } else if(this.tag) {
        return `${this.tag.title} notes`;
      }
    }

    this.optionsSubtitle = function() {
      var base = "";
      if(this.sortBy == "created_at") {
        base += " Date Added";
      } else if(this.sortBy == "client_updated_at") {
        base += " Date Modified";
      } else if(this.sortBy == "title") {
        base += " Title";
      }

      if(!this.tag || !this.tag.isSmartTag()) {
        // These rules don't apply for smart tags
        if(this.showArchived) {
          base += " | + Archived"
        }
        if(this.hidePinned) {
          base += " | – Pinned"
        }
      }

      return base;
    }

    this.toggleKey = function(key) {
      this[key] = !this[key];
      authManager.setUserPrefValue(key, this[key]);
      authManager.syncUserPreferences();
    }

    this.tagDidChange = function(tag, oldTag) {
      var scrollable = document.getElementById("notes-scrollable");
      if(scrollable) {
        scrollable.scrollTop = 0;
        scrollable.scrollLeft = 0;
      }

      this.resetPagination();

      this.showMenu = false;

      if(this.selectedNote && this.selectedNote.dummy) {
        if(oldTag) {
          _.remove(oldTag.notes, this.selectedNote);
        }
      }

      this.noteFilter.text = "";
      this.setNotes(tag.notes);
    }

    this.setNotes = function(notes) {
      notes.forEach((note) => {
        note.visible = true;
      })

      var createNew = this.visibleNotes().length == 0;
      this.selectFirstNote(createNew);
    }

    this.visibleNotes = function() {
      return this.sortedNotes.filter(function(note){
        return note.visible;
      });
    }

    this.selectFirstNote = function(createNew) {
      var visibleNotes = this.visibleNotes();

      if(visibleNotes.length > 0) {
        this.selectNote(visibleNotes[0]);
      } else if(createNew) {
        this.createNewNote();
      }
    }

    this.selectNote = function(note, viaClick = false) {
      if(!note) {
        this.createNewNote();
        return;
      }

      this.selectedNote = note;
      note.conflict_of = null; // clear conflict
      this.selectionMade()(note);
      this.selectedIndex = Math.max(this.visibleNotes().indexOf(note), 0);

      if(viaClick && this.isFiltering()) {
        desktopManager.searchText(this.noteFilter.text);
      }
    }

    this.isFiltering = function() {
      return this.noteFilter.text && this.noteFilter.text.length > 0;
    }

    this.createNewNote = function() {
      // The "Note X" counter is based off this.tag.notes.length, but sometimes, what you see in the list is only a subset.
      // We can use this.visibleNotes().length, but that only accounts for non-paginated results, so first 15 or so.
      var title = "Note" + (this.tag.notes ? (" " + (this.tag.notes.length + 1)) : "");
      let newNote = modelManager.createItem({content_type: "Note", content: {text: "", title: title}});
      newNote.dummy = true;
      this.newNote = newNote;
      this.selectNote(this.newNote);
      this.addNew()(this.newNote);
    }

    this.noteFilter = {text : ''};

    this.filterNotes = function(note) {
      var canShowArchived = false, canShowPinned = true;
      var isSmartTag = this.tag.isSmartTag();
      if(isSmartTag) {
        canShowArchived = this.tag.isReferencingArchivedNotes();
      } else {
        canShowArchived = this.showArchived;
        canShowPinned = !this.hidePinned;
      }

      if((note.archived && !canShowArchived) || (note.pinned && !canShowPinned)) {
        note.visible = false;
        return note.visible;
      }

      var filterText = this.noteFilter.text.toLowerCase();
      if(filterText.length == 0) {
        note.visible = true;
      } else {
        var words = filterText.split(" ");
        var matchesTitle = words.every(function(word) { return  note.safeTitle().toLowerCase().indexOf(word) >= 0; });
        var matchesBody = words.every(function(word) { return  note.safeText().toLowerCase().indexOf(word) >= 0; });
        note.visible = matchesTitle || matchesBody;
      }

      return note.visible;
    }.bind(this)

    this.onFilterEnter = function() {
      // For Desktop, performing a search right away causes input to lose focus.
      // We wait until user explicity hits enter before highlighting desktop search results.
      this.searchEntered = true;
      desktopManager.searchText(this.noteFilter.text);
    }

    this.clearFilterText = function() {
      this.noteFilter.text = '';
      this.onFilterEnter();
      this.filterTextChanged();

      // Reset loaded notes
      this.resetPagination();
    }

    this.filterTextChanged = function() {
      if (this.searchEntered) {
        this.searchEntered = false;
      }

      $timeout(function(){
        if(!this.selectedNote.visible) {
          this.selectFirstNote(false);
        }
      }.bind(this), 100)
    }

    this.selectedMenuItem = function() {
      this.showMenu = false;
    }

    this.selectedSortByCreated = function() {
      this.setSortBy("created_at");
      this.sortDescending = true;
    }

    this.selectedSortByUpdated = function() {
      this.setSortBy("client_updated_at");
      this.sortDescending = true;
    }

    this.selectedSortByTitle = function() {
      this.setSortBy("title");
      this.sortDescending = false;
    }

    this.setSortBy = function(type) {
      this.sortBy = type;
      authManager.setUserPrefValue("sortBy", this.sortBy);
      authManager.syncUserPreferences();
    }

    this.shouldShowTags = function(note) {
      if(this.hideTags) {
        return false;
      }

      if(this.tag.all) {
        return true;
      }

      // Inside a tag, only show tags string if note contains tags other than this.tag
      return note.tags && note.tags.length > 1;
    }

  });
<|endoftext|>


<commit_before>import React from 'react';
import {Button, Checkbox, FormGroup, FormControl, InputGroup, ControlLabel, Glyphicon, Row, Col} from 'react-bootstrap';

import NumeralInputWithUnitsCompo from './NumeralInputWithUnitsCompo';
import Select from 'react-select';

import {solventOptions} from './staticDropdownOptions/options';

export default class SampleForm extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      sample: props.sample
    }
  }

  handleAmountChanged(amount) {
    let sample = this.state.sample;
    sample.setAmountAndNormalizeToGram(amount);
    this.setState({
      sample: sample
    });
  }

  handleUnitChanged(unit, nextUnit, value) {
    let convertedValue = value;
    if(unit && nextUnit && unit != nextUnit) {
      switch(unit) {
        case 'g':
          if(nextUnit == 'mol') {
            convertedValue = value * 2;
          }
          break;
        case 'mol':
          if(nextUnit == 'g') {
            convertedValue = value / 2;
          }
          break;
      }
    }
    return convertedValue;
  }

  showStructureEditor() {
    this.props.parent.setState({
      showStructureEditor: true
    })
  }

  structureEditorButton(isDisabled) {
    return (
      <Button onClick={this.showStructureEditor.bind(this)} disabled={isDisabled}>
        <Glyphicon glyph='pencil'/>
      </Button>
    )
  }

  // Input components of sample details should be disabled if detail level does not allow to read their content
  topSecretCheckbox(sample) {
    if(!sample.isMethodDisabled('is_top_secret')) {
      return (
        <Checkbox ref="topSecretInput"
        checked={sample.is_top_secret}
        onChange={(e) => this.handleFieldChanged(sample, 'is_top_secret', e.target.checked)}
        >Top secret</Checkbox>
      )
    }
  }

  moleculeInput(sample) {
    return (
      <FormGroup>
        <ControlLabel>Molecule</ControlLabel>
        <InputGroup>
          <FormControl type="text" ref="moleculeInput"
            value={sample.molecule && (sample.molecule.iupac_name || sample.molecule.sum_formular || '')}
            disabled={sample.isMethodDisabled('molecule_iupac_name')}
            readOnly={sample.isMethodDisabled('molecule_iupac_name')}
            onChange={(e) => this.handleFieldChanged(sample, 'molecule_iupac_name', e.target.value)}
          />
          <InputGroup.Button>
            {this.structureEditorButton(sample.isMethodDisabled('molecule_iupac_name'))}
          </InputGroup.Button>
        </InputGroup>
      </FormGroup>
    )
  }

  handleFieldChanged(sample, field, e) {
    if(/amount/.test(field))
      this.handleAmountChanged(e);
    else if (e && e.value) // for numeric inputs
      sample[field] = e.value;
    else
      sample[field] = e;

    this.props.parent.setState({
      sample: sample
    });
  }

  textInput(sample, field, label, disabled = false) {
    return (
      <FormGroup>
        <ControlLabel>{label}</ControlLabel>
        <FormControl type="text"
          value={sample[field] || ''}
          onChange={(e) => {this.handleFieldChanged(sample, field, e.target.value)}}
          disabled={disabled || sample.isMethodDisabled(field)}
          readOnly={disabled || sample.isMethodDisabled(field)}
        />
      </FormGroup>
    )
  }

  sampleSolvent(sample) {
    return (
      <Select ref='solventInput'
              name='solvents'
              multi={false}
              options={solventOptions}
              onChange={(e) => this.handleFieldChanged(sample, 'solvent', e)}
              value={sample.solvent}
              disabled={sample.isMethodDisabled('solvent')}
      />
    )
  }

  attachedAmountInput(sample, size) {
    if(!sample.contains_residues)
      return false;

    return this.numInput(sample, 'defined_part_amount', 'g',
    ['milli','none'], 4, 'Attached', 'attachedAmountMg', true, "Weight of the defined part", size)
  }

  numInput(sample, field, unit, prefixes, precision, label, ref = '', disabled = false, title='', size=2) {
    if(sample.contains_residues && unit == 'l')
      return false;

    return (
      <Col md={size} key={field + sample.id.toString()}>
        <NumeralInputWithUnitsCompo
          value={!isNaN(sample[field]) ? sample[field] : null}
          unit={unit}
          label={label}
          ref={ref}
          metricPrefix={prefixes[0]}
          metricPrefixes = {prefixes}
          precision={precision}
          title={title}
          disabled={disabled}
          onChange={(e) => this.handleFieldChanged(sample, field, e)}
          />
      </Col>
    )
  }

  sampleAmount(sample, inputsSize=2) {
    let content = [];
    if(sample.isMethodDisabled('amount_value') == false) {
      if(sample.isMethodRestricted('molecule') == true) {
        content.push(
          this.numInput(sample, 'amount_g', 'g',['milli','none'], 4, 'Amount', 'massMgInput', false, '', inputsSize)
        )
      } else {
        content.push(
          this.numInput(sample, 'amount_g', 'g',['milli','none'], 4, 'Amount', 'massMgInput', false, '', inputsSize)
        )

        if(!sample.contains_residues)
          content.push(
            this.numInput(sample, 'amount_l', 'l', ['milli','micro','none'], 5, '\u202F', 'l', false, '', inputsSize)
          )

        content.push(
          this.numInput(sample, 'amount_mol', 'mol', ['milli','none'], 4, '\u202F', 'amountInput', false, '', inputsSize)
        )

        if(sample.contains_residues)
          content.push(
            this.attachedAmountInput(sample, inputsSize)
          )
      }
     return content;
    } else {
      return (
        <FormGroup>
          <ControlLabel>Amount</ControlLabel>
          <FormControl type="text" disabled defaultValue="***" readOnly/>
        </FormGroup>
      )
    }
  }

  sampleDescription(sample) {
    return (
      <FormGroup>
        <ControlLabel>Description</ControlLabel>
        <FormControl componentClass="textarea"  ref="descriptionInput"
             placeholder={sample.description}
             value={sample.description || ''}
             onChange={(e) => this.handleFieldChanged(sample, 'description', e.target.value)}
             rows={2}
             disabled={sample.isMethodDisabled('description')}
        />
      </FormGroup>
    )
  }

  render() {
    let sample = this.state.sample || {}

    return (

        <Row>
          <Col md={6}>{this.moleculeInput(sample)}</Col>
          <Col md={6} className="top-secret-checkbox">
            {this.topSecretCheckbox(sample)}
          </Col>
        </Row>

        <Row>
          <Col md={4}>{this.textInput(sample, 'name', 'Name')}</Col>
          <Col md={4}>
            {this.textInput(sample, 'external_label', 'External label')}
          </Col>
          <Col md={4}>{this.textInput(sample, 'location', 'Location')}</Col>
        </Row>

        <Row className="visible-hd">
          {this.sampleAmount(sample)}
          {this.numInput(sample, 'density', 'g/ml', ['none'], 5, 'Density')}
          {this.numInput(sample, 'boiling_point', '°C', ['none'], 5, 'Boiling point')}
          {this.numInput(sample, 'melting_point', '°C', ['none'], 5, 'Melting point')}
        </Row>

        <Row className="hidden-hd">
          {this.sampleAmount(sample, 4)}
        </Row>
        <Row className="hidden-hd">
          {this.numInput(sample, 'density', 'g/ml', ['none'], 5, 'Density', '', false, '', 4)}
          {this.numInput(sample, 'boiling_point', '°C', ['none'], 5, 'Boiling point', '', false, '', 4)}
          {this.numInput(sample, 'melting_point', '°C', ['none'], 5, 'Melting point', '', false, '', 4)}
        </Row>

        <Row>
          <Col md={4}>{this.sampleDescription(sample)}</Col>
          {this.numInput(sample, 'purity', 'none', ['none'], 5, 'Purity')}
          <Col md={2}>{this.textInput(sample, 'impurities', 'Impurities')}</Col>
          <Col md={4}>
            Solvent
            {this.sampleSolvent(sample)}
          </Col>
        </Row>

    )
  }
}
<commit_msg>Disabled polymer density and melting-point<commit_after>import React from 'react';
import {Button, Checkbox, FormGroup, FormControl, InputGroup, ControlLabel, Glyphicon, Row, Col} from 'react-bootstrap';

import NumeralInputWithUnitsCompo from './NumeralInputWithUnitsCompo';
import Select from 'react-select';

import {solventOptions} from './staticDropdownOptions/options';

export default class SampleForm extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      sample: props.sample
    }
  }

  handleAmountChanged(amount) {
    let sample = this.state.sample;
    sample.setAmountAndNormalizeToGram(amount);
    this.setState({
      sample: sample
    });
  }

  handleUnitChanged(unit, nextUnit, value) {
    let convertedValue = value;
    if(unit && nextUnit && unit != nextUnit) {
      switch(unit) {
        case 'g':
          if(nextUnit == 'mol') {
            convertedValue = value * 2;
          }
          break;
        case 'mol':
          if(nextUnit == 'g') {
            convertedValue = value / 2;
          }
          break;
      }
    }
    return convertedValue;
  }

  showStructureEditor() {
    this.props.parent.setState({
      showStructureEditor: true
    })
  }

  structureEditorButton(isDisabled) {
    return (
      <Button onClick={this.showStructureEditor.bind(this)} disabled={isDisabled}>
        <Glyphicon glyph='pencil'/>
      </Button>
    )
  }

  // Input components of sample details should be disabled if detail level does not allow to read their content
  topSecretCheckbox(sample) {
    if(!sample.isMethodDisabled('is_top_secret')) {
      return (
        <Checkbox ref="topSecretInput"
        checked={sample.is_top_secret}
        onChange={(e) => this.handleFieldChanged(sample, 'is_top_secret', e.target.checked)}
        >Top secret</Checkbox>
      )
    }
  }

  moleculeInput(sample) {
    return (
      <FormGroup>
        <ControlLabel>Molecule</ControlLabel>
        <InputGroup>
          <FormControl type="text" ref="moleculeInput"
            value={sample.molecule && (sample.molecule.iupac_name || sample.molecule.sum_formular || '')}
            disabled={sample.isMethodDisabled('molecule_iupac_name')}
            readOnly={sample.isMethodDisabled('molecule_iupac_name')}
            onChange={(e) => this.handleFieldChanged(sample, 'molecule_iupac_name', e.target.value)}
          />
          <InputGroup.Button>
            {this.structureEditorButton(sample.isMethodDisabled('molecule_iupac_name'))}
          </InputGroup.Button>
        </InputGroup>
      </FormGroup>
    )
  }

  handleFieldChanged(sample, field, e) {
    if(/amount/.test(field))
      this.handleAmountChanged(e);
    else if (e && e.value) // for numeric inputs
      sample[field] = e.value;
    else
      sample[field] = e;

    this.props.parent.setState({
      sample: sample
    });
  }

  textInput(sample, field, label, disabled = false) {
    return (
      <FormGroup>
        <ControlLabel>{label}</ControlLabel>
        <FormControl type="text"
          value={sample[field] || ''}
          onChange={(e) => {this.handleFieldChanged(sample, field, e.target.value)}}
          disabled={disabled || sample.isMethodDisabled(field)}
          readOnly={disabled || sample.isMethodDisabled(field)}
        />
      </FormGroup>
    )
  }

  sampleSolvent(sample) {
    return (
      <Select ref='solventInput'
              name='solvents'
              multi={false}
              options={solventOptions}
              onChange={(e) => this.handleFieldChanged(sample, 'solvent', e)}
              value={sample.solvent}
              disabled={sample.isMethodDisabled('solvent')}
      />
    )
  }

  attachedAmountInput(sample, size) {
    if(!sample.contains_residues)
      return false;

    return this.numInput(sample, 'defined_part_amount', 'g',
    ['milli','none'], 4, 'Attached', 'attachedAmountMg', true, "Weight of the defined part", size)
  }

  numInput(sample, field, unit, prefixes, precision, label, ref = '', disabled = false, title='', size=2) {
    if(sample.contains_residues && unit == 'l')
      return false;

    return (
      <Col md={size} key={field + sample.id.toString()}>
        <NumeralInputWithUnitsCompo
          value={!isNaN(sample[field]) ? sample[field] : null}
          unit={unit}
          label={label}
          ref={ref}
          metricPrefix={prefixes[0]}
          metricPrefixes = {prefixes}
          precision={precision}
          title={title}
          disabled={disabled}
          onChange={(e) => this.handleFieldChanged(sample, field, e)}
          />
      </Col>
    )
  }

  sampleAmount(sample, inputsSize=2) {
    let content = [];
    if(sample.isMethodDisabled('amount_value') == false) {
      if(sample.isMethodRestricted('molecule') == true) {
        content.push(
          this.numInput(sample, 'amount_g', 'g',['milli','none'], 4, 'Amount', 'massMgInput', false, '', inputsSize)
        )
      } else {
        content.push(
          this.numInput(sample, 'amount_g', 'g',['milli','none'], 4, 'Amount', 'massMgInput', false, '', inputsSize)
        )

        if(!sample.contains_residues)
          content.push(
            this.numInput(sample, 'amount_l', 'l', ['milli','micro','none'], 5, '\u202F', 'l', false, '', inputsSize)
          )

        content.push(
          this.numInput(sample, 'amount_mol', 'mol', ['milli','none'], 4, '\u202F', 'amountInput', false, '', inputsSize)
        )

        if(sample.contains_residues)
          content.push(
            this.attachedAmountInput(sample, inputsSize)
          )
      }
     return content;
    } else {
      return (
        <FormGroup>
          <ControlLabel>Amount</ControlLabel>
          <FormControl type="text" disabled defaultValue="***" readOnly/>
        </FormGroup>
      )
    }
  }

  sampleDescription(sample) {
    return (
      <FormGroup>
        <ControlLabel>Description</ControlLabel>
        <FormControl componentClass="textarea"  ref="descriptionInput"
             placeholder={sample.description}
             value={sample.description || ''}
             onChange={(e) => this.handleFieldChanged(sample, 'description', e.target.value)}
             rows={2}
             disabled={sample.isMethodDisabled('description')}
        />
      </FormGroup>
    )
  }

  render() {
    let sample = this.state.sample || {}
    let isPolymer = sample.molfile.indexOf(" R# ") !== -1

    return (

        <Row>
          <Col md={6}>{this.moleculeInput(sample)}</Col>
          <Col md={6} className="top-secret-checkbox">
            {this.topSecretCheckbox(sample)}
          </Col>
        </Row>

        <Row>
          <Col md={4}>{this.textInput(sample, 'name', 'Name')}</Col>
          <Col md={4}>
            {this.textInput(sample, 'external_label', 'External label')}
          </Col>
          <Col md={4}>{this.textInput(sample, 'location', 'Location')}</Col>
        </Row>

        <Row className="visible-hd">
          {this.sampleAmount(sample)}
          {this.numInput(sample, 'density', 'g/ml', ['none'], 5, 'Density', '', isPolymer)}
          {this.numInput(sample, 'boiling_point', '°C', ['none'], 5, 'Boiling point')}
          {this.numInput(sample, 'melting_point', '°C', ['none'], 5, 'Melting point', '', isPolymer)}
        </Row>

        <Row className="hidden-hd">
          {this.sampleAmount(sample, 4)}
        </Row>
        <Row className="hidden-hd">
          {this.numInput(sample, 'density', 'g/ml', ['none'], 5, 'Density', '', isPolymer, '', 4)}
          {this.numInput(sample, 'boiling_point', '°C', ['none'], 5, 'Boiling point', '', false, '', 4)}
          {this.numInput(sample, 'melting_point', '°C', ['none'], 5, 'Melting point', '', isPolymer, '', 4)}
        </Row>

        <Row>
          <Col md={4}>{this.sampleDescription(sample)}</Col>
          {this.numInput(sample, 'purity', 'none', ['none'], 5, 'Purity')}
          <Col md={2}>{this.textInput(sample, 'impurities', 'Impurities')}</Col>
          <Col md={4}>
            Solvent
            {this.sampleSolvent(sample)}
          </Col>
        </Row>

    )
  }
}
<|endoftext|>


<commit_before>/*global jQuery,cyberDojo*/

var cyberDojo = (function(cd, $) {
  "use strict";

  cd.syntaxHighlightEnabled = false;
  cd.syntaxHighlightTabSize = 4;

  var fileExtension = function (filename) {
    var lastPoint = filename.lastIndexOf('.');
    if (lastPoint == -1) {
      return '';
    }
    return filename.substring(lastPoint);
  };

  var codeMirrorMode = function (filename) {
    filename = filename.toLowerCase();

    switch (filename) {
      case 'makefile':
        return 'text/x-makefile';
      case 'instructions':
        return '';
    }

    switch (fileExtension(filename)) {
      case '.c':
        return 'text/x-csrc';
      case '.cpp':
        return 'text/x-c++src';
      case '.hpp':
      case '.h':
        return 'text/x-c++hdr';
      case '.java':
        return 'text/x-java';
      case '.cs':
        return 'text/x-csharp';
      case '.scala':
        return 'text/x-scala';
      case '.clj':
        return 'text/x-clojure';
      case '.coffee':
        return 'text/x-coffeescript';
      case '.d':
        return 'text/x-d';
      case '.feature':
        return 'text/x-feature';
      case '.js':
        return 'text/javascript';
      case '.php':
        return 'text/x-php';
      case '.py':
        return 'text/x-python';
      case '.rb':
        return 'text/x-ruby';
      case '.sh':
        return 'text/x-sh';
      case '.vb':
        return 'text/x-vb';
      case '.vhdl':
        return 'text/x-vhdl';
    }
    return '';
  };

  var syntaxHighlightFileContentForId = function (filename) {
    return 'syntax_highlight_file_content_for_' + filename;
  };

  var runActionOnAllCodeMirrorEditors = function(action) {
    $.each($('.CodeMirror'), function (i, editor_div) {
      action(editor_div.CodeMirror);
    });
  };

  var toggleLineNumbers = function(cm, lineNumber) {
    var noLineNumbersTheme = " cyber-dojo-no-linenumbers";
    runActionOnAllCodeMirrorEditors(function(editor) {
      var theme = editor.getOption("theme");

      if(theme.indexOf(noLineNumbersTheme) !== -1) {
        theme = theme.replace(noLineNumbersTheme, "");
      } else {
        theme += noLineNumbersTheme;
      }

      editor.setOption("theme", theme);
    });
  };

  cd.switchEditorToCodeMirror = function (filename) {
    var editor = CodeMirror.fromTextArea(document.getElementById('file_content_for_' + filename), {
      lineNumbers: true,
      matchBrackets: true,
      mode: codeMirrorMode(filename),
      indentUnit: cd.syntaxHighlightTabSize,
      tabSize: cd.syntaxHighlightTabSize,
      theme: "cyber-dojo-colour"
    });

    editor.on("gutterClick", toggleLineNumbers);

    editor.getWrapperElement().id = syntaxHighlightFileContentForId(filename);

    editor.setOption("extraKeys", {
      'Alt-T': function (cm) {
        $('#test-button').click();
      },
      'Alt-J': function (cm) {
        cd.loadNextFile();
      },
      'Alt-K': function (cm) {
        cd.loadPreviousFile();
      },
      'Alt-O': function (cm) {
        cd.toggleOutputFile();
      }
    });
    var lineNumbers = document.getElementById(filename + '_line_numbers');
    lineNumbers.style.display = 'none';
  };

  cd.switchAllEditorsToCodeMirror = function() {
    $.each($('.file_content'), function (i, editor_text_area) {
      var filename = editor_text_area.attributes['data-filename'].value;
      cd.switchEditorToCodeMirror(filename);
    });
  };

  cd.removeSyntaxHilightEditor = function (filename) {
    var element = document.getElementById(syntaxHighlightFileContentForId(filename));
    if (element != null) {
      element.CodeMirror.toTextArea();
    }
  };

  cd.focusSyntaxHighlightEditor = function (filename) {
    var element = document.getElementById(syntaxHighlightFileContentForId(filename));
    if (element != null) {
      element.CodeMirror.refresh();
      element.CodeMirror.focus();
    }
  };

  cd.saveCodeFromSyntaxHighlightEditors = function () {
    $.each($('.CodeMirror'), function (i, editor_div) {
      editor_div.CodeMirror.save();
    });
  };

  return cd;

})(cyberDojo || {}, jQuery);
<commit_msg>Made the CodeMirror editor for the output read only<commit_after>/*global jQuery,cyberDojo*/

var cyberDojo = (function(cd, $) {
  "use strict";

  cd.syntaxHighlightEnabled = false;
  cd.syntaxHighlightTabSize = 4;

  var fileExtension = function (filename) {
    var lastPoint = filename.lastIndexOf('.');
    if (lastPoint == -1) {
      return '';
    }
    return filename.substring(lastPoint);
  };

  var codeMirrorMode = function (filename) {
    filename = filename.toLowerCase();

    switch (filename) {
      case 'makefile':
        return 'text/x-makefile';
      case 'instructions':
        return '';
    }

    switch (fileExtension(filename)) {
      case '.c':
        return 'text/x-csrc';
      case '.cpp':
        return 'text/x-c++src';
      case '.hpp':
      case '.h':
        return 'text/x-c++hdr';
      case '.java':
        return 'text/x-java';
      case '.cs':
        return 'text/x-csharp';
      case '.scala':
        return 'text/x-scala';
      case '.clj':
        return 'text/x-clojure';
      case '.coffee':
        return 'text/x-coffeescript';
      case '.d':
        return 'text/x-d';
      case '.feature':
        return 'text/x-feature';
      case '.js':
        return 'text/javascript';
      case '.php':
        return 'text/x-php';
      case '.py':
        return 'text/x-python';
      case '.rb':
        return 'text/x-ruby';
      case '.sh':
        return 'text/x-sh';
      case '.vb':
        return 'text/x-vb';
      case '.vhdl':
        return 'text/x-vhdl';
    }
    return '';
  };

  var syntaxHighlightFileContentForId = function (filename) {
    return 'syntax_highlight_file_content_for_' + filename;
  };

  var runActionOnAllCodeMirrorEditors = function(action) {
    $.each($('.CodeMirror'), function (i, editor_div) {
      action(editor_div.CodeMirror);
    });
  };

  var toggleLineNumbers = function(cm, lineNumber) {
    var noLineNumbersTheme = " cyber-dojo-no-linenumbers";
    runActionOnAllCodeMirrorEditors(function(editor) {
      var theme = editor.getOption("theme");

      if(theme.indexOf(noLineNumbersTheme) !== -1) {
        theme = theme.replace(noLineNumbersTheme, "");
      } else {
        theme += noLineNumbersTheme;
      }

      editor.setOption("theme", theme);
    });
  };

  cd.switchEditorToCodeMirror = function (filename) {
    var editor = CodeMirror.fromTextArea(document.getElementById('file_content_for_' + filename), {
      lineNumbers: true,
      matchBrackets: true,
      mode: codeMirrorMode(filename),
      indentUnit: cd.syntaxHighlightTabSize,
      tabSize: cd.syntaxHighlightTabSize,
      theme: "cyber-dojo-colour",
      readOnly: (filename == 'output')
    });

    editor.on("gutterClick", toggleLineNumbers);

    editor.getWrapperElement().id = syntaxHighlightFileContentForId(filename);

    editor.setOption("extraKeys", {
      'Alt-T': function (cm) {
        $('#test-button').click();
      },
      'Alt-J': function (cm) {
        cd.loadNextFile();
      },
      'Alt-K': function (cm) {
        cd.loadPreviousFile();
      },
      'Alt-O': function (cm) {
        cd.toggleOutputFile();
      }
    });
    var lineNumbers = document.getElementById(filename + '_line_numbers');
    lineNumbers.style.display = 'none';
  };

  cd.switchAllEditorsToCodeMirror = function() {
    $.each($('.file_content'), function (i, editor_text_area) {
      var filename = editor_text_area.attributes['data-filename'].value;
      cd.switchEditorToCodeMirror(filename);
    });
  };

  cd.removeSyntaxHilightEditor = function (filename) {
    var element = document.getElementById(syntaxHighlightFileContentForId(filename));
    if (element != null) {
      element.CodeMirror.toTextArea();
    }
  };

  cd.focusSyntaxHighlightEditor = function (filename) {
    var element = document.getElementById(syntaxHighlightFileContentForId(filename));
    if (element != null) {
      element.CodeMirror.refresh();
      element.CodeMirror.focus();
    }
  };

  cd.saveCodeFromSyntaxHighlightEditors = function () {
    $.each($('.CodeMirror'), function (i, editor_div) {
      editor_div.CodeMirror.save();
    });
  };

  return cd;

})(cyberDojo || {}, jQuery);
<|endoftext|>