<commit_before>requirejs.config({
  baseUrl: 'bower_components',
  paths: {
    d3: 'd3/d3.min',
    queue: 'queue-async/queue.min',
    lodash: 'lodash/dist/lodash.min',
    jquery: 'jquery/dist/jquery.min',
    bootstrap: 'bootstrap/dist/js/bootstrap.min',
    async:     'requirejs-plugins/src/async',

    // internal components

    baseChart:  '../js/components/basechart',
    nestMap:    '../js/components/nestmap',
    nestDetail: '../js/components/nestdetail',
    nestChart:  '../js/components/nestchart',
    googleMap:  '../js/components/googlemap'
  },
  shim: {
    bootstrap: {deps: ['jquery']}
  }
});

define(['d3', 'jquery', 'queue', 'bootstrap', 'nestMap'], function (d3, $, queue, bs, NestMap) {

  var DATE_FORMAT = d3.time.format("%m/%d/%Y");
  var DATETIME_FORMAT = d3.time.format("%Y-%m-%d %H:%M:%S");
  var LAT_LONG_REGEX = /(\d{2,3})(\d{2}\.\d{4})([NESW]?)/g;

  // construct the map

  var nestMap = new NestMap($(".chart").get(0));

  queue()
   .defer(d3.csv, 'data/nests.csv')
   .defer(d3.csv, 'data/records.csv')
   .await(onData);

  function onData(error, nests, records) {
    if (error) {
      console.error("error", error);
      return;
    }

    // preprocess records

    records = records.map(transformRecord);

    // construct map of records grouped by nest ID

    var recordsMap = d3.nest()
      .key(function(d) {return +d.nest_id;})
      .sortValues(function(a, b) {return a.date - b.date;})
      .map(records);

    // preprocess nests

    nests = nests
      .map(transformNest)
      .filter(function(nest) {return recordsMap[nest.id] && nest.lng > -80;})
      .map(function(nest) {return nestJoinRecords(nest, recordsMap[nest.id]);})
      .sort(function(a, b) {return a.name.localeCompare(b.name);});

    nestMap.initialize(nests);
    populateMenu(nests);
    navigateHash(nests);
  }

  function nestJoinRecords(nest, records) {

    // group records by time

    recordsByTime = d3.nest()
      .key(function(d) {
        //return new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate(), d.date.getHours());
        return new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
      })
      .sortValues(function(a, b) {return a.date - b.date;})
      .entries(records);

    // establish mean temperatures during that time period

    var meanTempsByPeriod = recordsByTime.reduce(function(acc, period) {
      acc.push({
        date: new Date(period.key),
        temperature: d3.mean(period.values, function(d) {return d.temperature;})
      });
      return acc;
    }, []);

    // assign mean temperature to nest

    nest.records = meanTempsByPeriod;
    return nest;
  }

  function transformRecord(record) {
    record.date = DATETIME_FORMAT.parse(record.record_datetime);
    record.nest_id = +record.nest_id;
    record.temperature = +record.temperature;
    return record;
  }

  function transformNest(nest, i) {
    var newNest = {
      id: i,
      name: nest['Sensor #'] + ':' + nest['Comm ID #'] + '(' + i + ')',
      comm: nest['Comm ID #'],
      nestDate: DATE_FORMAT.parse(nest['Nest Date']),
      lat: +nest['Lat'],
      lng: +nest['Long']
    };

    console.log("newNest.nestDate", newNest.nestDate);

    return newNest;
  }

  function parseLatLong(ll) {
    var tokens = ll.split(LAT_LONG_REGEX);
    var degrees = parseInt(tokens[1]);
    var minutes = parseFloat(tokens[2]);
    var ordinal = tokens[3];
    var sign = (ordinal.length == 0 || "NW".indexOf(ordinal) < 0) ? -1 : 1;
    return sign * (degrees + (minutes * 60 / 3600));
  }

  function navigateHash(nests) {
    var nestName = window.location.hash.substr(1);
    nests.forEach(function(nest) {
      if (nestName.toLowerCase() == nest.name.toLowerCase()) {
        nestMap.zoomToNest(nest);
      }
    });
  }

  function populateMenu(nests) {
    $(".see-all").on("click", nestMap.zoomToAll);
    var picker = $(".nest-picker");
    nests.forEach(function(nest) {
      var a = $("<a/>").text(nest.name)
        .attr("href", "#" + nest.name.toLowerCase())
        .on("click", function() {nestMap.zoomToNest(nest);});
      var li = $("<li/>")
        .append(a);
      picker.append(li);
    });
  }

  function generateRandomTimeSeries(timeScale, valueScale, sampleCount, volitility) {
    volitility = volitility || .1;
    var volitilityScale = d3.scale.linear().range([-volitility, volitility]);
    var timeSeries = [];
    var normalValue = Math.random();

    // create samples

    for (var i = 0; i < sampleCount; ++i) {

      // compute value normal

      var dNormal = volitilityScale(Math.random());
      normalValue += dNormal;
      normalValue = d3.max([0, d3.min([1, normalValue])]);

      // create record

      timeSeries.push({
        time: timeScale.invert(i / (sampleCount - 1)),
        value: valueScale(normalValue)
      });
    }

    return timeSeries;
  }
});
<commit_msg>a little more clean up<commit_after>requirejs.config({
  baseUrl: 'bower_components',
  paths: {
    d3: 'd3/d3.min',
    queue: 'queue-async/queue.min',
    lodash: 'lodash/dist/lodash.min',
    jquery: 'jquery/dist/jquery.min',
    bootstrap: 'bootstrap/dist/js/bootstrap.min',
    async:     'requirejs-plugins/src/async',

    // internal components

    baseChart:  '../js/components/basechart',
    nestMap:    '../js/components/nestmap',
    nestDetail: '../js/components/nestdetail',
    nestChart:  '../js/components/nestchart',
    googleMap:  '../js/components/googlemap'
  },
  shim: {
    bootstrap: {deps: ['jquery']}
  }
});

define(['d3', 'jquery', 'queue', 'bootstrap', 'nestMap'], function (d3, $, queue, bs, NestMap) {

  var DATE_FORMAT = d3.time.format("%m/%d/%Y");
  var DATETIME_FORMAT = d3.time.format("%Y-%m-%d %H:%M:%S");
  var LAT_LONG_REGEX = /(\d{2,3})(\d{2}\.\d{4})([NESW]?)/g;

  // construct the map

  var nestMap = new NestMap($(".chart").get(0));

  queue()
    .defer(d3.csv, 'data/nests.csv')
    .defer(d3.csv, 'data/records.csv')
    .await(onData);

  function onData(error, nests, records) {
    if (error) {
      console.error("error", error);
      return;
    }

    // preprocess records

    records = records.map(transformRecord);

    // construct map of records grouped by nest ID

    var recordsMap = d3.nest()
      .key(function(d) {return +d.nest_id;})
      .sortValues(function(a, b) {return a.date - b.date;})
      .map(records);

    // preprocess nests

    nests = nests
      .map(transformNest)
      .filter(function(nest) {return nest.show;})
      .filter(function(nest) {return recordsMap[nest.id] && nest.lng > -80;})
      .map(function(nest) {return nestJoinRecords(nest, recordsMap[nest.id]);})
      .sort(function(a, b) {return a.name.localeCompare(b.name);});

    nestMap.initialize(nests);
    populateMenu(nests);
    navigateHash(nests);
  }

  function nestJoinRecords(nest, records) {

    // group records by time

    recordsByTime = d3.nest()
      .key(function(d) {
        //return new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate(), d.date.getHours());
        return new Date(d.date.getFullYear(), d.date.getMonth(), d.date.getDate());
      })
      .sortValues(function(a, b) {return a.date - b.date;})
      .entries(records);

    // establish mean temperatures during that time period

    var meanTempsByPeriod = recordsByTime.reduce(function(acc, period) {
      acc.push({
        date: new Date(period.key),
        temperature: d3.mean(period.values, function(d) {return d.temperature;})
      });
      return acc;
    }, []);

    // assign mean temperature to nest

    nest.records = meanTempsByPeriod;
    return nest;
  }

  function transformRecord(record) {
    record.date = DATETIME_FORMAT.parse(record.record_datetime);
    record.nest_id = +record.nest_id;
    record.temperature = +record.temperature;
    return record;
  }

  function transformNest(nest, i) {
    return {
      id: i,
      name: nest['Sensor #'] + ':' + nest['Comm ID #'] + '(' + i + ')',
      comm: nest['Comm ID #'],
      nestDate: DATE_FORMAT.parse(nest['Nest Date']),
      lat: +nest['Lat'],
      lng: +nest['Long'],
      show: nest['Show on Map'].toLowerCase() == 'yes'
    };
  }

  function parseLatLong(ll) {
    var tokens = ll.split(LAT_LONG_REGEX);
    var degrees = parseInt(tokens[1]);
    var minutes = parseFloat(tokens[2]);
    var ordinal = tokens[3];
    var sign = (ordinal.length == 0 || "NW".indexOf(ordinal) < 0) ? -1 : 1;
    return sign * (degrees + (minutes * 60 / 3600));
  }

  function navigateHash(nests) {
    var nestName = window.location.hash.substr(1);
    nests.forEach(function(nest) {
      if (nestName.toLowerCase() == nest.name.toLowerCase()) {
        nestMap.zoomToNest(nest);
      }
    });
  }

  function populateMenu(nests) {
    $(".see-all").on("click", nestMap.zoomToAll);
    var picker = $(".nest-picker");
    nests.forEach(function(nest) {
      var a = $("<a/>").text(nest.name)
        .attr("href", "#" + nest.name.toLowerCase())
        .on("click", function() {nestMap.zoomToNest(nest);});
      var li = $("<li/>")
        .append(a);
      picker.append(li);
    });
  }

  function generateRandomTimeSeries(timeScale, valueScale, sampleCount, volitility) {
    volitility = volitility || .1;
    var volitilityScale = d3.scale.linear().range([-volitility, volitility]);
    var timeSeries = [];
    var normalValue = Math.random();

    // create samples

    for (var i = 0; i < sampleCount; ++i) {

      // compute value normal

      var dNormal = volitilityScale(Math.random());
      normalValue += dNormal;
      normalValue = d3.max([0, d3.min([1, normalValue])]);

      // create record

      timeSeries.push({
        time: timeScale.invert(i / (sampleCount - 1)),
        value: valueScale(normalValue)
      });
    }

    return timeSeries;
  }
});
<|endoftext|>


<commit_before>/* global Dropzone*/
import Ember from 'ember';

export default Ember.Component.extend({
  classNames: ['dropzone'],

  myDropzone:undefined,

  dropzoneOptions: null,

  url: '#',
  method: null,
  parallelUploads: null,
  maxFilesize: null,
  filesizeBase: null,
  paramName: null,
  uploadMultiple: null,
  headers: null,
  addRemoveLinks: null,
  previewsContainer: null,
  clickable: null,
  maxThumbnailsize: null,
  thumbnailWidth: null,
  thumbnailHeight: null,
  maxFiles: null,
  // resize
  acceptedFiles: null,
  autoProcessQueue: null,
  forceFallback: null,
  previewTemplate: null,

  // Dropzone translations
  dictDefaultMessage: null,
  dictFallbackMessage: null,
  dictFallbackText: null,
  dictInvalidFileType: null,
  dictFileTooBig: null,
  dictResponseError: null,
  dictCancelUpload: null,
  dictCancelUploadConfirmation: null,
  dictRemoveFile: null,
  dictMaxFilesExceeded: null,

  // Events

  // All of these receive the event as first parameter:
  drop: null,
  dragstart: null,
  dragend: null,
  dragenter: null,
  dragover: null,
  dragleave: null,
  // All of these receive the file as first parameter:
  addedfile: null,
  removedfile: null,
  thumbnail: null,
  error: null,
  processing: null,
  uploadprogress: null,
  sending: null,
  success: null,
  complete: null,
  canceled: null,
  maxfilesreached: null,
  maxfilesexceeded : null,
  // All of these receive a list of files as first parameter and are only called if the uploadMultiple option is true:
  processingmultiple: null,
  sendingmultiple: null,
  successmultiple: null,
  completemultiple: null,
  canceledmultiple: null,
  //Special events:
  totaluploadprogress: null,
  reset: null,
  queuecomplete: null,

  getDropzoneOptions(){
    let dropzoneOptions = {};
    let dropzoneKeys = [
      "url",
      "method",
      "parallelUploads",
      "maxFilesize",
      "filesizeBase",
      "paramName",
      "uploadMultiple",
      "headers",
      "addRemoveLinks",
      "previewsContainer",
      "clickable",
      "maxThumbnailsize",
      "thumbnailWidth",
      "thumbnailHeight",
      "maxFiles",
      "acceptedFiles",
      "autoProcessQueue",
      "forceFallback",
      "previewTemplate",
      "dictDefaultMessage",
      "dictFallbackMessage",
      "dictFallbackText",
      "dictInvalidFileType",
      "dictFileTooBig",
      "dictResponseError",
      "dictCancelUpload",
      "dictCancelUploadConfirmation",
      "dictRemoveFile",
      "dictMaxFilesExceeded",
      "drop",
      "dragstart",
      "dragend",
      "dragenter",
      "dragover",
      "dragleave",
      "addedfile",
      "removedfile",
      "thumbnail",
      "error",
      "processing",
      "uploadprogress",
      "sending",
      "success",
      "complete",
      "canceled",
      "maxfilesreached",
      "maxfilesexceeded",
      "processingmultiple",
      "sendingmultiple",
      "successmultiple",
      "completemultiple",
      "canceledmultiple",
      "totaluploadprogress",
      "reset",
      "queuecomplete"
    ];
    let dropzoneProperties = [
      this.url,
      this.method,
      this.parallelUploads,
      this.maxFilesize,
      this.filesizeBase,
      this.paramName,
      this.uploadMultiple,
      this.headers,
      this.addRemoveLinks,
      this.previewsContainer,
      this.clickable,
      this.maxThumbnailsize,
      this.thumbnailWidth,
      this.thumbnailHeight,
      this.maxFiles,
      // resize
      this.acceptedFiles,
      this.autoProcessQueue,
      this.forceFallback,
      this.previewTemplate,

      // Dropzone translations
      this.dictDefaultMessage,
      this.dictFallbackMessage,
      this.dictFallbackText,
      this.dictInvalidFileType,
      this.dictFileTooBig,
      this.dictResponseError,
      this.dictCancelUpload,
      this.dictCancelUploadConfirmation,
      this.dictRemoveFile,
      this.dictMaxFilesExceeded,

      // Events

      // All of these receive the event as first parameter:
      this.drop,
      this.dragstart,
      this.dragend,
      this.dragenter,
      this.dragover,
      this.dragleave,
      // All of these receive the file as first parameter:
      this.addedfile,
      this.removedfile,
      this.thumbnail,
      this.error,
      this.processing,
      this.uploadprogress,
      this.sending,
      this.success,
      this.complete,
      this.canceled,
      this.maxfilesreached,
      this.maxfilesexceeded,
      // All of these receive a list of files as first parameter and are only called if the uploadMultiple option is true:
      this.processingmultiple,
      this.sendingmultiple,
      this.successmultiple,
      this.completemultiple,
      this.canceledmultiple,
      //Special events:
      this.totaluploadprogress,
      this.reset,
      this.queuecomplete
    ];

    for( let i=0; i < dropzoneProperties.length; i++ ){
      if(dropzoneProperties[i] !== null){
        dropzoneOptions[dropzoneKeys[i]] = dropzoneProperties[i];
      }
    }
    this.set('dropzoneOptions', dropzoneOptions);
  },

  createDropzone(element){
    this.set('myDropzone', new Dropzone(element, this.dropzoneOptions));
  },

  insertDropzone: Ember.on('didInsertElement', function(){
    this.getDropzoneOptions();
    Dropzone.autoDiscover = false;
    this.createDropzone('div.dropzone');
    return this.myDropzone;
  })
});
<commit_msg>add withCredentials<commit_after>/* global Dropzone*/
import Ember from 'ember';

export default Ember.Component.extend({
  classNames: ['dropzone'],

  myDropzone:undefined,

  dropzoneOptions: null,

  url: '#',
  withCredentials: null,
  method: null,
  parallelUploads: null,
  maxFilesize: null,
  filesizeBase: null,
  paramName: null,
  uploadMultiple: null,
  headers: null,
  addRemoveLinks: null,
  previewsContainer: null,
  clickable: null,
  maxThumbnailsize: null,
  thumbnailWidth: null,
  thumbnailHeight: null,
  maxFiles: null,
  // resize
  acceptedFiles: null,
  autoProcessQueue: null,
  forceFallback: null,
  previewTemplate: null,

  // Dropzone translations
  dictDefaultMessage: null,
  dictFallbackMessage: null,
  dictFallbackText: null,
  dictInvalidFileType: null,
  dictFileTooBig: null,
  dictResponseError: null,
  dictCancelUpload: null,
  dictCancelUploadConfirmation: null,
  dictRemoveFile: null,
  dictMaxFilesExceeded: null,

  // Events

  // All of these receive the event as first parameter:
  drop: null,
  dragstart: null,
  dragend: null,
  dragenter: null,
  dragover: null,
  dragleave: null,
  // All of these receive the file as first parameter:
  addedfile: null,
  removedfile: null,
  thumbnail: null,
  error: null,
  processing: null,
  uploadprogress: null,
  sending: null,
  success: null,
  complete: null,
  canceled: null,
  maxfilesreached: null,
  maxfilesexceeded : null,
  // All of these receive a list of files as first parameter and are only called if the uploadMultiple option is true:
  processingmultiple: null,
  sendingmultiple: null,
  successmultiple: null,
  completemultiple: null,
  canceledmultiple: null,
  //Special events:
  totaluploadprogress: null,
  reset: null,
  queuecomplete: null,

  getDropzoneOptions(){
    let dropzoneOptions = {};
    let dropzoneKeys = [
      "url",
      "method",
      "parallelUploads",
      "maxFilesize",
      "filesizeBase",
      "paramName",
      "uploadMultiple",
      "headers",
      "addRemoveLinks",
      "previewsContainer",
      "clickable",
      "maxThumbnailsize",
      "thumbnailWidth",
      "thumbnailHeight",
      "maxFiles",
      "acceptedFiles",
      "autoProcessQueue",
      "forceFallback",
      "previewTemplate",
      "dictDefaultMessage",
      "dictFallbackMessage",
      "dictFallbackText",
      "dictInvalidFileType",
      "dictFileTooBig",
      "dictResponseError",
      "dictCancelUpload",
      "dictCancelUploadConfirmation",
      "dictRemoveFile",
      "dictMaxFilesExceeded",
      "drop",
      "dragstart",
      "dragend",
      "dragenter",
      "dragover",
      "dragleave",
      "addedfile",
      "removedfile",
      "thumbnail",
      "error",
      "processing",
      "uploadprogress",
      "sending",
      "success",
      "complete",
      "canceled",
      "maxfilesreached",
      "maxfilesexceeded",
      "processingmultiple",
      "sendingmultiple",
      "successmultiple",
      "completemultiple",
      "canceledmultiple",
      "totaluploadprogress",
      "reset",
      "queuecomplete",
      "withCredentials"
    ];
    let dropzoneProperties = [
      this.url,
      this.method,
      this.parallelUploads,
      this.maxFilesize,
      this.filesizeBase,
      this.paramName,
      this.uploadMultiple,
      this.headers,
      this.addRemoveLinks,
      this.previewsContainer,
      this.clickable,
      this.maxThumbnailsize,
      this.thumbnailWidth,
      this.thumbnailHeight,
      this.maxFiles,
      // resize
      this.acceptedFiles,
      this.autoProcessQueue,
      this.forceFallback,
      this.previewTemplate,

      // Dropzone translations
      this.dictDefaultMessage,
      this.dictFallbackMessage,
      this.dictFallbackText,
      this.dictInvalidFileType,
      this.dictFileTooBig,
      this.dictResponseError,
      this.dictCancelUpload,
      this.dictCancelUploadConfirmation,
      this.dictRemoveFile,
      this.dictMaxFilesExceeded,

      // Events

      // All of these receive the event as first parameter:
      this.drop,
      this.dragstart,
      this.dragend,
      this.dragenter,
      this.dragover,
      this.dragleave,
      // All of these receive the file as first parameter:
      this.addedfile,
      this.removedfile,
      this.thumbnail,
      this.error,
      this.processing,
      this.uploadprogress,
      this.sending,
      this.success,
      this.complete,
      this.canceled,
      this.maxfilesreached,
      this.maxfilesexceeded,
      // All of these receive a list of files as first parameter and are only called if the uploadMultiple option is true:
      this.processingmultiple,
      this.sendingmultiple,
      this.successmultiple,
      this.completemultiple,
      this.canceledmultiple,
      //Special events:
      this.totaluploadprogress,
      this.reset,
      this.queuecomplete,
      this.withCredentials
    ];

    for( let i=0; i < dropzoneProperties.length; i++ ){
      if(dropzoneProperties[i] !== null){
        dropzoneOptions[dropzoneKeys[i]] = dropzoneProperties[i];
      }
    }
    this.set('dropzoneOptions', dropzoneOptions);
  },

  createDropzone(element){
    this.set('myDropzone', new Dropzone(element, this.dropzoneOptions));
  },

  insertDropzone: Ember.on('didInsertElement', function(){
    this.getDropzoneOptions();
    Dropzone.autoDiscover = false;
    this.createDropzone('div.dropzone');
    return this.myDropzone;
  })
});
<|endoftext|>


<commit_before>import AnnotationComponent from './AnnotationComponent'
import IsolatedNodeComponent from './IsolatedNodeComponent'
import TextPropertyComponent from './TextPropertyComponent'
import TextPropertyEditor from './TextPropertyEditor'
import TextNodeComponent from './TextNodeComponent'
import UnsupportedNodeComponent from './UnsupportedNodeComponent'

export default {
  configure (config) {
    config.addComponent('annotation', AnnotationComponent)
    config.addComponent('isolated-node', IsolatedNodeComponent)
    config.addComponent('text-node', TextNodeComponent)
    config.addComponent('text-property', TextPropertyComponent)
    config.addComponent('text-property-editor', TextPropertyEditor)
    config.addComponent('unsupported-node', UnsupportedNodeComponent)
  }
}
<commit_msg>Register component for inline nodes.<commit_after>import AnnotationComponent from './AnnotationComponent'
import IsolatedNodeComponent from './IsolatedNodeComponent'
import IsolatedInlineNodeComponent from './IsolatedInlineNodeComponent'
import TextPropertyComponent from './TextPropertyComponent'
import TextPropertyEditor from './TextPropertyEditor'
import TextNodeComponent from './TextNodeComponent'
import UnsupportedNodeComponent from './UnsupportedNodeComponent'

export default {
  configure (config) {
    config.addComponent('annotation', AnnotationComponent)
    config.addComponent('inline-node', IsolatedInlineNodeComponent)
    config.addComponent('isolated-node', IsolatedNodeComponent)
    config.addComponent('text-node', TextNodeComponent)
    config.addComponent('text-property', TextPropertyComponent)
    config.addComponent('text-property-editor', TextPropertyEditor)
    config.addComponent('unsupported-node', UnsupportedNodeComponent)
  }
}
<|endoftext|>


<commit_before>/**
 * Created by ssmith on 12/29/15.
 */
var ws = new SockJS('http://127.0.0.1:15674/stomp');
var client = Stomp.over(ws);

// SockJS does not support heart-beat: disable heart-beats
client.heartbeat.outgoing = 0;
client.heartbeat.incoming = 0;

var on_message = function(data) {
    var content = $('#content');
    content.text(data);
}
var on_connect = function(x) {
    id = client.subscribe("/topic/hello", function(d) {
        on_message(d.body);
    });
};
var on_error =  function() {
    console.log('error');
};
client.connect('guest', 'guest', on_connect, on_error, '/')<commit_msg>change hard coded path to the same path as the server<commit_after>/**
 * Created by ssmith on 12/29/15.
 */
var ws = new SockJS('http://' + window.location.hostname + ':15674/stomp');
var client = Stomp.over(ws);

// SockJS does not support heart-beat: disable heart-beats
client.heartbeat.outgoing = 0;
client.heartbeat.incoming = 0;

var on_message = function(data) {
    var content = $('#content');
    content.text(data);
}
var on_connect = function(x) {
    id = client.subscribe("/topic/hello", function(d) {
        on_message(d.body);
    });
};
var on_error =  function() {
    console.log('error');
};
client.connect('guest', 'guest', on_connect, on_error, '/')<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({
    store: Ember.inject.service('store'),
    action: null,
    classNames: ['ember-table-jsonapi'],
    hasActions: false,
    makeRequest: true,
    sortableClass: 'sortable',
    tableLoadedMessage: 'No Data.',
    columnLength: Ember.computed('columns', function() {
        var columnLength = this.get('columns').length;
        return this.get('hasActions') ? columnLength + 1 : columnLength;
    }),

    // Allows multiple yields
    header: {
        isHeader: true
    },
    body: {
        isBody: true
    },
    footer: {
        isFooter: true
    },

    // Model to be requested
    modelType: null,
    // Bind variable for table data
    bindModel: null,
    columns: null,

    // pagination defaults
    page: 1,
    limit: 10,
    pageLimit: 0,
    offset: 0,
    sort: null,
    filter: null,
    // If additional static params are required in requests
    // expects Object {}
    staticParams: null,

    // State flags
    isSuccess: false,
    isFailure: false,

    defaultSuccessMessage: 'Success!',
    defaultFailureMessage: 'There was an issue. Please check below for errors.',

    // Messages
    successMessage: Ember.get(Ember.Component, 'defaultSuccessMessage'),
    failureMessage: Ember.get(Ember.Component, 'defaultFailureMessage'),

    // For pushing any per field errors
    errors: null,

    isBindModelLoaded: Ember.computed('errors', 'bindModel', 'bindModel.isFulfilled', 'bindModel.isLoaded', 'modelType', function() {
        // If bindModel array isLoaded but empty
        if (this.get('bindModel.isLoaded')) {
            return true;
        }
        // If bindModel.content array loaded is empty
        if (this.get('bindModel.isFulfilled')) {
            return true;
        }
        // If errors
        if (this.get('errors')) {
            return true;
        }
        // If bindModel array is empty
        if (this.get('bindModel') && this.get('bindModel').length === 0) {
            return true;
        }
        // Show custom tableLoadedMessage
        if (this.get('bindModel') === null && this.get('modelType') === null) {
            return true;
        }

        return false;
    }),

    isColumnFilters: Ember.computed('columns', function() {
        let columns = this.get('columns');

        for (var i = columns.length - 1; i >= 0; i--) {
            if (columns[i].hasOwnProperty('property')) {
                return true;
            }
        }

        return false;
    }),

    defaultSort: Ember.on('init', function() {
        this.get('columns').map(function(el) {
            if (el.hasOwnProperty('defaultSort')) {
                this.set('sort', el.defaultSort);
            }
        }.bind(this));
    }),

    query: Ember.computed('page', 'limit', 'offset', 'sort', 'filter.@each.value', 'staticParams', function() {
        var query = {},
            filter = this.get('filter') || [];
        query = {
            'page': this.get('page'),
            'limit': this.get('limit'),
            'offset': this.get('offset'),
            'sort': this.get('sort'),
            'filter': filter.reduce( (memo, filter) => Ember.merge(memo, {[filter.field]: filter.value}), {} ),
        };

        // Merge staticParams/query into query
        Ember.merge(query, this.get('staticParams'));

        return query;
    }),

    setModel: Ember.on('init', Ember.observer('query', function() {
        Ember.run.once(this, function() {
            // If makeRequest is false do not make request and setModel
            if (this.get('makeRequest')) {
                this.reset();
                var modelType = this.get('modelType'),
                    params = this.get('query');
                // Override to set dynamic offset based on page and limit
                params.offset = (params.page * params.limit) - params.limit;
                if (isNaN(params.offset)) {
                    params.offset = null;
                }

                return this.get('store').query(modelType, params).then(
                    function(data) {
                        // pagination - return number of pages
                        let pageLimit = Math.ceil(data.meta.total/params.limit);
                        // determine if pageLimit is a valid number value
                        if (isFinite(pageLimit)) {
                            this.set('pageLimit', pageLimit);
                        } else {
                            this.set('pageLimit', null);
                        }

                        this.set('bindModel', data);
                    }.bind(this),
                    function(errors) {
                        this.failure(errors);
                    }.bind(this)
                );
            }
        });
    })),

    actions: {
        sortBy(property) {
            this.setSort(property);
        }
    },

    setSort: Ember.on('didInsertElement', function(sortProperty) {
        if (this.get('sort') || sortProperty) {
            var _this = this,
                $table = this.$(),
                property,
                classProperty,
                $tableHeader;

            if (sortProperty) {
                property = sortProperty;
                classProperty = property.replace(/\./g, '-');
                $tableHeader = Ember.$('#' + classProperty);
            } else {
                property = this.get('sort').replace(/\-/g, '');
                // Must be the opposite of property
                sortProperty = '-' + property;
                classProperty = property.replace(/\./g, '-');
                $tableHeader = Ember.$('#' + classProperty);
            }

            // Remove all classes on th.sortable but sortable class
            $table.find('th').removeClass(function(i, group) {
                var list = group.split(' ');
                return list.filter(function(val) {
                    return (val !== _this.get('sortableClass'));
                }).join(' ');
            });

            if (this.get('sort') === sortProperty) {
                this.set('sort', '-' + property);
                $tableHeader.addClass('sort-desc');
            } else {
                this.set('sort', property);
                $tableHeader.addClass('sort-asc');
            }
        }
    }),

    failure(response) {
        this.reset();
        this.setProperties({
            isFailure: true,
            pageLimit: null,
        });

        // Set per field errors if found
        if ('errors' in response) {
            this.set('errors', response.errors);
        }
    },

    reset() {
        this.setProperties({
            'bindModel': null,
            'errors': null,
            'isSuccess': false,
            'isFailure': false,
            'successMessage': this.get('defaultSuccessMessage'),
            'failureMessage': this.get('defaultFailureMessage'),
        });
    },
});
<commit_msg>Move the component request into its own method to allow extension<commit_after>import Ember from 'ember';

export default Ember.Component.extend({
    store: Ember.inject.service('store'),
    action: null,
    classNames: ['ember-table-jsonapi'],
    hasActions: false,
    makeRequest: true,
    sortableClass: 'sortable',
    tableLoadedMessage: 'No Data.',
    columnLength: Ember.computed('columns', function() {
        var columnLength = this.get('columns').length;
        return this.get('hasActions') ? columnLength + 1 : columnLength;
    }),

    // Allows multiple yields
    header: {
        isHeader: true
    },
    body: {
        isBody: true
    },
    footer: {
        isFooter: true
    },

    // Model to be requested
    modelType: null,
    // Bind variable for table data
    bindModel: null,
    columns: null,

    // pagination defaults
    page: 1,
    limit: 10,
    pageLimit: 0,
    offset: 0,
    sort: null,
    filter: null,
    // If additional static params are required in requests
    // expects Object {}
    staticParams: null,

    // State flags
    isSuccess: false,
    isFailure: false,

    defaultSuccessMessage: 'Success!',
    defaultFailureMessage: 'There was an issue. Please check below for errors.',

    // Messages
    successMessage: Ember.get(Ember.Component, 'defaultSuccessMessage'),
    failureMessage: Ember.get(Ember.Component, 'defaultFailureMessage'),

    // For pushing any per field errors
    errors: null,

    isBindModelLoaded: Ember.computed('errors', 'bindModel', 'bindModel.isFulfilled', 'bindModel.isLoaded', 'modelType', function() {
        // If bindModel array isLoaded but empty
        if (this.get('bindModel.isLoaded')) {
            return true;
        }
        // If bindModel.content array loaded is empty
        if (this.get('bindModel.isFulfilled')) {
            return true;
        }
        // If errors
        if (this.get('errors')) {
            return true;
        }
        // If bindModel array is empty
        if (this.get('bindModel') && this.get('bindModel').length === 0) {
            return true;
        }
        // Show custom tableLoadedMessage
        if (this.get('bindModel') === null && this.get('modelType') === null) {
            return true;
        }

        return false;
    }),

    isColumnFilters: Ember.computed('columns', function() {
        let columns = this.get('columns');

        for (var i = columns.length - 1; i >= 0; i--) {
            if (columns[i].hasOwnProperty('property')) {
                return true;
            }
        }

        return false;
    }),

    defaultSort: Ember.on('init', function() {
        this.get('columns').map(function(el) {
            if (el.hasOwnProperty('defaultSort')) {
                this.set('sort', el.defaultSort);
            }
        }.bind(this));
    }),

    query: Ember.computed('page', 'limit', 'offset', 'sort', 'filter.@each.value', 'staticParams', function() {
        var query = {},
            filter = this.get('filter') || [];
        query = {
            'page': this.get('page'),
            'limit': this.get('limit'),
            'offset': this.get('offset'),
            'sort': this.get('sort'),
            'filter': filter.reduce( (memo, filter) => Ember.merge(memo, {[filter.field]: filter.value}), {} ),
        };

        // Merge staticParams/query into query
        Ember.merge(query, this.get('staticParams'));

        return query;
    }),

    request(modelType, params) {
        return this.get('store').query(modelType, params).then(
            function(data) {
                // pagination - return number of pages
                let pageLimit = Math.ceil(data.meta.total/params.limit);
                // determine if pageLimit is a valid number value
                if (isFinite(pageLimit)) {
                    this.set('pageLimit', pageLimit);
                } else {
                    this.set('pageLimit', null);
                }

                this.set('bindModel', data);
            }.bind(this),
            function(errors) {
                this.failure(errors);
            }.bind(this)
        );
    },

    setModel: Ember.on('init', Ember.observer('query', function() {
        Ember.run.once(this, function() {
            // If makeRequest is false do not make request and setModel
            if (this.get('makeRequest')) {
                this.reset();
                var modelType = this.get('modelType'),
                    params = this.get('query');
                // Override to set dynamic offset based on page and limit
                params.offset = (params.page * params.limit) - params.limit;
                if (isNaN(params.offset)) {
                    params.offset = null;
                }

                return this.request(modelType, params);
            }
        });
    })),

    actions: {
        sortBy(property) {
            this.setSort(property);
        }
    },

    setSort: Ember.on('didInsertElement', function(sortProperty) {
        if (this.get('sort') || sortProperty) {
            var _this = this,
                $table = this.$(),
                property,
                classProperty,
                $tableHeader;

            if (sortProperty) {
                property = sortProperty;
                classProperty = property.replace(/\./g, '-');
                $tableHeader = Ember.$('#' + classProperty);
            } else {
                property = this.get('sort').replace(/\-/g, '');
                // Must be the opposite of property
                sortProperty = '-' + property;
                classProperty = property.replace(/\./g, '-');
                $tableHeader = Ember.$('#' + classProperty);
            }

            // Remove all classes on th.sortable but sortable class
            $table.find('th').removeClass(function(i, group) {
                var list = group.split(' ');
                return list.filter(function(val) {
                    return (val !== _this.get('sortableClass'));
                }).join(' ');
            });

            if (this.get('sort') === sortProperty) {
                this.set('sort', '-' + property);
                $tableHeader.addClass('sort-desc');
            } else {
                this.set('sort', property);
                $tableHeader.addClass('sort-asc');
            }
        }
    }),

    failure(response) {
        this.reset();
        this.setProperties({
            isFailure: true,
            pageLimit: null,
        });

        // Set per field errors if found
        if ('errors' in response) {
            this.set('errors', response.errors);
        }
    },

    reset() {
        this.setProperties({
            'bindModel': null,
            'errors': null,
            'isSuccess': false,
            'isFailure': false,
            'successMessage': this.get('defaultSuccessMessage'),
            'failureMessage': this.get('defaultFailureMessage'),
        });
    },
});
<|endoftext|>


<commit_before>import React from 'react';
import {Button, ButtonGroup, ButtonToolbar, FormControls, Input, Modal, Panel, ListGroup, ListGroupItem, Glyphicon} from 'react-bootstrap';
import SVG from 'react-inlinesvg';

import ElementActions from './actions/ElementActions';
import ElementStore from './stores/ElementStore';

import UIStore from './stores/UIStore';

import NumeralInputWithUnits from './NumeralInputWithUnits'
import ElementCollectionLabels from './ElementCollectionLabels';

import StructureEditorModal from './structure_editor/StructureEditorModal';

import Aviator from 'aviator';

export default class SampleDetails extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      sample: props.sample,
      showStructureEditor: false
    }
  }

  componentDidMount() {
    ElementStore.listen(this.onChange.bind(this));
  }

  componentWillUnmount() {
    ElementStore.unlisten(this.onChange.bind(this));
  }

  onChange(state) {
    if(!state.currentElement || state.currentElement.type == 'sample') {
      this.setState({
        sample: state.currentElement
      });
    }
  }

  closeDetails() {
    let uiState = UIStore.getState();
    Aviator.navigate(`/collection/${uiState.currentCollectionId}`);
  }

  createSampleObject() {
    let uiState = UIStore.getState();
    return {
      id: this.state.sample.id,
      name: this.state.sample.name,
      amount_value: this.state.sample.amount_value,
      amount_unit: this.state.sample.amount_unit,
      description: this.state.sample.description,
      purity: this.state.sample.purity,
      solvent: this.state.sample.solvent,
      impurities: this.state.sample.impurities,
      location: this.state.sample.location,
      molfile: this.state.sample.molfile,
      molecule: this.state.sample.molecule,
      collection_id: uiState.currentCollectionId
    }
  }

  updateSample() {
    ElementActions.updateSample(this.createSampleObject());
  }

  createSample() {
    ElementActions.createSample(this.createSampleObject());
  }

  handleNameChanged(e) {
    let sample = this.state.sample;
    sample.name = this.refs.nameInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleDescriptionChanged(e) {
    let sample = this.state.sample;
    sample.description = this.refs.descriptionInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleAmountChanged(amount) {
    let sample = this.state.sample;
    sample.amount_unit = amount.unit;
    sample.amount_value = amount.value;
    this.setState({
      sample: sample
    });
  }

  handlePurityChanged(e) {
    let sample = this.state.sample;
    sample.purity = this.refs.purityInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleDensityChanged(e) {
    let sample = this.state.sample;
    sample.molecule.density = this.refs.densityInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleImpuritiesChanged(e) {
    let sample = this.state.sample;
    sample.impurities = this.refs.impuritiesInput.getValue();
    this.setState({
      sample: sample
    });
  }
  handleBoilingPointChanged(e) {
    let sample = this.state.sample;
    sample.molecule.boiling_point = this.refs.boilingPointInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleLocationChanged(e) {
    let sample = this.state.sample;
    sample.location = this.refs.locationInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleSolventChanged(e) {
    let sample = this.state.sample;
    sample.solvent = this.refs.solventInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleMeltingPointChanged(e) {
    let sample = this.state.sample;
    sample.molecule.melting_point = this.refs.meltingPointInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleUnitChanged(unit, nextUnit, value) {
    console.log("ajax call with unit: " + unit + " nextUnit: " + nextUnit + " and value: " + 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;
      }
    }
    console.log("result:" + convertedValue);
    return convertedValue;
  }

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

  hideStructureEditor() {
    this.setState({
      showStructureEditor: false
    })
  }

  handleStructureEditorSave(molfile) {
    // TODO: handle the resulting molfile and submit it
    console.log("Molecule MOL-file:");
    console.log(molfile);

    // TODO: optimize
    let sample = this.state.sample;
    if(sample) {
      sample.molfile = molfile
    }
    this.setState({sample: sample})

    this.hideStructureEditor()
  }

  handleStructureEditorCancel() {
    this.hideStructureEditor()
  }

  _submitFunction() {
    if (this.state.sample.id == '_new_') {
      this.createSample();
    } else {
      this.updateSample();
    }
  }

  _submitLabel() {
    if (this.state.sample.id == '_new_') {
      return "Save Sample";
    } else {
      return "Update Sample";
    }
  }

  sampleIsValid() {
    let sample = this.state.sample;
    return sample && sample.molfile
  }

  render() {

    let sample = this.state.sample || {}
    let sampleAmount = sample.amount_value && sample.amount_unit ? `(${sample.amount_value} ${sample.amount_unit})` : '';
    let svgPath = sample.molecule && sample.molecule.molecule_svg_file ? `/images/molecules/${sample.molecule.molecule_svg_file}`  : '';
    let molfile = sample.molfile;

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

    let sampleIsValid = this.sampleIsValid();

    return (

        <StructureEditorModal
          key={sample.id}
          showModal={this.state.showStructureEditor}
          onSave={this.handleStructureEditorSave.bind(this)}
          onCancel={this.handleStructureEditorCancel.bind(this)}
          molfile={molfile}
        />
        <Panel header="Sample Details" bsStyle='primary'>

{sample.name}

{sampleAmount}

              <ElementCollectionLabels element={sample} key={sample.id} />

              <SVG key={sample.molecule && sample.molecule.id} src={svgPath} className="molecule-mid"/>

          <ListGroup fill>

            <form>
              <ListGroupItem>
                <Input type="text" label="Molecule" ref="moleculeInput"
                  buttonAfter={structureEditorButton}
                  value={sample.molecule && (sample.molecule.iupac_name || sample.molecule.sum_formular)}
                />

                      <Input type="text" label="InChI"
                        value={sample.molecule && (sample.molecule.inchistring) }
                        disabled
                      />

                      <Input type="text" label="M. Weight"
                        value={sample.molecule && (sample.molecule.molecular_weight) }
                        disabled
                      />

                      <Input type="text" label="Density" ref="densityInput"
                        value={sample.molecule && (sample.molecule.density) }
                        onChange={(e) => this.handleDensityChanged(e)}
                      />

                      <Input type="text" label="Formula"
                        value={sample.molecule && (sample.molecule.sum_formular) }
                        disabled
                      />

                      <Input type="text" label="Boiling Point" ref="boilingPointInput"
                        value={sample.molecule && (sample.molecule.boiling_point) }
                        onChange={(e) => this.handleBoilingPointChanged(e)}
                      />

                      <Input type="text" label="Melting Point" ref="meltingPointInput"
                       value={sample.molecule && (sample.molecule.melting_point) }
                       onChange={(e) => this.handleMeltingPointChanged(e)}
                      />

              </ListGroupItem>
              <ListGroupItem>

                      <Input type="text" label="Name" ref="nameInput"
                        placeholder={sample.name}
                        value={sample.name}
                        onChange={(e) => this.handleNameChanged(e)}
                      />

                      <Input type="text" label="Impurities"
                        ref="impuritiesInput"
                        value={sample.impurities}
                        onChange={(e) => this.handleImpuritiesChanged(e)}
                      />

                      <Input type='select' label='Solvent' ref="solventInput" onChange={(e) => this.handleSolventChanged(e)} value={sample.solvent}>
                        <option value=''>-- Please Select --</option>
                        <option value='Solvent1'>Solvent1</option>
                        <option value='Solvent2'>Solvent2</option>
                        <option value='Solvent3'>Solvent3</option>
                      </Input>

                      <Input type="text" label="Weight"
                        value={sample.weight}
                        disabled
                      />

                      <Input type="text" label="Volume"
                        value={sample.volume}
                        disabled
                      />

                      <NumeralInputWithUnits
                         key={sample.id}
                         value={sample.amount_value}
                         unit={sample.amount_unit || 'g'}
                         label="Amount"
                         units={['g', 'ml', 'mol']}
                         numeralFormat='0,0.00'
                         convertValueFromUnitToNextUnit={(unit, nextUnit, value) => this.handleUnitChanged(unit, nextUnit, value)}
                         onChange={(amount) => this.handleAmountChanged(amount)}
                      />

                      <Input type="text" label="Purity"
                        ref="purityInput"
                        value={sample.purity}
                        onChange={(e) => this.handlePurityChanged(e)}
                      />

                      <Input type="textarea" label="Location"
                        ref="locationInput"
                        value={sample.location}
                        onChange={(e) => this.handleLocationChanged(e)}
                        rows={2}
                      />

                      <Input type="textarea" label="Description" ref="descriptionInput"
                        placeholder={sample.description}
                        value={sample.description}
                        onChange={(e) => this.handleDescriptionChanged(e)}
                        rows={2}
                      />

              </ListGroupItem>
              <ListGroupItem>
                <ButtonToolbar>
                  <Button bsStyle="primary" onClick={this.closeDetails.bind(this)}>Back</Button>
                  <Button bsStyle="warning" onClick={this._submitFunction.bind(this)} disabled={!sampleIsValid}>{this._submitLabel()}</Button>
                </ButtonToolbar>
              </ListGroupItem>
            </form>
          </ListGroup>
        </Panel>

    )
  }
}
<commit_msg>replace dropdown with select2, add static solvents<commit_after>import React from 'react';
import {Button, ButtonGroup, ButtonToolbar, FormControls, Input, Modal, Panel, ListGroup, ListGroupItem, Glyphicon} from 'react-bootstrap';
import SVG from 'react-inlinesvg';

import ElementActions from './actions/ElementActions';
import ElementStore from './stores/ElementStore';

import UIStore from './stores/UIStore';

import NumeralInputWithUnits from './NumeralInputWithUnits'
import ElementCollectionLabels from './ElementCollectionLabels';
import Select from 'react-select';

import StructureEditorModal from './structure_editor/StructureEditorModal';

import Aviator from 'aviator';

export default class SampleDetails extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      sample: props.sample,
      showStructureEditor: false
    }
  }

  componentDidMount() {
    ElementStore.listen(this.onChange.bind(this));
  }

  componentWillUnmount() {
    ElementStore.unlisten(this.onChange.bind(this));
  }

  onChange(state) {
    if(!state.currentElement || state.currentElement.type == 'sample') {
      this.setState({
        sample: state.currentElement
      });
    }
  }

  closeDetails() {
    let uiState = UIStore.getState();
    Aviator.navigate(`/collection/${uiState.currentCollectionId}`);
  }

  createSampleObject() {
    let uiState = UIStore.getState();
    return {
      id: this.state.sample.id,
      name: this.state.sample.name,
      amount_value: this.state.sample.amount_value,
      amount_unit: this.state.sample.amount_unit,
      description: this.state.sample.description,
      purity: this.state.sample.purity,
      solvent: this.state.sample.solvent,
      impurities: this.state.sample.impurities,
      location: this.state.sample.location,
      molfile: this.state.sample.molfile,
      molecule: this.state.sample.molecule,
      collection_id: uiState.currentCollectionId
    }
  }

  updateSample() {
    ElementActions.updateSample(this.createSampleObject());
  }

  createSample() {
    ElementActions.createSample(this.createSampleObject());
  }

  handleNameChanged(e) {
    let sample = this.state.sample;
    sample.name = this.refs.nameInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleDescriptionChanged(e) {
    let sample = this.state.sample;
    sample.description = this.refs.descriptionInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleAmountChanged(amount) {
    let sample = this.state.sample;
    sample.amount_unit = amount.unit;
    sample.amount_value = amount.value;
    this.setState({
      sample: sample
    });
  }

  handlePurityChanged(e) {
    let sample = this.state.sample;
    sample.purity = this.refs.purityInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleDensityChanged(e) {
    let sample = this.state.sample;
    sample.molecule.density = this.refs.densityInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleImpuritiesChanged(e) {
    let sample = this.state.sample;
    sample.impurities = this.refs.impuritiesInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleBoilingPointChanged(e) {
    let sample = this.state.sample;
    sample.molecule.boiling_point = this.refs.boilingPointInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleLocationChanged(e) {
    let sample = this.state.sample;
    sample.location = this.refs.locationInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleSolventChanged(event) {
    let {sample} = this.state;
    sample.solvent = event;
    this.setState({
      sample
    });
  }

  handleMeltingPointChanged(e) {
    let sample = this.state.sample;
    sample.molecule.melting_point = this.refs.meltingPointInput.getValue();
    this.setState({
      sample: sample
    });
  }

  handleUnitChanged(unit, nextUnit, value) {
    console.log("ajax call with unit: " + unit + " nextUnit: " + nextUnit + " and value: " + 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;
      }
    }
    console.log("result:" + convertedValue);
    return convertedValue;
  }

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

  hideStructureEditor() {
    this.setState({
      showStructureEditor: false
    })
  }

  handleStructureEditorSave(molfile) {
    // TODO: handle the resulting molfile and submit it
    console.log("Molecule MOL-file:");
    console.log(molfile);

    // TODO: optimize
    let sample = this.state.sample;
    if(sample) {
      sample.molfile = molfile
    }
    this.setState({sample: sample})

    this.hideStructureEditor()
  }

  handleStructureEditorCancel() {
    this.hideStructureEditor()
  }

  _submitFunction() {
    if(this.state.sample.id == '_new_') {
      this.createSample();
    } else {
      this.updateSample();
    }
  }

  _submitLabel() {
    if(this.state.sample.id == '_new_') {
      return "Save Sample";
    } else {
      return "Update Sample";
    }
  }

  sampleIsValid() {
    let sample = this.state.sample;
    return sample && sample.molfile
  }

  render() {

    let sample = this.state.sample || {}
    let sampleAmount = sample.amount_value && sample.amount_unit ? `(${sample.amount_value} ${sample.amount_unit})` : '';
    let svgPath = sample.molecule && sample.molecule.molecule_svg_file ? `/images/molecules/${sample.molecule.molecule_svg_file}` : '';
    let molfile = sample.molfile;

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

    let sampleIsValid = this.sampleIsValid();

    return (

        <StructureEditorModal
          key={sample.id}
          showModal={this.state.showStructureEditor}
          onSave={this.handleStructureEditorSave.bind(this)}
          onCancel={this.handleStructureEditorCancel.bind(this)}
          molfile={molfile}
          />
        <Panel header="Sample Details" bsStyle='primary'>

{sample.name}

{sampleAmount}

                <ElementCollectionLabels element={sample} key={sample.id}/>

                <SVG key={sample.molecule && sample.molecule.id} src={svgPath} className="molecule-mid"/>

          <ListGroup fill>

            <form>
              <ListGroupItem>
                <Input type="text" label="Molecule" ref="moleculeInput"
                       buttonAfter={structureEditorButton}
                       value={sample.molecule && (sample.molecule.iupac_name || sample.molecule.sum_formular)}
                  />

                      <Input type="text" label="InChI"
                             value={sample.molecule && (sample.molecule.inchistring) }
                             disabled
                        />

                      <Input type="text" label="M. Weight"
                             value={sample.molecule && (sample.molecule.molecular_weight) }
                             disabled
                        />

                      <Input type="text" label="Density" ref="densityInput"
                             value={sample.molecule && (sample.molecule.density) }
                             onChange={(e) => this.handleDensityChanged(e)}
                        />

                      <Input type="text" label="Formula"
                             value={sample.molecule && (sample.molecule.sum_formular) }
                             disabled
                        />

                      <Input type="text" label="Boiling Point" ref="boilingPointInput"
                             value={sample.molecule && (sample.molecule.boiling_point) }
                             onChange={(e) => this.handleBoilingPointChanged(e)}
                        />

                      <Input type="text" label="Melting Point" ref="meltingPointInput"
                             value={sample.molecule && (sample.molecule.melting_point) }
                             onChange={(e) => this.handleMeltingPointChanged(e)}
                        />

              </ListGroupItem>
              <ListGroupItem>

                      <Input type="text" label="Name" ref="nameInput"
                             placeholder={sample.name}
                             value={sample.name}
                             onChange={(e) => this.handleNameChanged(e)}
                        />

                      <Input type="text" label="Impurities"
                             ref="impuritiesInput"
                             value={sample.impurities}
                             onChange={(e) => this.handleImpuritiesChanged(e)}
                        />

                      Solvent
                      <Select ref='solventInput'
                              name='solvents'
                              multi={false}
                              options={solvents}
                              onChange={(e) => this.handleSolventChanged(e)}
                              value={sample.solvent}/>

                      <Input type="text" label="Weight"
                             value={sample.weight}
                             disabled
                        />

                      <Input type="text" label="Volume"
                             value={sample.volume}
                             disabled
                        />

                      <NumeralInputWithUnits
                        key={sample.id}
                        value={sample.amount_value}
                        unit={sample.amount_unit || 'g'}
                        label="Amount"
                        units={['g', 'ml', 'mol']}
                        numeralFormat='0,0.00'
                        convertValueFromUnitToNextUnit={(unit, nextUnit, value) => this.handleUnitChanged(unit, nextUnit, value)}
                        onChange={(amount) => this.handleAmountChanged(amount)}
                        />

                      <Input type="text" label="Purity"
                             ref="purityInput"
                             value={sample.purity}
                             onChange={(e) => this.handlePurityChanged(e)}
                        />

                      <Input type="textarea" label="Location"
                             ref="locationInput"
                             value={sample.location}
                             onChange={(e) => this.handleLocationChanged(e)}
                             rows={2}
                        />

                      <Input type="textarea" label="Description" ref="descriptionInput"
                             placeholder={sample.description}
                             value={sample.description}
                             onChange={(e) => this.handleDescriptionChanged(e)}
                             rows={2}
                        />

              </ListGroupItem>
              <ListGroupItem>
                <ButtonToolbar>
                  <Button bsStyle="primary" onClick={this.closeDetails.bind(this)}>Back</Button>
                  <Button bsStyle="warning" onClick={this._submitFunction.bind(this)}
                          disabled={!sampleIsValid}>{this._submitLabel()}</Button>
                </ButtonToolbar>
              </ListGroupItem>
            </form>
          </ListGroup>
        </Panel>

    )
  }
}

const solvents = [{
  label: 'Aceton',
  value: 'Aceton'
}, {
  label: 'Benzol',
  value: 'Benzol'
}, {
  label: 'Butanol',
  value: 'Butanol'
}, {
  label: 'Chloroform',
  value: 'Chloroform'
}, {
  label: 'Cyclohexan',
  value: 'Cyclohexan'
}, {
  label: 'Diethylether',
  value: 'Diethylether'
}, {
  label: 'Dimethylsulfoxid (DMF)',
  value: 'Dimethylsulfoxid'
}, {
  label: 'Dimethylformamid (DMF)',
  value: 'Dimethylformamid'
}, {
  label: '1,4-Dioxan',
  value: '1,4-Dioxan'
}, {
  label: 'Essigsäure',
  value: 'Essigsäure'
}, {
  label: 'Ethanol',
  value: 'Ethanol'
}, {
  label: 'Ethylacetat',
  value: 'Ethylacetat'
}, {
  label: 'Isopropanol',
  value: 'Isopropanol'
}, {
  label: 'Methanol',
  value: 'Methanol'
}, {
  label: 'Methylenchlorid (DCM)',
  value: 'Methylenchlorid'
}, {
  label: 'Methyl-tert-butylether (MTBE)',
  value: 'Methyl-tert-butylether'
}, {
  label: 'n-Hexan',
  value: 'n-Hexan'
}, {
  label: 'N-Methyl-2-pyrrolidon (NMP)',
  value: 'N-Methyl-2-pyrrolidon'
}, {
  label: 'Pentan',
  value: 'Pentan'
}, {
  label: 'Pyridin',
  value: 'Pyridin'
}, {
  label: 'Tetrahydrofuran (THF)',
  value: 'Tetrahydrofuran'
}, {
  label: 'Toluol',
  value: 'Toluol'
}, {
  label: 'Wasser',
  value: 'Wasser'
}, {
  label: 'CDCl3',
  value: 'CDCl3'
}, {
  label: 'MeOD-d4',
  value: 'MeOD-d4'
}, {
  label: 'C6D6',
  value: 'C6D6'
}, {
  label: 'D2O',
  value: 'D2O'
}];
<|endoftext|>


<commit_before>var global_min = 0;
var global_max = 0;
var chr_lengths = new Array();
var total_len = 0;
function manhattan_plot(canvasid,study) {
	// fetch the list of chromosomes and their lengths
	$.getJSON("/chromosomes/at",
		function(json1) {
			for(var i=0;i<json1.length;i++) {
				chr_lengths[i] = json1[i][1];
				total_len += json1[i][1];
			}
			// fetch the max score for this study
			$.getJSON("/maxscore/GWAS/"+study,
			function(json2) {
				global_max = Math.ceil(json2[0][0]);
				draw_manhattan(canvasid,study);
			}
		);
	});
}

function draw_manhattan(canvasid,study) {
  // get the canvas
	var canvas = document.getElementById(canvasid);
	var ctx = canvas.getContext('2d');
	ctx.canvas.width = window.innerWidth-20;
	ctx.canvas.height = window.innerHeight - 20;
	ctx.strokeStyle="black";
	ctx.strokeRect(0,0,ctx.canvas.width,ctx.canvas.height);
	var offset=0;
	var ysize = ctx.canvas.height;
	for(var i=0;i<chr_lengths.length;i++) {
		var xsize = ctx.canvas.width*(chr_lengths[i]/total_len);
		do_scatter(ctx,study,i+1,offset,xsize,ysize,chr_lengths[i]);
		offset += xsize;
	}
}

var sc = 2;
function do_scatter(ctx,study,chr,offset,xsize,ysize,chr_length) {
	var minbins=3;
	var fudge = 0.99;
	$.getJSON("/scatter/GWAS/"+study+"/"+chr+"/"+Math.floor(xsize/sc)+"/"+Math.floor(ysize/sc)+"/"+fudge+"/"+0+"/"+chr_length+"/"+0+"/"+global_max+"/"+minbins,
		function(json) {
			var xrange = chr_length;
			var yrange = global_max;
			var xfactor = xsize/xrange;
			var yfactor = ysize/yrange;
			if (chr % 2 === 0) {
				ctx.fillStyle="darkorange";
			} else {
				ctx.fillStyle="black";
			}
			for(var i=0; i<json.data.length; i++) {
				var rect = json.data[i];
				if (rect.length == 2) {
					// not a rectangle, just a point
					var x = xfactor*rect[0] + offset;
					var y = ysize - sc - yfactor*rect[1];
					ctx.fillRect(Math.floor(x),Math.floor(y),sc,sc);
				} else {
					var width = xfactor*(rect[1]-rect[0]);
					var height = sc+yfactor*(rect[3]-rect[2]);
					var x = xfactor*rect[0] + offset;
					var y = ysize - height - yfactor*rect[2];
					if (height < sc) {height=sc;}
					if (width < sc) {width = sc;}
					if (height * width < 0) {
						ctx.fillRect(Math.floor(x),Math.floor(y),sc,sc);
					} else {
						ctx.fillRect(Math.floor(x),Math.floor(y),Math.ceil(width),Math.ceil(height));
					}
				}
			}
		});
}

function manhattan_plot_dots(canvasid,study) {
	// fetch the list of chromosomes and their lengths
	$.getJSON("/chromosomes/at",
		function(json1) {
			for(var i=0;i<json1.length;i++) {
				chr_lengths[i] = json1[i][1];
				total_len += json1[i][1];
			}
			// fetch the max score for this study
			$.getJSON("/maxscore/GWAS/"+study,
			function(json2) {
				global_max = Math.ceil(json2[0][0]);
				draw_manhattan_dots(canvasid,study);
			}
		);
	});
}
var ctxi;
function draw_manhattan_dots(canvasid,study) {
  // get the canvas
	var canvas = document.getElementById(canvasid);
	var ctx = canvas.getContext('2d');
	ctx.canvas.width = window.innerWidth-20;
	ctx.canvas.height = window.innerHeight - 20;
	ctx.strokeStyle="black";
	ctx.strokeRect(0,0,ctx.canvas.width,ctx.canvas.height);

	// resize the interactive layer
	var canvasi = document.getElementById(canvasid+'i');
	ctxi = canvasi.getContext('2d');
	ctxi.canvas.width = ctx.canvas.width;
	ctxi.canvas.height = ctx.canvas.height;

	tool = new drag_select();

	// add event listeners
	canvasi.addEventListener('mousedown', ev_canvas, false);
	canvasi.addEventListener('mousemove', ev_canvas, false);
	canvasi.addEventListener('mouseup',   ev_canvas, false);

	var offset=0;
	var ysize = ctx.canvas.height;
	for(var i=0;i<chr_lengths.length;i++) {
		var xsize = ctx.canvas.width*(chr_lengths[i]/total_len);
		do_scatter_dots(ctx,study,i+1,offset,xsize,ysize,chr_lengths[i]);
		offset += xsize;
	}
}

function do_scatter_dots(ctx,study,chr,offset,xsize,ysize,chr_length) {
	$.getJSON("/scatter/GWAS/nobinning/"+study+"/"+chr,
		function(json) {
			var xrange = chr_length;
			var yrange = global_max;
			var xfactor = xsize/xrange;
			var yfactor = ysize/yrange;
			if (chr % 2 === 0) {
				ctx.fillStyle="darkorange";
			} else {
				ctx.fillStyle="black";
			}
			for(var i=0; i<json.length; i++) {
				var x = xfactor*json[i][0] + offset;
				var y = ysize - 2 - yfactor*json[i][1];
				ctx.fillRect(Math.floor(x),Math.floor(y),sc,sc);
			}
		});
}

function drag_select () {
	var tool = this;
	this.started = false;

	this.mousedown = function (ev) {
		tool.started = true;
		tool.x = ev._x;
		tool.y = ev._y;
		ctxi.clearRect(0,0,ctxi.canvas.width,ctxi.canvas.height);
		ctxi.strokeRect(tool.x,tool.y,1,1);
	};

	this.mousemove = function (ev) {
		if (tool.started) {
			var x1 = tool.x;
			var y1 = tool.y;
			var x2 = ev._x;
			var y2 = ev._y;
			if (x2 - x1 > 1) {
				x1++;
				x2--;
				if (y2 - y1 > 1) {
					y1++;
					y2--;
					ctxi.clearRect(x1,y1,x2,y2);
				} else if (y1 - y2 > 1) {
					y1--;
					y2++;
					ctxi.clearRect(x1,y1,x2,y2);
				}
			} else if (x1 - x2 > 1) {
				x1--;
				x2++;
				if (y2 - y1 > 1) {
					y1++;
					y2--;
					ctxi.clearRect(x1,y1,x2,y2);
				} else if (y1 - y2 > 1) {
					y1--;
					y2++;
					ctxi.clearRect(x1,y1,x2,y2);
				}
			}
			ctxi.strokeRect(tool.x, tool.y, ev._x - tool.x + 1, ev._y - tool.y + 1);
		}
	};

	this.mouseup = function(ev) {
		if (tool.started) {
			alert("selected rectangle (" + tool.x + "," + tool.y + "," + ev._x + "," + ev._y + ")");
			tool.started = false;
		}
	};
}

function ev_canvas (ev) {
	if (ev.layerX || ev.layerX == 0) {
		ev._x = ev.layerX;
		ev._y = ev.layerY;
	} else if (ev.offsetX || ev.offsetX == 0) {
		ev._x = ev.offsetX;
		ev._y = ev.offsetY;
	}
	var func = drag_select[ev.type];
	if (func) {
		func(ev)
	}
}<commit_msg>added interative layer for drag-select<commit_after>var ctxi;
tool = new Object();
tool.started = false;
var global_min = 0;
var global_max = 0;
var chr_lengths = new Array();
var total_len = 0;
var sc = 2;
var circles = false;
function manhattan_plot(canvasid,study) {
	// fetch the list of chromosomes and their lengths
	$.getJSON("/chromosomes/at",
		function(json1) {
			for(var i=0;i<json1.length;i++) {
				chr_lengths[i] = json1[i][1];
				total_len += json1[i][1];
			}
			// fetch the max score for this study
			$.getJSON("/maxscore/GWAS/"+study,
			function(json2) {
				global_max = Math.ceil(json2[0][0]);
				draw_manhattan(canvasid,study);
			}
		);
	});
}

function draw_manhattan(canvasid,study) {
  // get the canvas
	var canvas = document.getElementById(canvasid);
	var ctx = canvas.getContext('2d');
	ctx.canvas.width = window.innerWidth-20;
	ctx.canvas.height = window.innerHeight - 20;
	ctx.strokeStyle="black";
	ctx.strokeRect(0,0,ctx.canvas.width,ctx.canvas.height);

	// resize the interactive layer
	var canvasi = document.getElementById(canvasid+'i');
	ctxi = canvasi.getContext('2d');
	ctxi.canvas.width = ctx.canvas.width;
	ctxi.canvas.height = ctx.canvas.height;
	ctxi.strokeStyle="red";
	ctxi.fillStyle = "rgba(255,0,0,0.3)";

	// add event listeners
	canvasi.addEventListener('mousedown', ev_mousedown, false);
	canvasi.addEventListener('mousemove', ev_mousemove, false);
	canvasi.addEventListener('mouseup',   ev_mouseup, false);

	var offset=0;
	var ysize = ctx.canvas.height;
	for(var i=0;i<chr_lengths.length;i++) {
		var xsize = ctx.canvas.width*(chr_lengths[i]/total_len);
		do_scatter(ctx,study,i+1,offset,xsize,ysize,chr_lengths[i]);
		offset += xsize;
	}
}

function do_scatter(ctx,study,chr,offset,xsize,ysize,chr_length) {
	var minbins=3;
	var fudge = 0.99;
	$.getJSON("/scatter/GWAS/"+study+"/"+chr+"/"+Math.floor(xsize/sc)+"/"+Math.floor(ysize/sc)+"/"+fudge+"/"+0+"/"+chr_length+"/"+0+"/"+global_max+"/"+minbins,
		function(json) {
			var xrange = chr_length;
			var yrange = global_max;
			var xfactor = xsize/xrange;
			var yfactor = ysize/yrange;
			if (chr % 2 === 0) {
				ctx.fillStyle="darkorange";
			} else {
				ctx.fillStyle="black";
			}
			for(var i=0; i<json.data.length; i++) {
				var rect = json.data[i];
				if (rect.length == 2) {
					// not a rectangle, just a point
					var x = xfactor*rect[0] + offset;
					var y = ysize - sc - yfactor*rect[1];
					if (circles) {
						ctx.beginPath();
						ctx.arc(Math.floor(x),Math.floor(y),sc,0,2*Math.PI,true);
						ctx.closePath();
						ctx.fill();
					} else {
						ctx.fillRect(Math.floor(x),Math.floor(y),sc,sc);
					}
				} else {
					var width = xfactor*(rect[1]-rect[0]);
					var height = sc+yfactor*(rect[3]-rect[2]);
					var x = xfactor*rect[0] + offset;
					var y = ysize - height - yfactor*rect[2];
					if (height < sc) {height=sc;}
					if (width < sc) {width = sc;}
					ctx.fillRect(Math.floor(x),Math.floor(y),Math.ceil(width),Math.ceil(height));
				}
			}
		});
}

function manhattan_plot_dots(canvasid,study) {
	// fetch the list of chromosomes and their lengths
	$.getJSON("/chromosomes/at",
		function(json1) {
			for(var i=0;i<json1.length;i++) {
				chr_lengths[i] = json1[i][1];
				total_len += json1[i][1];
			}
			// fetch the max score for this study
			$.getJSON("/maxscore/GWAS/"+study,
			function(json2) {
				global_max = Math.ceil(json2[0][0]);
				draw_manhattan_dots(canvasid,study);
			}
		);
	});
}

function draw_manhattan_dots(canvasid,study) {
  // get the canvas
	var canvas = document.getElementById(canvasid);
	var ctx = canvas.getContext('2d');
	ctx.canvas.width = window.innerWidth-20;
	ctx.canvas.height = window.innerHeight - 20;
	ctx.strokeStyle="black";
	ctx.strokeRect(0,0,ctx.canvas.width,ctx.canvas.height);

	// resize the interactive layer
	var canvasi = document.getElementById(canvasid+'i');
	ctxi = canvasi.getContext('2d');
	ctxi.canvas.width = ctx.canvas.width;
	ctxi.canvas.height = ctx.canvas.height;
	ctxi.strokeStyle="red";
	ctxi.fillStyle = "rgba(255,0,0,0.3)";

	// add event listeners
	canvasi.addEventListener('mousedown', ev_mousedown, false);
	canvasi.addEventListener('mousemove', ev_mousemove, false);
	canvasi.addEventListener('mouseup',   ev_mouseup, false);

	var offset=0;
	var ysize = ctx.canvas.height;
	for(var i=0;i<chr_lengths.length;i++) {
		var xsize = ctx.canvas.width*(chr_lengths[i]/total_len);
		do_scatter_dots(ctx,study,i+1,offset,xsize,ysize,chr_lengths[i]);
		offset += xsize;
	}
}

function do_scatter_dots(ctx,study,chr,offset,xsize,ysize,chr_length) {
	$.getJSON("/scatter/GWAS/nobinning/"+study+"/"+chr,
		function(json) {
			var xrange = chr_length;
			var yrange = global_max;
			var xfactor = xsize/xrange;
			var yfactor = ysize/yrange;
			if (chr % 2 === 0) {
				ctx.fillStyle="darkorange";
			} else {
				ctx.fillStyle="black";
			}
			for(var i=0; i<json.length; i++) {
				var x = xfactor*json[i][0] + offset;
				var y = ysize - 2 - yfactor*json[i][1];
				if (circles) {
					ctx.beginPath();
					ctx.arc(Math.floor(x),Math.floor(y),sc,0,2*Math.PI,true);
					ctx.closePath();
					ctx.fill();
				} else {
					ctx.fillRect(Math.floor(x),Math.floor(y),sc,sc);
				}
			}
		});
}

function ev_mousedown (ev) {
	if (ev.layerX || ev.layerX == 0) {
		ev._x = ev.layerX;
		ev._y = ev.layerY;
	} else if (ev.offsetX || ev.offsetX == 0) {
		ev._x = ev.offsetX;
		ev._y = ev.offsetY;
	}
	tool.started = true;
	tool.x = ev._x;
	tool.y = ev._y;
	ctxi.clearRect(0,0,ctxi.canvas.width,ctxi.canvas.height);
	ctxi.strokeRect(tool.x,tool.y,1,1);
}

function ev_mousemove (ev) {
	if (tool.started) {
		if (ev.layerX || ev.layerX == 0) {
			ev._x = ev.layerX;
			ev._y = ev.layerY;
		} else if (ev.offsetX || ev.offsetX == 0) {
			ev._x = ev.offsetX;
			ev._y = ev.offsetY;
		}
		var x = tool.x < ev._x ? tool.x : ev._x;
		var y = tool.y < ev._y ? tool.y : ev._y;
		var width=Math.abs(ev._x - tool.x + 1);
		var height=Math.abs(ev._y - tool.y + 1);
		ctxi.clearRect(0,0,ctxi.canvas.width,ctxi.canvas.height);
		ctxi.fillRect(x,y,width,height);
		ctxi.strokeRect(x, y, width, height);
	}
}

function ev_mouseup (ev) {
	if (tool.started) {
		if (ev.layerX || ev.layerX == 0) {
			ev._x = ev.layerX;
			ev._y = ev.layerY;
		} else if (ev.offsetX || ev.offsetX == 0) {
			ev._x = ev.offsetX;
			ev._y = ev.offsetY;
		}
		var x1 = tool.x;
		var x2 = ev._x;
		var y1 = tool.y;
		var y2 = ev._y;
		if (tool.x > ev._x) {
			x1 = ev._x;
			x2 = tool.x;
		}
		if (tool.y > ev._y) {
			y1 = ev._y;
			y2 = tool.y;
		}
		// need to convert x, y pixels to intervals on chromosomes and scores
		alert("selected rectangle (" + x1 + "," + y1 + "," + x2 + "," + y2 + ")");
		tool.started = false;
		ctxi.clearRect(0,0,ctxi.canvas.width,ctxi.canvas.height);
	}
}
<|endoftext|>


<commit_before>/*global $,cyberDojo*/

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

  cd.dialog_revert = function(title, id, avatarName, t) {

	var self = cd.dialog_revert;
	var tag = t;
	var data = undefined;
	var preview = undefined;

    self.runTestsWithRevertTag = function() {
      var form = cd.testForm();
      var action = form.attr('action');
      var revert_action = action + '&revert_tag=' + tag;
      form.attr('action', revert_action);
      form.submit();
      form.attr('action', action);
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.revertTagInfo = function(tag) {
      var colour = tag.colour;
      var avatarImage =
        '';
      var filename = 'traffic_light_' + colour;
      if (tag.revert_tag !== null) {
        filename += '_revert';
      }
      var trafficLight =
        "";

	  return '' +
	    '
' +
		  '
' +
		    '' +
			  avatarImage +
		    '	' +
		    '' +
			  trafficLight +
		    '	' +
		  '
' +
		'
';
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.revertTagFilenames = function() {
      var div = $('
');
	  var filenames = [ ];
      var filename;
      for (filename in data.visibleFiles) {
        filenames.push(filename);
      }
      filenames.sort();
      $.each(filenames, function(_, filename) {
        var f = $('
', {
          'class': 'filename',
          'id': 'radio_' + filename,
          'text': filename
        });
        div.append(f);
      });
      return div;
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.navigateButton = function(name) {
	  return '
' +
		'' +
	  '
';
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.navigateButtons = function(tag) {
	  return '
' +
	    '
' +
          '
' +
            '' +
               self.navigateButton('first') +
			'	' +
			'' +
               self.navigateButton('prev') +
			'	' +
			'' +
			  '' +
				' ' + tag.number + ' ' +
			  '' +
			'	' +
            '' +
               self.navigateButton('next') +
			'	' +
			'' +
               self.navigateButton('last') +
			'	' +
		  '
' +
		'
' +
	  '
';
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.reverterDiv = function()  {
      var div = $('
', {
        'id': 'revert_dialog'
      });
      var table = $('
');
      table.append(
        "
" +
          "" +
			self.revertTagInfo(data.inc) +
          "	" +
          "" +
           "<textarea id='revert_content' wrap='off'></textarea>" +
          "	" +
	    "
" +
		"
" +
          "" +
		    self.navigateButtons(data.inc) +
          "	" +
        "
" +
		"
" +
          "" +
		    "
" +
			  self.revertTagFilenames(data.visibleFiles).html() +
			"
" +
          "	" +
        "
");

      div.append(table);
	  var textArea = $('#revert_content', div);
	  textArea.attr('readonly', 'readonly');
	  textArea.addClass('file_content');
      return div;
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.showContentOnFilenameClick = function() {
	  var textArea = $('#revert_content', preview);
	  var previous = undefined;
	  $('.filename', preview).each(function() {
		$(this).click(function() {
		  var filename = $(this).text();
		  var content = data.visibleFiles[filename];
		  textArea.val(content);
		  if (previous !== undefined) {
			previous.removeClass('selected');
		  }
		  $(this).addClass('selected');
		  previous = $(this);
		});
	  });
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.setupNavigateButtonHandlers = function() {

	  var refresh = function() {
		$.getJSON('/reverter/revert',
		  {
			id: id,
			avatar: avatarName,
			tag: tag
		  },
		  function(d) {
			data = d;
			$('#filenames', preview).html(self.revertTagFilenames().html());
	        self.showContentOnFilenameClick();
    	    $('.filename', preview)[0].click();
		  }
		);
	  };

	  $('#prev_button', preview).click(function() {
		tag -= 1;
		refresh();
	  });

	  $('#next_button', preview).click(function() {
		tag += 1;
		refresh();
	  });
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.createRevertDialog = function() {
	  self.showContentOnFilenameClick();
	  $('.filename', preview)[0].click();
	  self.setupNavigateButtonHandlers();

	  return preview.dialog({
		  title: cd.dialogTitle(title),
		  autoOpen: false,
		  width: 950,
		  modal: true,
		  buttons: {
			ok: function() {
			  var newFilename;
			  $.each(cd.filenames(), function(n, filename) {
				if (filename !== 'output') {
				  cd.doDelete(filename);
				}
			  });
			  for (newFilename in data.visibleFiles) {
				if (newFilename !== 'output') {
				  cd.newFileContent(newFilename, data.visibleFiles[newFilename]);
				}
			  }
			  self.runTestsWithRevertTag();
			  $(this).dialog('close');
			}, // revert: ... {
			cancel: function() {
			  $(this).dialog('close');
			} // cancel: ... {
		  } // buttons: {
		}); // preview.dialog({
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    $.getJSON('/reverter/revert',
      {
        id: id,
        avatar: avatarName,
        tag: tag
      },
	  function(d) {
		data = d;
	    preview = self.reverterDiv();
		self.createRevertDialog().dialog('open');
	  }
    );

  }; // cd.dialog_revert = function(title, id, avatarName, t) {

  return cd;
})(cyberDojo || {}, $);

<commit_msg>refactoring<commit_after>/*global $,cyberDojo*/

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

  cd.dialog_revert = function(title, id, avatarName, t) {

	var self = cd.dialog_revert;
	var tag = t;
	var data = undefined;
	var preview = undefined;

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.runTestsWithRevertTag = function() {
      var form = cd.testForm();
      var action = form.attr('action');
      var revert_action = action + '&revert_tag=' + tag;
      form.attr('action', revert_action);
      form.submit();
      form.attr('action', action);
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.revertTagInfo = function(tag) {
      var colour = tag.colour;
      var avatarImage =
        '';
      var filename = 'traffic_light_' + colour;
      if (tag.revert_tag !== null) {
        filename += '_revert';
      }
      var trafficLight =
        "";

	  return '' +
	    '
' +
		  '
' +
		    '' +
			  avatarImage +
		    '	' +
		    '' +
			  trafficLight +
		    '	' +
		  '
' +
		'
';
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.revertTagFilenames = function() {
      var div = $('
');
	  var filenames = [ ];
      var filename;
      for (filename in data.visibleFiles) {
        filenames.push(filename);
      }
      filenames.sort();
      $.each(filenames, function(_, filename) {
        var f = $('
', {
          'class': 'filename',
          'id': 'radio_' + filename,
          'text': filename
        });
        div.append(f);
      });
      return div;
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.navigateButton = function(name) {
	  return '
' +
		'' +
	  '
';
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.navigateButtons = function(tag) {
	  return '
' +
	    '
' +
          '
' +
            '' +
               self.navigateButton('first') +
			'	' +
			'' +
               self.navigateButton('prev') +
			'	' +
			'' +
			  '' +
				' ' + tag.number + ' ' +
			  '' +
			'	' +
            '' +
               self.navigateButton('next') +
			'	' +
			'' +
               self.navigateButton('last') +
			'	' +
		  '
' +
		'
' +
	  '
';
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.reverterDiv = function()  {
      var div = $('
', {
        'id': 'revert_dialog'
      });
      var table = $('
');
      table.append(
        "
" +
          "" +
			self.revertTagInfo(data.inc) +
          "	" +
          "" +
           "<textarea id='revert_content' wrap='off'></textarea>" +
          "	" +
	    "
" +
		"
" +
          "" +
		    self.navigateButtons(data.inc) +
          "	" +
        "
" +
		"
" +
          "" +
		    "
" +
			  self.revertTagFilenames(data.visibleFiles).html() +
			"
" +
          "	" +
        "
");

      div.append(table);
	  var textArea = $('#revert_content', div);
	  textArea.attr('readonly', 'readonly');
	  textArea.addClass('file_content');
      return div;
    };

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.showContentOnFilenameClick = function() {
	  var textArea = $('#revert_content', preview);
	  var previous = undefined;
	  $('.filename', preview).each(function() {
		$(this).click(function() {
		  var filename = $(this).text();
		  var content = data.visibleFiles[filename];
		  textArea.val(content);
		  if (previous !== undefined) {
			previous.removeClass('selected');
		  }
		  $(this).addClass('selected');
		  previous = $(this);
		});
	  });
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.refresh = function() {
	  $.getJSON('/reverter/revert',
		{
		  id: id,
		  avatar: avatarName,
		  tag: tag
		},
		function(d) {
		  data = d;
		  $('#filenames', preview).html(self.revertTagFilenames().html());
		  self.showContentOnFilenameClick();
		  $('.filename', preview)[0].click();
		}
	  );
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    self.setupNavigateButtonHandlers = function() {
	  $('#prev_button', preview).click(function() {
		tag -= 1;
		self.refresh();
	  });
	  $('#next_button', preview).click(function() {
		tag += 1;
		self.refresh();
	  });
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

	self.createRevertDialog = function() {
	  self.showContentOnFilenameClick();
	  $('.filename', preview)[0].click();
	  self.setupNavigateButtonHandlers();

	  return preview.dialog({
		  title: cd.dialogTitle(title),
		  autoOpen: false,
		  width: 950,
		  modal: true,
		  buttons: {
			ok: function() {
			  var newFilename;
			  $.each(cd.filenames(), function(n, filename) {
				if (filename !== 'output') {
				  cd.doDelete(filename);
				}
			  });
			  for (newFilename in data.visibleFiles) {
				if (newFilename !== 'output') {
				  cd.newFileContent(newFilename, data.visibleFiles[newFilename]);
				}
			  }
			  self.runTestsWithRevertTag();
			  $(this).dialog('close');
			}, // revert: ... {
			cancel: function() {
			  $(this).dialog('close');
			} // cancel: ... {
		  } // buttons: {
		}); // preview.dialog({
	};

    //- - - - - - - - - - - - - - - - - - - - - - - - - -

    $.getJSON('/reverter/revert',
      {
        id: id,
        avatar: avatarName,
        tag: tag
      },
	  function(d) {
		data = d;
	    preview = self.reverterDiv();
		self.createRevertDialog().dialog('open');
	  }
    );

  }; // cd.dialog_revert = function(title, id, avatarName, t) {

  return cd;
})(cyberDojo || {}, $);

<|endoftext|>


<commit_before>var faye = require('faye')
    path = require('path'),
    http = require('http'),
    fs = require('fs'),
    request = require('request');

var source="snapper1";//fixme@@

var configPath = path.join(__dirname, '..', '..', 'shared', 'config.json'),
    config = require(configPath),
    client = new faye.Client( fayeUrl(config.collector) );

client.subscribe('/trigger', handleTrigger);
console.log("subscribed to trigger");

// Send a heartbeat every few secs (defined in config)
setInterval(heartbeat, config.heartbeatIntervalSecs * 1000);

// Tell everyone we're here
function heartbeat() {
  client.publish('/heartbeat', { id: '1', type: 'emitter' });
}

function fayeUrl(url) {
  return 'http://' + url.host + ':' + url.port + '/faye';
}

function handleTrigger(msg) {
  var faceFilename = '../snapper/images/camcvface.jpg';
  console.log('Trigger message received');
   try {
      console.log('faye: got message', typeof msg,  msg);
      console.log(msg.trigger);
      // load file from the expected place if it's not too old
      console.log('looking for creation time of '+path.join(__dirname, faceFilename));
      fs.stat(path.join(__dirname, faceFilename), fsDateCallback);
   } catch(e) {
      console.log("problem");
      console.error(e.stack);
   }
}

function fsDateCallback(err, data){
   var anyFilename = '../snapper/images/camcvimage.jpg';
   var faceFilename = '../snapper/images/camcvface.jpg';
   var threshold = 10*1000; //diff is in milliseconds, so this gives threshold in secs - should be in config?
   if(err){
      console.log("error: assuming no file, sending whatever we have");
      sendImage(anyFilename);

   }else{
     console.log("fs date callback ok so far");
     console.log(data.ctime);

     var startDate = data.ctime.getTime();
     var endDate = Date.now();
     var diff = endDate - startDate;
     console.log('startDate '+startDate+' endDate '+endDate+' diff '+diff/1000+' threshold '+threshold+' secs');
     if(diff < threshold){
       console.log('face image is ok, sending');
       sendImage(faceFilename);
     }else{
       console.log('face image is too old with threshold '+threshold+' - sending non-face');
       sendImage(anyFilename);
     }
   }
}

function sendImage(filename){
   var r = request.post('http://'+config.collector.host+':'+config.collector.port+'/image', requestCallback);
   var form = r.form()
   form.append("folder_id", "0");
   form.append("source", source);
   console.log("uploading "+path.join(__dirname, filename)+" to "+config.collector.host+":"+config.collector.port+"/image");
   form.append("name", fs.createReadStream(path.join(__dirname, filename)));
}

function requestCallback(err, res, body) {
   if(err){
      console.log("error");
   }
   console.log("ok");
   console.log(body);
}

<commit_msg>Improved imageSender with non-hardcoded hostname<commit_after>var faye = require('faye')
    path = require('path'),
    http = require('http'),
    fs = require('fs'),
    request = require('request'),
    os = require("os");

var source = os.hostname();

console.log("source is "+source);

var configPath = path.join(__dirname, '..', '..', 'shared', 'config.json'),
    config = require(configPath),
    client = new faye.Client( fayeUrl(config.collector) );

client.subscribe('/trigger', handleTrigger);
console.log("subscribed to trigger");

// Send a heartbeat every few secs (defined in config)
setInterval(heartbeat, config.heartbeatIntervalSecs * 1000);

// Tell everyone we're here
function heartbeat() {
  client.publish('/heartbeat', { id: '1', type: 'emitter' });
}

function fayeUrl(url) {
  return 'http://' + url.host + ':' + url.port + '/faye';
}

function handleTrigger(msg) {
  var faceFilename = '../snapper/images/camcvface.jpg';
  console.log('Trigger message received');
   try {
      console.log('faye: got message', typeof msg,  msg);
      console.log(msg.trigger);
      // load file from the expected place if it's not too old
      console.log('looking for creation time of '+path.join(__dirname, faceFilename));
      fs.stat(path.join(__dirname, faceFilename), fsDateCallback);
   } catch(e) {
      console.log("problem");
      console.error(e.stack);
   }
}

function fsDateCallback(err, data){
   var anyFilename = '../snapper/images/camcvimage.jpg';
   var faceFilename = '../snapper/images/camcvface.jpg';
   var threshold = 10*1000; //diff is in milliseconds, so this gives threshold in secs - should be in config?
   if(err){
      console.log("error: assuming no file, sending whatever we have");
      sendImage(anyFilename);

   }else{
     console.log("fs date callback ok so far");
     console.log(data.ctime);

     var startDate = data.ctime.getTime();
     var endDate = Date.now();
     var diff = endDate - startDate;
     console.log('startDate '+startDate+' endDate '+endDate+' diff '+diff/1000+' threshold '+threshold+' secs');
     if(diff < threshold){
       console.log('face image is ok, sending');
       sendImage(faceFilename);
     }else{
       console.log('face image is too old with threshold '+threshold+' - sending non-face');
       sendImage(anyFilename);
     }
   }
}

function sendImage(filename){
   var r = request.post('http://'+config.collector.host+':'+config.collector.port+'/image', requestCallback);
   var form = r.form()
   form.append("folder_id", "0");
   form.append("source", source);
   console.log("uploading "+path.join(__dirname, filename)+" to "+config.collector.host+":"+config.collector.port+"/image");
   form.append("name", fs.createReadStream(path.join(__dirname, filename)));
}

function requestCallback(err, res, body) {
   if(err){
      console.log("error");
   }
   console.log("ok");
   console.log(body);
}

<|endoftext|>