<commit_before>//A single survey

function ResponseShowView(responseID) {
  var _ = require('lib/underscore')._;
  var Survey = require('models/survey');
  var Answer = require('models/answer');
  var Question = require('models/question');
  var Response = require('models/response');
  var TopLevelView = require('ui/common/components/TopLevelView');
  var ButtonView = require('ui/common/components/ButtonView');
  var SeparatorView = require('ui/common/components/SeparatorView');
  var Palette = require('ui/common/components/Palette');
  var Toast = require('ui/common/components/Toast');
  var ConfirmDialog = require('ui/common/components/ConfirmDialog');
  var Measurements = require('ui/common/components/Measurements');

  var convertResponseDataForTable = function() {
    var response = Response.findOneById(responseID);
    var answers = response.answers();
    var responses = _(answers).map(function(answer) {
      var row = Ti.UI.createTableViewRow({
        layout : 'vertical',
        className : 'responseShowTableRow'
      });

      var questionLabel = Ti.UI.createLabel({
        text : Question.findOneById(answer.question_id).content,
        color : Palette.PRIMARY_COLOR,
        font : {
          fontSize : '20dip',
          fontStyle : 'bold'
        },
        left : Measurements.PADDING_SMALL
      });

      var answerLabel = Ti.UI.createLabel({
        text : answer.contentForDisplay() || '',
        color : Palette.PRIMARY_COLOR,
        left : Measurements.PADDING_SMALL,
        font : {
          fontSize : Measurements.FONT_MEDIUM        }
      });

      row.add(questionLabel);
      row.add(answerLabel);

      if (answer.isImage()) {
        var imageView = Ti.UI.createImageView({
          width : 100,
          height : 100,
          image : answer.image
        });
        row.add(imageView);
      }

      row.add(new SeparatorView(Palette.SECONDARY_COLOR_LIGHT, Measurements.PADDING_SMALL));
      row.add(new SeparatorView(Palette.WHITE, Measurements.PADDING_SMALL));
      return (row);
    });
    return responses;
  };
  var self = new TopLevelView('Response Details');

  // now assign that array to the table's data property to add those objects as rows
  var table = Titanium.UI.createTableView({
    separatorColor : 'transparent',
    top : self.headerHeight,
    data : convertResponseDataForTable()
  });

  var responseEditButton = new ButtonView('Edit', {
    width : '48%',
    left : '1%'
  });

  var responseDeleteButton = new ButtonView('Delete', {
    width : '48%',
    right : '1%'
  });

  responseEditButton.addEventListener('click', function(e) {
    self.fireEvent('ResponseShowView:responseEdit', {
      responseID : responseID
    });
  });

  var activityIndicator = Ti.UI.Android.createProgressIndicator({
    message : L('response_delete'),
    location : Ti.UI.Android.PROGRESS_INDICATOR_DIALOG,
    type : Ti.UI.Android.PROGRESS_INDICATOR_INDETERMINANT
  });
  self.add(activityIndicator);

  responseDeleteButton.addEventListener('click', function(e) {
    var confirmDialog = new ConfirmDialog("Delete", "Are you sure you want to delete the response?", onConfirm = function(e) {
      activityIndicator.show();
      var response = Response.findOneById(responseID);
      response.destroyAnswers();
      response.destroy();
      (new Toast('Response deleted')).show();
      self.fireEvent('ResponseShowView:responseDeleted', {
        responseID : responseID
      });
      activityIndicator.hide();
    });
    confirmDialog.show();
  });

  var buttonsView = Ti.UI.createView({
    layout : 'horizontal'
  });
  buttonsView.add(new SeparatorView(Palette.SECONDARY_COLOR_LIGHT, Measurements.PADDING_SMALL));
  buttonsView.add(responseEditButton);
  buttonsView.add(new SeparatorView(Palette.SECONDARY_COLOR_LIGHT, Measurements.PADDING_SMALL, { width : '2%'}));
  buttonsView.add(responseDeleteButton);

  table.setHeaderView(buttonsView);

  var refreshView = function() {
    table.setData(convertResponseDataForTable());
  };

  Ti.App.addEventListener('updatedResponse', refreshView);
  // self.add(buttonsView);
  self.add(table);

  self.cleanup = function() {
    self.remove(table);
    table = null;
  };

  return self;
}

module.exports = ResponseShowView;
<commit_msg>Add a check if table is defined or not before calling setData<commit_after>//A single survey

function ResponseShowView(responseID) {
  var _ = require('lib/underscore')._;
  var Survey = require('models/survey');
  var Answer = require('models/answer');
  var Question = require('models/question');
  var Response = require('models/response');
  var TopLevelView = require('ui/common/components/TopLevelView');
  var ButtonView = require('ui/common/components/ButtonView');
  var SeparatorView = require('ui/common/components/SeparatorView');
  var Palette = require('ui/common/components/Palette');
  var Toast = require('ui/common/components/Toast');
  var ConfirmDialog = require('ui/common/components/ConfirmDialog');
  var Measurements = require('ui/common/components/Measurements');

  var convertResponseDataForTable = function() {
    var response = Response.findOneById(responseID);
    var answers = response.answers();
    var responses = _(answers).map(function(answer) {
      var row = Ti.UI.createTableViewRow({
        layout : 'vertical',
        className : 'responseShowTableRow'
      });

      var questionLabel = Ti.UI.createLabel({
        text : Question.findOneById(answer.question_id).content,
        color : Palette.PRIMARY_COLOR,
        font : {
          fontSize : '20dip',
          fontStyle : 'bold'
        },
        left : Measurements.PADDING_SMALL
      });

      var answerLabel = Ti.UI.createLabel({
        text : answer.contentForDisplay() || '',
        color : Palette.PRIMARY_COLOR,
        left : Measurements.PADDING_SMALL,
        font : {
          fontSize : Measurements.FONT_MEDIUM        }
      });

      row.add(questionLabel);
      row.add(answerLabel);

      if (answer.isImage()) {
        var imageView = Ti.UI.createImageView({
          width : 100,
          height : 100,
          image : answer.image
        });
        row.add(imageView);
      }

      row.add(new SeparatorView(Palette.SECONDARY_COLOR_LIGHT, Measurements.PADDING_SMALL));
      row.add(new SeparatorView(Palette.WHITE, Measurements.PADDING_SMALL));
      return (row);
    });
    return responses;
  };
  var self = new TopLevelView('Response Details');

  // now assign that array to the table's data property to add those objects as rows
  var table = Titanium.UI.createTableView({
    separatorColor : 'transparent',
    top : self.headerHeight,
    data : convertResponseDataForTable()
  });

  var responseEditButton = new ButtonView('Edit', {
    width : '48%',
    left : '1%'
  });

  var responseDeleteButton = new ButtonView('Delete', {
    width : '48%',
    right : '1%'
  });

  responseEditButton.addEventListener('click', function(e) {
    self.fireEvent('ResponseShowView:responseEdit', {
      responseID : responseID
    });
  });

  var activityIndicator = Ti.UI.Android.createProgressIndicator({
    message : L('response_delete'),
    location : Ti.UI.Android.PROGRESS_INDICATOR_DIALOG,
    type : Ti.UI.Android.PROGRESS_INDICATOR_INDETERMINANT
  });
  self.add(activityIndicator);

  responseDeleteButton.addEventListener('click', function(e) {
    var confirmDialog = new ConfirmDialog("Delete", "Are you sure you want to delete the response?", onConfirm = function(e) {
      activityIndicator.show();
      var response = Response.findOneById(responseID);
      response.destroyAnswers();
      response.destroy();
      (new Toast('Response deleted')).show();
      self.fireEvent('ResponseShowView:responseDeleted', {
        responseID : responseID
      });
      activityIndicator.hide();
    });
    confirmDialog.show();
  });

  var buttonsView = Ti.UI.createView({
    layout : 'horizontal'
  });
  buttonsView.add(new SeparatorView(Palette.SECONDARY_COLOR_LIGHT, Measurements.PADDING_SMALL));
  buttonsView.add(responseEditButton);
  buttonsView.add(new SeparatorView(Palette.SECONDARY_COLOR_LIGHT, Measurements.PADDING_SMALL, { width : '2%'}));
  buttonsView.add(responseDeleteButton);

  table.setHeaderView(buttonsView);

  var refreshView = function() {
    if (table)
      table.setData(convertResponseDataForTable());
  };

  Ti.App.addEventListener('updatedResponse', refreshView);
  // self.add(buttonsView);
  self.add(table);

  self.cleanup = function() {
    self.remove(table);
    table = null;
  };

  return self;
}

module.exports = ResponseShowView;
<|endoftext|>


<commit_before>$(function(){
    app.views = app.views || {};

    function parseAltitudeMode(ge, modeString) {
        // Return an AltitudeMode object corresponding to the given string.
        var kml = ('<Document>'
                   +'<Placemark>'
                   +'<Point>'
                   +'<coordinates>0,0</coordinates>'
                   +'<altitudeMode>'+modeString+'</altitudeMode>'
                   +'</Point>'
                   +'</Placemark>'
                   +'</Document>');
        return (ge.parseKml(kml)
                .getFeatures()
                .getFirstChild()
                .getGeometry()
                .getAltitudeMode());
    }

    app.views.EarthView = Backbone.View.extend({
        el: 'div',

        events:{
            //'earth:loaded': 'render',
            //'earth:init': 'drawPlan',
        },

        initialize: function(){
            _.bindAll(this);
            if ( ! app.options.offline ) {
                this.on('earth:loaded', this.render);
                google.load('earth', '1', {
                    callback: this.render,
                });
            } else {
                this.$el.css({'background-color': 'blue', 'height': '800px'});
            }
        },

       render: function(){
            google.earth.createInstance(this.el, _.bind(this.earthInit, this), _.bind(this.earthFailure, this));
        },

        earthInit: function(ge){
            this.ge = ge;
            window.ge = ge;
            ge.getWindow().setVisibility(true);
            app.vent.trigger("earth:init");

            // Configure the Earth instance
            ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
            ge.getLayerRoot().enableLayerById(ge.LAYER_BORDERS, true);
            ge.getLayerRoot().enableLayerById(ge.LAYER_ROADS, true);
            ge.getOptions().setScaleLegendVisibility(true);
            ge.getOptions().setUnitsFeetMiles(false);
            ge.getOptions().setFlyToSpeed(ge.SPEED_TELEPORT);

            // Disable the terrain
            ge.getLayerRoot().enableLayerById(ge.LAYER_TERRAIN, false);

            ge.gex = new GEarthExtensions(ge);

            app.options.XGDS_PLANNER_CLAMP_MODE_JS =
                parseAltitudeMode(this.ge, app.options.XGDS_PLANNER2_CLAMP_MODE_KML || 'relativeToSeaFloor');

            app.vent.trigger('earth:init');
            this.trigger('earth:init');
            this.drawPlan();
        },

        earthFailure: function(){
            alert("Earth plugin failed to load.");
        },

        drawPlan: function(){
            if (this.planView){ alert("PlanView was previosly rendered."); }
            this.planView = new PlanKmlView({
                collection: app.currentPlan.get('sequence'),
                ge: this.ge,
            });
            this.planView.render();
            this.ge.getFeatures().appendChild(this.planView.doc);
            this.ge.gex.util.flyToObject( this.planView.doc );
        },

    });

    // This view class manages a KML Document object that represents an entire plan.
    // On instantiation, pass in the plan sequence Backbone collection as the "collection" arguement.
    var PlanKmlView = Backbone.View.extend({
        // KML boilerplate and styles live in templates/handlebars/plan-kml.handlebars
        template: Handlebars.compile($('#template-plan-kml').html()),
        geEvents: [], // container holds GE events for later removal

        initialize: function(){
            var ge = this.ge = this.options.ge;
            var doc = this.doc = ge.parseKml( this.template( {options: app.options} ) );
            this.stationsFolder = ge.gex.dom.buildFolder({ name: "stations" });
            this.segmentsFolder = ge.gex.dom.buildFolder({ name: "segments" });
            this.kmlFolders = [this.stationsFolder, this.segmentsFolder];
            _.each( this.kmlFolders, function(folder){ doc.getFeatures().appendChild(folder); });

            // re-rendering the whole KML View on add proves to be pretty slow.
            //this.collection.on('add', this.render, this);
            app.vent.on('mapmode', this.setMode, this);
            app.vent.trigger('mapmode', 'navigate');

            this.collection.resequence();  // Sometimes it doesn't resequence itself on load
            this.collection.plan.kmlView = this; // This is here so we can reference it via global scope from inside GE Event handlers.  Grrrr....
        },

        clearKmlFolder: function(folder){
            var featureContainer = folder.getFeatures();
            while( featureContainer.hasChildNodes() ) { featureContainer.removeChild( featureContainer.getLastChild() ); }
        },

        render: function(){
            _.each( this.kmlFolders, this.clearKmlFolder);
            this.drawStations();
            this.drawSegments();
            if (this.currentMode) {
                this.resetMode();
            }
        },

        drawStation: function(station){
            var stationPointView = new StationPointView({ge: this.ge, model: station});
            this.stationsFolder.getFeatures().appendChild(stationPointView.placemark);
        },

        drawStations: function(){
            _.each(
                this.collection.filter( function(model){ return model.get('type') == 'Station'; } ),
                this.drawStation,
                this // view context
            );
        },

        drawSegment: function(segment, fromStation, toStation){
            var segmentLineView = new SegmentLineView({
                model: segment,
                fromStation: fromStation,
                toStation: toStation,
                ge: this.ge
            });
            this.segmentsFolder.getFeatures().appendChild(segmentLineView.placemark);
        },

        drawSegments: function(){
            if ( this.segmentsFolder.getFeatures().hasChildNodes() ) {
                this.clearKmlFolder( this.segmentsFolder);
            }
            this.collection.each(function(item, index, list){
                if (item.get('type') == 'Segment') {
                    var fromStation = list[index-1];
                    var toStation = list[index+1];
                    this.drawSegment(item, fromStation, toStation);
                }
            }, this);
        },

        addGeEvent: function(target, eventID, listenerCallback, useCapture) {
            this.geEvents.push(arguments);
            google.earth.addEventListener(target, eventID, listenerCallback, useCapture);
        },

        clearGeEvents: function() {
            while( this.geEvents.length > 0 ) {
                google.earth.removeEventListener.apply(google.earth, this.geEvents.pop() );
            }
        },

        setMode: function(modeName){
            console.log("Set mouse mode: " + modeName);
            var modeMap = {
                'addStations': 'addStationsMode',
                'navigate': 'navigateMode',
                'reposition': 'repositionMode',
            };

            if ( this.currentMode ) { this.currentMode.exit.call(this); }
            var mode = _.isObject(modeName) ? modeName : this[modeMap[modeName]];
            mode.enter.call(this);
            this.currentMode = mode;
        },

        resetMode: function() {
            if ( this.currentMode) {
                var mode = this.currentMode;
                mode.exit();
                mode.enter();
            }
        },

        addStationsMode: {
            enter: function(){
                this.addGeEvent(this.ge.getGlobe(), 'click', this.clickAddStation);
            },
            exit: function(){
                this.clearGeEvents();
            },
        }, // end addStationMode

        navigateMode: {
            enter: function(){},
            exit: function(){}
        },

        repositionMode: {
            enter: function(){
                var planview = this;
                var stations = this.stationsFolder.getFeatures().getChildNodes();
                var l = stations.getLength();
                for ( var station,i=0; i<l; i++ ) {
                    station = stations.item(i);
                    this.ge.gex.edit.makeDraggable(station, {
                        bounce: false,
                        dragCallback: function(){
                            this.view.model.trigger('dragUpdate', this);
                        },
                        dropCallback: function(){
                            // "this" is the placemark GE object.
                            var model = this.view.model;
                            var point = this.getGeometry();
                            model.setPoint(point.getLongitude(), point.getLatitude());
                            planview.render();
                        },
                    });
                }
                this.drawMidpoints();
            }, // end enter
            exit: function(){
                this.destroyMidpoints();
                var stations = this.stationsFolder.getFeatures().getChildNodes();
                var l = stations.getLength();
                for ( var station,i=0; i<l; i++ ) {
                    station = stations.item(i);
                    this.ge.gex.edit.endDraggable(station);
                }
            },
        }, // end repositionMode

        clickAddStation: function(evt){
            console.log("Add Station");
            var coords = [evt.getLongitude(), evt.getLatitude()];
            var station = app.models.stationFactory({ coordinates: coords });
            var segment = app.models.segmentFactory();
            app.currentPlan.get('sequence').appendStation(station);

            // Jump through some hoops to avoid a slow total re-render.  Not really thrilled with this solution.
            app.currentPlan.kmlView.drawStation(station);
            var seq = app.currentPlan.get('sequence');
            var l = seq.length;
            app.currentPlan.kmlView.drawSegment( seq.at(l-2), seq.at(l-3), seq.at(l-1) );
        },

        drawMidpoints: function(){
            if (! this.midpoitnsFolder) { this.midpointsFolder = this.ge.gex.dom.buildFolder({ name: "midpoints" }); }
            this.doc.getFeatures().appendChild(this.midpointsFolder);
            var fldrFeatures = this.midpointsFolder.getFeatures();

            this.collection.each(function(item, idx, list){
                var station1, station2;
                if ( item.get('type') == "Segment" ) {
                    station1 = list[idx - 1];
                    station2 = list[idx + 1];
                    midpoint = midpointPlacemark({
                        ge: this.ge,
                        segment: item,
                        index: idx,
                        station1: station1,
                        station2: station2,
                        view: this
                    });
                    fldrFeatures.appendChild(midpoint);
                }
            }, this);
            this.kmlFolders.push(this.midpointsFolder);
        },

        destroyMidpoints: function(){
            this.doc.getFeatures().removeChild( this.midpointsFolder );
            this.kmlFolders.pop();
            delete this.midpointsFolder;
        },
    });

    // This view class manages the map point for a single Station model
    var StationPointView = Backbone.View.extend({
        initialize: function(){
            var gex = this.options.ge.gex;
            var pmOptions = {};
            pmOptions.name = this.model.get('sequenceLabel') || this.model.toString();
            pmOptions.altitudeMode = app.options.plannerClampMode || this.options.ge.ALTITUDE_CLAMP_TO_GROUND;
            pmOptions.style = '#waypoint';
            var point =  this.model.get('geometry').coordinates;
            pmOptions.point = [ point[1], point[0] ]; // Lon, Lat
            this.placemark = gex.dom.buildPlacemark(
                pmOptions
            );
            this.placemark.view = this; // 2-way link for GE event handlers to use
            this.model.on('change', this.redraw, this);
        },

        redraw: function(){
            // redraw code. To be invoked when relevant model attributes change.
            var kmlPoint = this.placemark.getGeometry();
            var coords = this.model.get('geometry').coordinates;
            coords = [coords[1], coords[0]];
            kmlPoint.setLatLng.apply(kmlPoint, coords);
            this.placemark.setName( this.model.get('sequenceLabel') || this.model.toString() );
        },
    });

    var SegmentLineView = Backbone.View.extend({
        initialize: function(){
            var options = this.options;
            if ( ! options.ge && options.toStation && options.fromStation) { throw "Missing a required option!"; }
            this.ge = this.options.ge;
            this.gex = this.options.ge.gex;
            this.fromStation = this.options.fromStation;
            this.toStation = this.options.toStation;
            this.otherStation = {};
            this.otherStation[options.toStation.cid]= options.fromStation;
            this.otherStation[options.fromStation.cid]= options.toStation;
            _.each([this.fromStation, this.toStation], function(stationModel){
                stationModel.on('change:geometry', function(){ this.update(stationModel); }, this);
                stationModel.on('dragUpdate', function( placemark ) {
                    var geom = placemark.getGeometry();
                    var coords = [geom.getLatitude(), geom.getLongitude()];
                    this.update( this.otherStation[placemark.view.model.cid], coords );
                }, this);
            }, this);
            this.render();
        },

        render: function(){
            var coords = _.map( [this.fromStation, this.toStation], function(station){
                var geom = station.get('geometry').coordinates;
                return [geom[1], geom[0]]; // Lon, Lat
            });

            var linestring = this.gex.dom.buildLineString(coords, {tessellate: true});
            this.placemark = this.gex.dom.buildPlacemark({
                lineString: linestring,
                style: '#segment',
                altitudeMode: app.options.plannerClampMode || this.ge.ALTITUDE_CLAMP_TO_GROUND,
            });
            this.update.apply(this, coords );
        },

        /*
        ** Update the endpoints of the segment when either adjacent station changes.
        ** points can be either station PathElements, or an array of coordinates (lon, lat)
        ** You can also supply just one station PathElement
        */
        update: function(point1, point2){

            if ( _.isUndefined(point2) && point1.cid ) {
                // only one model was supplied for update.  Go get the other one.
                point2 = this.otherStation[point1.cid];
            }

            var coords = [];
            _.each([point1, point2], function(point) {
                if ( _.isArray(point) ) {
                    coords.push(point);
                } else if ( _.isObject(point) && _.isFunction(point.get) ) {
                    var geom = point.get('geometry').coordinates;
                    coords.push( [geom[1], geom[0]] ); // Lon, Lat
                }
            });

            var linestring = this.gex.dom.buildLineString(coords, {tessellate: true});
            this.placemark.setGeometry(linestring); // ???
        },
    });

    /*
    ** Calculate the midpoint from an array of arrays of coordinates.
    */
    var calcMidpoint = function(coordsArr){
        var sums = _.reduce( coordsArr, function(memo, arr) {
                var result = [];
                _.each(arr, function(n, idx){
                    result.push(n + memo[idx]);
                });
                return result
            },
            _.map(coordsArr[0], function(){return 0;})
        );
        return _.map(sums, function(n){ return n / coordsArr.length });
    };

    var midpointPlacemark = function(options){
        var points = [];
        _.each([options.station1,options.station2], function(station){
            var coords = station.get('geometry').coordinates;
            points.push([coords[1], coords[0]]);
        });
        var midpoint = calcMidpoint(points);
        var placemark = this.ge.gex.dom.buildPlacemark({
            point: midpoint,
            style: "#midpoint"
        });

        options.ge.gex.edit.makeDraggable(placemark, {
            bounce: false,
            dropCallback: function(){
                var view = options.view, index = options.index;
                var geom = this.getGeometry();
                var station = app.models.stationFactory({
                    coordinates: [geom.getLongitude(), geom.getLatitude()],
                });
                view.collection.insertStation( index, station );
                view.render(); //redraw
                view.drawMidpoints();
            },
        });

        return placemark;
    };

});
<commit_msg>Fixes some problems with mouse mode-switching<commit_after>$(function(){
    app.views = app.views || {};

    function parseAltitudeMode(ge, modeString) {
        // Return an AltitudeMode object corresponding to the given string.
        var kml = ('<Document>'
                   +'<Placemark>'
                   +'<Point>'
                   +'<coordinates>0,0</coordinates>'
                   +'<altitudeMode>'+modeString+'</altitudeMode>'
                   +'</Point>'
                   +'</Placemark>'
                   +'</Document>');
        return (ge.parseKml(kml)
                .getFeatures()
                .getFirstChild()
                .getGeometry()
                .getAltitudeMode());
    }

    app.views.EarthView = Backbone.View.extend({
        el: 'div',

        events:{
            //'earth:loaded': 'render',
            //'earth:init': 'drawPlan',
        },

        initialize: function(){
            _.bindAll(this);
            if ( ! app.options.offline ) {
                this.on('earth:loaded', this.render);
                google.load('earth', '1', {
                    callback: this.render,
                });
            } else {
                this.$el.css({'background-color': 'blue', 'height': '800px'});
            }
        },

       render: function(){
            google.earth.createInstance(this.el, _.bind(this.earthInit, this), _.bind(this.earthFailure, this));
        },

        earthInit: function(ge){
            this.ge = ge;
            window.ge = ge;
            ge.getWindow().setVisibility(true);
            app.vent.trigger("earth:init");

            // Configure the Earth instance
            ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
            ge.getLayerRoot().enableLayerById(ge.LAYER_BORDERS, true);
            ge.getLayerRoot().enableLayerById(ge.LAYER_ROADS, true);
            ge.getOptions().setScaleLegendVisibility(true);
            ge.getOptions().setUnitsFeetMiles(false);
            ge.getOptions().setFlyToSpeed(ge.SPEED_TELEPORT);

            // Disable the terrain
            ge.getLayerRoot().enableLayerById(ge.LAYER_TERRAIN, false);

            ge.gex = new GEarthExtensions(ge);

            app.options.XGDS_PLANNER_CLAMP_MODE_JS =
                parseAltitudeMode(this.ge, app.options.XGDS_PLANNER2_CLAMP_MODE_KML || 'relativeToSeaFloor');

            app.vent.trigger('earth:init');
            this.trigger('earth:init');
            this.drawPlan();
        },

        earthFailure: function(){
            alert("Earth plugin failed to load.");
        },

        drawPlan: function(){
            if (this.planView){ alert("PlanView was previosly rendered."); }
            this.planView = new PlanKmlView({
                collection: app.currentPlan.get('sequence'),
                ge: this.ge,
            });
            this.planView.render();
            this.ge.getFeatures().appendChild(this.planView.doc);
            this.ge.gex.util.flyToObject( this.planView.doc );
        },

    });

    // This view class manages a KML Document object that represents an entire plan.
    // On instantiation, pass in the plan sequence Backbone collection as the "collection" arguement.
    var PlanKmlView = Backbone.View.extend({
        // KML boilerplate and styles live in templates/handlebars/plan-kml.handlebars
        template: Handlebars.compile($('#template-plan-kml').html()),
        geEvents: [], // container holds GE events for later removal

        initialize: function(){
            var ge = this.ge = this.options.ge;
            var doc = this.doc = ge.parseKml( this.template( {options: app.options} ) );
            this.stationsFolder = ge.gex.dom.buildFolder({ name: "stations" });
            this.segmentsFolder = ge.gex.dom.buildFolder({ name: "segments" });
            this.kmlFolders = [this.stationsFolder, this.segmentsFolder];
            _.each( this.kmlFolders, function(folder){ doc.getFeatures().appendChild(folder); });

            // re-rendering the whole KML View on add proves to be pretty slow.
            //this.collection.on('add', this.render, this);
            app.vent.on('mapmode', this.setMode, this);
            app.vent.trigger('mapmode', 'navigate');

            this.collection.resequence();  // Sometimes it doesn't resequence itself on load
            this.collection.plan.kmlView = this; // This is here so we can reference it via global scope from inside GE Event handlers.  Grrrr....
        },

        clearKmlFolder: function(folder){
            var featureContainer = folder.getFeatures();
            while( featureContainer.hasChildNodes() ) { featureContainer.removeChild( featureContainer.getLastChild() ); }
        },

        render: function(){
            _.each( this.kmlFolders, this.clearKmlFolder);
            this.drawStations();
            this.drawSegments();
            if (this.currentMode) {
                this.resetMode();
            }
        },

        drawStation: function(station){
            var stationPointView = new StationPointView({ge: this.ge, model: station});
            this.stationsFolder.getFeatures().appendChild(stationPointView.placemark);
        },

        drawStations: function(){
            _.each(
                this.collection.filter( function(model){ return model.get('type') == 'Station'; } ),
                this.drawStation,
                this // view context
            );
        },

        drawSegment: function(segment, fromStation, toStation){
            var segmentLineView = new SegmentLineView({
                model: segment,
                fromStation: fromStation,
                toStation: toStation,
                ge: this.ge
            });
            this.segmentsFolder.getFeatures().appendChild(segmentLineView.placemark);
        },

        drawSegments: function(){
            if ( this.segmentsFolder.getFeatures().hasChildNodes() ) {
                this.clearKmlFolder( this.segmentsFolder);
            }
            this.collection.each(function(item, index, list){
                if (item.get('type') == 'Segment') {
                    var fromStation = list[index-1];
                    var toStation = list[index+1];
                    this.drawSegment(item, fromStation, toStation);
                }
            }, this);
        },

        addGeEvent: function(target, eventID, listenerCallback, useCapture) {
            this.geEvents.push(arguments);
            google.earth.addEventListener(target, eventID, listenerCallback, useCapture);
        },

        clearGeEvents: function() {
            while( this.geEvents.length > 0 ) {
                google.earth.removeEventListener.apply(google.earth, this.geEvents.pop() );
            }
        },

        setMode: function(modeName){
            console.log("Set mouse mode: " + modeName);
            var modeMap = {
                'addStations': 'addStationsMode',
                'navigate': 'navigateMode',
                'reposition': 'repositionMode',
            };

            if ( this.currentMode ) { this.currentMode.exit.call(this); }
            var mode = _.isObject(modeName) ? modeName : this[modeMap[modeName]];
            mode.enter.call(this);
            this.currentMode = mode;
        },

        // Clean up, then re-enter the mode.  Useful for re-draws/
        resetMode: function() {
            if ( this.currentMode) {
                var mode = this.currentMode;
                mode.exit.call(this);
                mode.enter.call(this);
            }
        },

        addStationsMode: {
            enter: function(){
                this.addGeEvent(this.ge.getGlobe(), 'click', this.clickAddStation);
            },
            exit: function(){
                this.clearGeEvents();
            },
        }, // end addStationMode

        navigateMode: {
            enter: function(){},
            exit: function(){}
        },

        repositionMode: {
            enter: function(){
                var planview = this;
                var stations = this.stationsFolder.getFeatures().getChildNodes();
                var l = stations.getLength();
                for ( var station,i=0; i<l; i++ ) {
                    station = stations.item(i);
                    this.ge.gex.edit.makeDraggable(station, {
                        bounce: false,
                        dragCallback: function(){
                            this.view.model.trigger('dragUpdate', this);
                        },
                        dropCallback: function(){
                            // "this" is the placemark GE object.
                            var model = this.view.model;
                            var point = this.getGeometry();
                            model.setPoint(point.getLongitude(), point.getLatitude());
                            planview.render();
                        },
                    });
                }
                this.drawMidpoints();
            }, // end enter
            exit: function(){
                this.destroyMidpoints();
                var stations = this.stationsFolder.getFeatures().getChildNodes();
                var l = stations.getLength();
                for ( var station,i=0; i<l; i++ ) {
                    station = stations.item(i);
                    this.ge.gex.edit.endDraggable(station);
                }
            },
        }, // end repositionMode

        clickAddStation: function(evt){
            console.log("Add Station");
            var coords = [evt.getLongitude(), evt.getLatitude()];
            var station = app.models.stationFactory({ coordinates: coords });
            var segment = app.models.segmentFactory();
            app.currentPlan.get('sequence').appendStation(station);

            // Jump through some hoops to avoid a slow total re-render.  Not really thrilled with this solution.
            app.currentPlan.kmlView.drawStation(station);
            var seq = app.currentPlan.get('sequence');
            var l = seq.length;
            app.currentPlan.kmlView.drawSegment( seq.at(l-2), seq.at(l-3), seq.at(l-1) );
        },

        drawMidpoints: function(){
            if (! this.midpoitnsFolder) { this.midpointsFolder = this.ge.gex.dom.buildFolder({ name: "midpoints" }); }
            this.doc.getFeatures().appendChild(this.midpointsFolder);
            var fldrFeatures = this.midpointsFolder.getFeatures();

            this.collection.each(function(item, idx, list){
                var station1, station2;
                if ( item.get('type') == "Segment" ) {
                    station1 = list[idx - 1];
                    station2 = list[idx + 1];
                    midpoint = midpointPlacemark({
                        ge: this.ge,
                        segment: item,
                        index: idx,
                        station1: station1,
                        station2: station2,
                        view: this
                    });
                    fldrFeatures.appendChild(midpoint);
                }
            }, this);
            this.kmlFolders.push(this.midpointsFolder);
        },

        destroyMidpoints: function(){
            this.doc.getFeatures().removeChild( this.midpointsFolder );
            this.kmlFolders.pop();
            delete this.midpointsFolder;
        },
    });

    // This view class manages the map point for a single Station model
    var StationPointView = Backbone.View.extend({
        initialize: function(){
            var gex = this.options.ge.gex;
            var pmOptions = {};
            pmOptions.name = this.model.get('sequenceLabel') || this.model.toString();
            pmOptions.altitudeMode = app.options.plannerClampMode || this.options.ge.ALTITUDE_CLAMP_TO_GROUND;
            pmOptions.style = '#waypoint';
            var point =  this.model.get('geometry').coordinates;
            pmOptions.point = [ point[1], point[0] ]; // Lon, Lat
            this.placemark = gex.dom.buildPlacemark(
                pmOptions
            );
            this.placemark.view = this; // 2-way link for GE event handlers to use
            this.model.on('change', this.redraw, this);
        },

        redraw: function(){
            // redraw code. To be invoked when relevant model attributes change.
            var kmlPoint = this.placemark.getGeometry();
            var coords = this.model.get('geometry').coordinates;
            coords = [coords[1], coords[0]];
            kmlPoint.setLatLng.apply(kmlPoint, coords);
            this.placemark.setName( this.model.get('sequenceLabel') || this.model.toString() );
        },
    });

    var SegmentLineView = Backbone.View.extend({
        initialize: function(){
            var options = this.options;
            if ( ! options.ge && options.toStation && options.fromStation) { throw "Missing a required option!"; }
            this.ge = this.options.ge;
            this.gex = this.options.ge.gex;
            this.fromStation = this.options.fromStation;
            this.toStation = this.options.toStation;
            this.otherStation = {};
            this.otherStation[options.toStation.cid]= options.fromStation;
            this.otherStation[options.fromStation.cid]= options.toStation;
            _.each([this.fromStation, this.toStation], function(stationModel){
                stationModel.on('change:geometry', function(){ this.update(stationModel); }, this);
                stationModel.on('dragUpdate', function( placemark ) {
                    var geom = placemark.getGeometry();
                    var coords = [geom.getLatitude(), geom.getLongitude()];
                    this.update( this.otherStation[placemark.view.model.cid], coords );
                }, this);
            }, this);
            this.render();
        },

        render: function(){
            var coords = _.map( [this.fromStation, this.toStation], function(station){
                var geom = station.get('geometry').coordinates;
                return [geom[1], geom[0]]; // Lon, Lat
            });

            var linestring = this.gex.dom.buildLineString(coords, {tessellate: true});
            this.placemark = this.gex.dom.buildPlacemark({
                lineString: linestring,
                style: '#segment',
                altitudeMode: app.options.plannerClampMode || this.ge.ALTITUDE_CLAMP_TO_GROUND,
            });
            this.update.apply(this, coords );
        },

        /*
        ** Update the endpoints of the segment when either adjacent station changes.
        ** points can be either station PathElements, or an array of coordinates (lon, lat)
        ** You can also supply just one station PathElement
        */
        update: function(point1, point2){

            if ( _.isUndefined(point2) && point1.cid ) {
                // only one model was supplied for update.  Go get the other one.
                point2 = this.otherStation[point1.cid];
            }

            var coords = [];
            _.each([point1, point2], function(point) {
                if ( _.isArray(point) ) {
                    coords.push(point);
                } else if ( _.isObject(point) && _.isFunction(point.get) ) {
                    var geom = point.get('geometry').coordinates;
                    coords.push( [geom[1], geom[0]] ); // Lon, Lat
                }
            });

            var linestring = this.gex.dom.buildLineString(coords, {tessellate: true});
            this.placemark.setGeometry(linestring); // ???
        },
    });

    /*
    ** Calculate the midpoint from an array of arrays of coordinates.
    */
    var calcMidpoint = function(coordsArr){
        var sums = _.reduce( coordsArr, function(memo, arr) {
                var result = [];
                _.each(arr, function(n, idx){
                    result.push(n + memo[idx]);
                });
                return result
            },
            _.map(coordsArr[0], function(){return 0;})
        );
        return _.map(sums, function(n){ return n / coordsArr.length });
    };

    var midpointPlacemark = function(options){
        var points = [];
        _.each([options.station1,options.station2], function(station){
            var coords = station.get('geometry').coordinates;
            points.push([coords[1], coords[0]]);
        });
        var midpoint = calcMidpoint(points);
        var placemark = this.ge.gex.dom.buildPlacemark({
            point: midpoint,
            style: "#midpoint"
        });

        options.ge.gex.edit.makeDraggable(placemark, {
            bounce: false,
            dropCallback: function(){
                var view = options.view, index = options.index;
                var geom = this.getGeometry();
                var station = app.models.stationFactory({
                    coordinates: [geom.getLongitude(), geom.getLatitude()],
                });
                view.collection.insertStation( index, station );
                view.render(); //redraw
            },
        });

        return placemark;
    };

});
<|endoftext|>


<commit_before>'use strict'
/* jshint expr: true */
/* global sinon: false */
/* global moment:false */

describe('Controller: AuditsCtrl', function () {
  // load the controller's module
  beforeEach(module('openhimConsoleApp'))

  // setup config constant to be used for API server details
  beforeEach(function () {
    module('openhimConsoleApp', function ($provide) {
      $provide.constant('config', { protocol: 'https', host: 'localhost', hostPath: '', port: 8080, title: 'Title', footerTitle: 'FooterTitle', footerPoweredBy: 'FooterPoweredBy' })
    })
  })

  var scope, createController, httpBackend, modalSpy, auditsEndpoint // eslint-disable-line

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, $httpBackend, $uibModal) {
    httpBackend = $httpBackend

    $httpBackend.when('GET', new RegExp('.*/audits-filter-options')).respond(
      {
        eventType: [{ code: 'ITI-9', displayName: 'PIX Read', codeSystemName: 'IHE Transactions' }],
        eventID: [{ code: '222', displayName: 'Read', codeSystemName: 'DCM' }],
        activeParticipantRoleID: [{ code: '110152', displayName: 'Destination', codeSystemName: 'DCM' }],
        participantObjectIDTypeCode: [{ code: '2', displayName: 'PatientNumber', codeSystemName: 'RFC-3881' }],
        auditSourceID: ['openhim']
      }
    )

    auditsEndpoint = $httpBackend.when('GET', new RegExp('.*/audits'))
    auditsEndpoint.respond([
      {
        rawMessage: 'This will be the raw ATNA message that gets received to be used as a backup reference',
        eventIdentification: {
          eventDateTime: '2015-02-17T15:38:25.282+02:00',
          eventOutcomeIndicator: '0',
          eventActionCode: 'R',
          eventID: { code: '222', displayName: 'Read', codeSystemName: 'DCM' },
          eventTypeCode: { code: 'ITI-9', displayName: 'PIX Read', codeSystemName: 'IHE Transactions' }
        },
        activeParticipant: [
          {
            userID: 'pix|pix',
            alternativeUserID: '2100',
            userIsRequestor: 'false',
            networkAccessPointID: 'localhost',
            networkAccessPointTypeCode: '1',
            roleIDCode: { code: '110152', displayName: 'Destination', codeSystemName: 'DCM' }
          }
        ],
        auditSourceIdentification: { auditSourceID: 'openhim' },
        participantObjectIdentification: [
          {
            participantObjectID: '975cac30-68e5-11e4-bf2a-04012ce65b02^^^ECID&ECID&ISO',
            participantObjectTypeCode: '1',
            participantObjectTypeCodeRole: '1',
            participantObjectIDTypeCode: { code: '2', displayName: 'PatientNumber', codeSystemName: 'RFC-3881' }
          }
        ]
      }, {
        rawMessage: 'This will be the raw ATNA message that gets received to be used as a backup reference',
        eventIdentification: {
          eventDateTime: '2015-02-17T15:38:25.282+02:00',
          eventOutcomeIndicator: '0',
          eventActionCode: 'R',
          eventID: { code: '222', displayName: 'Read', codeSystemName: 'DCM' },
          eventTypeCode: { code: 'ITI-9', displayName: 'PIX Read', codeSystemName: 'IHE Transactions' }
        },
        activeParticipant: [
          {
            userID: 'pix|pix',
            alternativeUserID: '2100',
            userIsRequestor: 'false',
            networkAccessPointID: 'localhost',
            networkAccessPointTypeCode: '1',
            roleIDCode: { code: '110152', displayName: 'Destination', codeSystemName: 'DCM' }
          }
        ],
        auditSourceIdentification: { auditSourceID: 'openhim' },
        participantObjectIdentification: [
          {
            participantObjectID: '975cac30-68e5-11e4-bf2a-04012ce65b02^^^ECID&ECID&ISO',
            participantObjectTypeCode: '1',
            participantObjectTypeCodeRole: '1',
            participantObjectIDTypeCode: { code: '2', displayName: 'PatientNumber', codeSystemName: 'RFC-3881' }
          }
        ]
      }
    ])

    $httpBackend.when('GET', new RegExp('.*/heartbeat')).respond({ now: Date.now() })

    modalSpy = sinon.spy($uibModal, 'open')

    createController = function () {
      scope = $rootScope.$new()
      return $controller('AuditsCtrl', { $scope: scope })
    }
  }))

  afterEach(function () {
    httpBackend.verifyNoOutstandingExpectation()
    httpBackend.verifyNoOutstandingRequest()
  })

  it('should attach a list of audits to the scope', function () {
    createController()
    httpBackend.flush()
    scope.audits.length.should.equal(2)
    scope.auditsFilterOptions.eventType.length.should.equal(1)
    scope.auditsFilterOptions.eventID.length.should.equal(1)
    scope.auditsFilterOptions.activeParticipantRoleID.length.should.equal(1)
    scope.auditsFilterOptions.participantObjectIDTypeCode.length.should.equal(1)
    scope.auditsFilterOptions.auditSourceID.length.should.equal(1)
  })

  it('should check that the user prefered filters are set', function () {
    createController()
    httpBackend.flush()

    // the consoleSession object is setup with user profile in 'login.js'
    scope.settings.filter.limit.should.equal(10)
    scope.settings.list.tabview.should.equal('new')
  })

  it('should check filters are sent to the API', function () {
    createController()
    httpBackend.flush()

    var startDate = '2015-03-09T00:00:00+00:00'
    var endDate = '2015-03-09T00:00:00+00:00'

    scope.settings.filter.limit = 10
    scope.settings.filter.dateStart = moment(startDate).format()
    scope.settings.filter.dateEnd = moment(endDate).format()
    scope.filters.eventIdentification.eventID = '222---Read---DCM'
    scope.filters.eventIdentification.eventTypeCode = 'ITI-9---PIX Read---IHE Transactions'
    scope.filters.eventIdentification.eventActionCode = 'R'
    scope.filters.eventIdentification.eventOutcomeIndicator = '0'
    scope.filters.participantObjectIdentification.patientID.patientID = '975cac30-68e5-11e4-bf2a-04012ce65b02'
    scope.filters.auditSourceIdentification.auditSourceID = 'openhim'

    var filters = scope.returnFilters('filtersObject')
    var urlParams = scope.returnFilters('urlParams')

    // filter object that gets sent through the API for query filtering
    filters.filterLimit.should.equal(10)
    filters.filters['eventIdentification.eventDateTime'].should.equal('{"$gte":"' + moment(startDate).format() + '","$lte":"' + moment(endDate).endOf('day').format() + '"}')
    filters.filters['participantObjectIdentification.participantObjectID'].should.equal('"975cac30-68e5-11e4-bf2a-04012ce65b02\\\\^\\\\^\\\\^.*&.*&.*"')
    filters.filters['eventIdentification.eventTypeCode.code'].should.equal('ITI-9')
    filters.filters['eventIdentification.eventTypeCode.codeSystemName'].should.equal('PIX Read')
    filters.filters['eventIdentification.eventTypeCode.displayName'].should.equal('IHE Transactions')
    filters.filters['eventIdentification.eventID.code'].should.equal('222')
    filters.filters['eventIdentification.eventID.codeSystemName'].should.equal('Read')
    filters.filters['eventIdentification.eventID.displayName'].should.equal('DCM')
    filters.filters['eventIdentification.eventActionCode'].should.equal('R')
    filters.filters['eventIdentification.eventOutcomeIndicator'].should.equal('0')
    filters.filters['auditSourceIdentification.auditSourceID'].should.equal('openhim')

    // url params string that gets used to reload the audits URL with selected paramaters
    urlParams.should.equal('&limit=10&dateStart=' + moment(startDate).format('YYYY-MM-DD') + '&dateEnd=' + moment(endDate).endOf('day').format('YYYY-MM-DD') + '&patientID=975cac30-68e5-11e4-bf2a-04012ce65b02&eventTypeCode=ITI-9---PIX Read---IHE Transactions&eventID=222---Read---DCM&eventActionCode=R&eventOutcomeIndicator=0&auditSourceID=openhim')
  })

  it('should prepend new audits to the scope', function () {
    createController()
    httpBackend.flush()

    var originalLength = scope.audits.length

    auditsEndpoint.respond([
      {
        eventIdentification: {
          eventDateTime: new Date(),
          eventOutcomeIndicator: '0'
        },
        auditSourceIdentification: { auditSourceID: 'test2' },
        rawMessage: 'auto-added'
      }
    ])

    scope.pollForLatest()
    httpBackend.flush()

    scope.audits.length.should.equal(originalLength + 1)
    scope.audits[0].rawMessage.should.equal('auto-added')
  })
})
<commit_msg>Update the test for Audits to use correct date format Test was failing because it was using incorrect date format OHM-1085 OHM-927<commit_after>'use strict'
/* jshint expr: true */
/* global sinon: false */
/* global moment:false */

describe('Controller: AuditsCtrl', function () {
  // load the controller's module
  beforeEach(module('openhimConsoleApp'))

  // setup config constant to be used for API server details
  beforeEach(function () {
    module('openhimConsoleApp', function ($provide) {
      $provide.constant('config', { protocol: 'https', host: 'localhost', hostPath: '', port: 8080, title: 'Title', footerTitle: 'FooterTitle', footerPoweredBy: 'FooterPoweredBy' })
    })
  })

  var scope, createController, httpBackend, modalSpy, auditsEndpoint // eslint-disable-line

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, $httpBackend, $uibModal) {
    httpBackend = $httpBackend

    $httpBackend.when('GET', new RegExp('.*/audits-filter-options')).respond(
      {
        eventType: [{ code: 'ITI-9', displayName: 'PIX Read', codeSystemName: 'IHE Transactions' }],
        eventID: [{ code: '222', displayName: 'Read', codeSystemName: 'DCM' }],
        activeParticipantRoleID: [{ code: '110152', displayName: 'Destination', codeSystemName: 'DCM' }],
        participantObjectIDTypeCode: [{ code: '2', displayName: 'PatientNumber', codeSystemName: 'RFC-3881' }],
        auditSourceID: ['openhim']
      }
    )

    auditsEndpoint = $httpBackend.when('GET', new RegExp('.*/audits'))
    auditsEndpoint.respond([
      {
        rawMessage: 'This will be the raw ATNA message that gets received to be used as a backup reference',
        eventIdentification: {
          eventDateTime: '2015-02-17T15:38:25.282+02:00',
          eventOutcomeIndicator: '0',
          eventActionCode: 'R',
          eventID: { code: '222', displayName: 'Read', codeSystemName: 'DCM' },
          eventTypeCode: { code: 'ITI-9', displayName: 'PIX Read', codeSystemName: 'IHE Transactions' }
        },
        activeParticipant: [
          {
            userID: 'pix|pix',
            alternativeUserID: '2100',
            userIsRequestor: 'false',
            networkAccessPointID: 'localhost',
            networkAccessPointTypeCode: '1',
            roleIDCode: { code: '110152', displayName: 'Destination', codeSystemName: 'DCM' }
          }
        ],
        auditSourceIdentification: { auditSourceID: 'openhim' },
        participantObjectIdentification: [
          {
            participantObjectID: '975cac30-68e5-11e4-bf2a-04012ce65b02^^^ECID&ECID&ISO',
            participantObjectTypeCode: '1',
            participantObjectTypeCodeRole: '1',
            participantObjectIDTypeCode: { code: '2', displayName: 'PatientNumber', codeSystemName: 'RFC-3881' }
          }
        ]
      }, {
        rawMessage: 'This will be the raw ATNA message that gets received to be used as a backup reference',
        eventIdentification: {
          eventDateTime: '2015-02-17T15:38:25.282+02:00',
          eventOutcomeIndicator: '0',
          eventActionCode: 'R',
          eventID: { code: '222', displayName: 'Read', codeSystemName: 'DCM' },
          eventTypeCode: { code: 'ITI-9', displayName: 'PIX Read', codeSystemName: 'IHE Transactions' }
        },
        activeParticipant: [
          {
            userID: 'pix|pix',
            alternativeUserID: '2100',
            userIsRequestor: 'false',
            networkAccessPointID: 'localhost',
            networkAccessPointTypeCode: '1',
            roleIDCode: { code: '110152', displayName: 'Destination', codeSystemName: 'DCM' }
          }
        ],
        auditSourceIdentification: { auditSourceID: 'openhim' },
        participantObjectIdentification: [
          {
            participantObjectID: '975cac30-68e5-11e4-bf2a-04012ce65b02^^^ECID&ECID&ISO',
            participantObjectTypeCode: '1',
            participantObjectTypeCodeRole: '1',
            participantObjectIDTypeCode: { code: '2', displayName: 'PatientNumber', codeSystemName: 'RFC-3881' }
          }
        ]
      }
    ])

    $httpBackend.when('GET', new RegExp('.*/heartbeat')).respond({ now: Date.now() })

    modalSpy = sinon.spy($uibModal, 'open')

    createController = function () {
      scope = $rootScope.$new()
      return $controller('AuditsCtrl', { $scope: scope })
    }
  }))

  afterEach(function () {
    httpBackend.verifyNoOutstandingExpectation()
    httpBackend.verifyNoOutstandingRequest()
  })

  it('should attach a list of audits to the scope', function () {
    createController()
    httpBackend.flush()
    scope.audits.length.should.equal(2)
    scope.auditsFilterOptions.eventType.length.should.equal(1)
    scope.auditsFilterOptions.eventID.length.should.equal(1)
    scope.auditsFilterOptions.activeParticipantRoleID.length.should.equal(1)
    scope.auditsFilterOptions.participantObjectIDTypeCode.length.should.equal(1)
    scope.auditsFilterOptions.auditSourceID.length.should.equal(1)
  })

  it('should check that the user prefered filters are set', function () {
    createController()
    httpBackend.flush()

    // the consoleSession object is setup with user profile in 'login.js'
    scope.settings.filter.limit.should.equal(10)
    scope.settings.list.tabview.should.equal('new')
  })

  it('should check filters are sent to the API', function () {
    createController()
    httpBackend.flush()

    var startDate = '2015-03-09'
    var endDate = '2015-03-09'

    scope.settings.filter.limit = 10
    scope.settings.filter.dateStart = moment(startDate).format()
    scope.settings.filter.dateEnd = moment(endDate).format()
    scope.filters.eventIdentification.eventID = '222---Read---DCM'
    scope.filters.eventIdentification.eventTypeCode = 'ITI-9---PIX Read---IHE Transactions'
    scope.filters.eventIdentification.eventActionCode = 'R'
    scope.filters.eventIdentification.eventOutcomeIndicator = '0'
    scope.filters.participantObjectIdentification.patientID.patientID = '975cac30-68e5-11e4-bf2a-04012ce65b02'
    scope.filters.auditSourceIdentification.auditSourceID = 'openhim'

    var filters = scope.returnFilters('filtersObject')
    var urlParams = scope.returnFilters('urlParams')

    // filter object that gets sent through the API for query filtering
    filters.filterLimit.should.equal(10)
    filters.filters['eventIdentification.eventDateTime'].should.equal('{"$gte":"' + moment(startDate).format() + '","$lte":"' + moment(endDate).endOf('day').format() + '"}')
    filters.filters['participantObjectIdentification.participantObjectID'].should.equal('"975cac30-68e5-11e4-bf2a-04012ce65b02\\\\^\\\\^\\\\^.*&.*&.*"')
    filters.filters['eventIdentification.eventTypeCode.code'].should.equal('ITI-9')
    filters.filters['eventIdentification.eventTypeCode.codeSystemName'].should.equal('PIX Read')
    filters.filters['eventIdentification.eventTypeCode.displayName'].should.equal('IHE Transactions')
    filters.filters['eventIdentification.eventID.code'].should.equal('222')
    filters.filters['eventIdentification.eventID.codeSystemName'].should.equal('Read')
    filters.filters['eventIdentification.eventID.displayName'].should.equal('DCM')
    filters.filters['eventIdentification.eventActionCode'].should.equal('R')
    filters.filters['eventIdentification.eventOutcomeIndicator'].should.equal('0')
    filters.filters['auditSourceIdentification.auditSourceID'].should.equal('openhim')

    // url params string that gets used to reload the audits URL with selected paramaters
    urlParams.should.equal('&limit=10&dateStart=' + moment(startDate).format('YYYY-MM-DD') + '&dateEnd=' + moment(endDate).endOf('day').format('YYYY-MM-DD') + '&patientID=975cac30-68e5-11e4-bf2a-04012ce65b02&eventTypeCode=ITI-9---PIX Read---IHE Transactions&eventID=222---Read---DCM&eventActionCode=R&eventOutcomeIndicator=0&auditSourceID=openhim')
  })

  it('should prepend new audits to the scope', function () {
    createController()
    httpBackend.flush()

    var originalLength = scope.audits.length

    auditsEndpoint.respond([
      {
        eventIdentification: {
          eventDateTime: new Date(),
          eventOutcomeIndicator: '0'
        },
        auditSourceIdentification: { auditSourceID: 'test2' },
        rawMessage: 'auto-added'
      }
    ])

    scope.pollForLatest()
    httpBackend.flush()

    scope.audits.length.should.equal(originalLength + 1)
    scope.audits[0].rawMessage.should.equal('auto-added')
  })
})
<|endoftext|>


<commit_before>(function(){
	"use strict";

    var isNumber = function(n) {
      return !isNaN(parseFloat(n)) && isFinite(n);
    };

    var helpers = Chart.helpers;

	Chart.types.Bar.extend({
		name: "BarColoured",
        initialize:  function(data){

			//Expose options as a scope variable here so we can access it in the ScaleClass
			var options = this.options;

			this.ScaleClass = Chart.Scale.extend({
				offsetGridLines : true,
				calculateBarX : function(datasetCount, datasetIndex, barIndex){
					//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
					var xWidth = this.calculateBaseWidth(),
						xAbsolute = this.calculateX(barIndex) - (xWidth/2),
						barWidth = this.calculateBarWidth(datasetCount);

					return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
				},
				calculateBaseWidth : function(){
					return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
				},
				calculateBarWidth : function(datasetCount){
					//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
					var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

					return (baseWidth / datasetCount);
				}
			});

			this.datasets = [];

			//Set up tooltip events on the chart
			if (this.options.showTooltips){
				helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
					var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

					this.eachBars(function(bar){
						bar.restore(['fillColor', 'strokeColor']);
					});
					helpers.each(activeBars, function(activeBar){
						activeBar.fillColor = activeBar.highlightFill;
						activeBar.strokeColor = activeBar.highlightStroke;
					});
					this.showTooltip(activeBars);
				});
			}

			//Declare the extension of the default point, to cater for the options passed in to the constructor
			this.BarClass = Chart.Rectangle.extend({
				strokeWidth : this.options.barStrokeWidth,
				showStroke : this.options.barShowStroke,
				ctx : this.chart.ctx
			});

			//Iterate through each of the datasets, and build this into a property of the chart
			helpers.each(data.datasets,function(dataset,datasetIndex){

				var datasetObject = {
					label : dataset.label || null,
					fillColor : dataset.fillColor,
					strokeColor : dataset.strokeColor,
					bars : []
				};

				this.datasets.push(datasetObject);

                // Datapoint
                /*
                {
                    value: value,
                    fillColor: colour,
                    strokeColor: colour,
                    highlightFile: colour,
                    highlightStroke: colour
                }
                 */

				helpers.each(dataset.data,function(dataPoint,index){
                    if (isNumber(dataPoint)) {
                        dataPoint = {
                            value: dataPoint
                        };
                    }

					//Add a new point for each piece of data, passing any required data to draw.
					datasetObject.bars.push(new this.BarClass({
						value : dataPoint.value,
						label : data.labels[index],
						datasetLabel: dataset.label,
						strokeColor : dataPoint.strokeColor || dataset.strokeColor,
						fillColor : dataPoint.fillColor || dataset.fillColor,
						highlightFill : dataPoint.highlightFill || dataset.highlightFill || dataset.fillColor,
						highlightStroke : dataPoint.highlightStroke || dataset.highlightStroke || dataset.strokeColor
					}));
				},this);

			},this);

			this.buildScale(data.labels);

			this.BarClass.prototype.base = this.scale.endPoint;

			this.eachBars(function(bar, index, datasetIndex){
				helpers.extend(bar, {
					width : this.scale.calculateBarWidth(this.datasets.length),
					x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
					y: this.scale.endPoint
				});
				bar.save();
			}, this);

			this.render();
		},
        addData : function(valuesArray,label){
			//Map the values array for each of the datasets
			helpers.each(valuesArray,function(value,datasetIndex){
                   if (isNumber(value)) {
                        value = {
                            value: value
                        };
                    }

				//Add a new point for each piece of data, passing any required data to draw.
				this.datasets[datasetIndex].bars.push(new this.BarClass({
					value : value.value,
					label : label,
					x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
					y: this.scale.endPoint,
					width : this.scale.calculateBarWidth(this.datasets.length),
					base : this.scale.endPoint,
					strokeColor : value.strokeColor || this.datasets[datasetIndex].strokeColor,
					fillColor : value.fillColor || this.datasets[datasetIndex].fillColor
				}));
			},this);

			this.scale.addXLabel(label);
			//Then re-render the chart.
			this.update();
		},
    });

}).call(this);<commit_msg>Add license<commit_after>/*
 * Copyright (c) 2014, Dr Daniel Naylor. Licenced under the MIT Licence.
 */
(function(){
	"use strict";

    var isNumber = function(n) {
      return !isNaN(parseFloat(n)) && isFinite(n);
    };

    var helpers = Chart.helpers;

	Chart.types.Bar.extend({
		name: "BarColoured",
        initialize:  function(data){

			//Expose options as a scope variable here so we can access it in the ScaleClass
			var options = this.options;

			this.ScaleClass = Chart.Scale.extend({
				offsetGridLines : true,
				calculateBarX : function(datasetCount, datasetIndex, barIndex){
					//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
					var xWidth = this.calculateBaseWidth(),
						xAbsolute = this.calculateX(barIndex) - (xWidth/2),
						barWidth = this.calculateBarWidth(datasetCount);

					return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
				},
				calculateBaseWidth : function(){
					return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
				},
				calculateBarWidth : function(datasetCount){
					//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
					var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

					return (baseWidth / datasetCount);
				}
			});

			this.datasets = [];

			//Set up tooltip events on the chart
			if (this.options.showTooltips){
				helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
					var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

					this.eachBars(function(bar){
						bar.restore(['fillColor', 'strokeColor']);
					});
					helpers.each(activeBars, function(activeBar){
						activeBar.fillColor = activeBar.highlightFill;
						activeBar.strokeColor = activeBar.highlightStroke;
					});
					this.showTooltip(activeBars);
				});
			}

			//Declare the extension of the default point, to cater for the options passed in to the constructor
			this.BarClass = Chart.Rectangle.extend({
				strokeWidth : this.options.barStrokeWidth,
				showStroke : this.options.barShowStroke,
				ctx : this.chart.ctx
			});

			//Iterate through each of the datasets, and build this into a property of the chart
			helpers.each(data.datasets,function(dataset,datasetIndex){

				var datasetObject = {
					label : dataset.label || null,
					fillColor : dataset.fillColor,
					strokeColor : dataset.strokeColor,
					bars : []
				};

				this.datasets.push(datasetObject);

                // Datapoint
                /*
                {
                    value: value,
                    fillColor: colour,
                    strokeColor: colour,
                    highlightFile: colour,
                    highlightStroke: colour
                }
                 */

				helpers.each(dataset.data,function(dataPoint,index){
                    if (isNumber(dataPoint)) {
                        dataPoint = {
                            value: dataPoint
                        };
                    }

					//Add a new point for each piece of data, passing any required data to draw.
					datasetObject.bars.push(new this.BarClass({
						value : dataPoint.value,
						label : data.labels[index],
						datasetLabel: dataset.label,
						strokeColor : dataPoint.strokeColor || dataset.strokeColor,
						fillColor : dataPoint.fillColor || dataset.fillColor,
						highlightFill : dataPoint.highlightFill || dataset.highlightFill || dataset.fillColor,
						highlightStroke : dataPoint.highlightStroke || dataset.highlightStroke || dataset.strokeColor
					}));
				},this);

			},this);

			this.buildScale(data.labels);

			this.BarClass.prototype.base = this.scale.endPoint;

			this.eachBars(function(bar, index, datasetIndex){
				helpers.extend(bar, {
					width : this.scale.calculateBarWidth(this.datasets.length),
					x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
					y: this.scale.endPoint
				});
				bar.save();
			}, this);

			this.render();
		},
        addData : function(valuesArray,label){
			//Map the values array for each of the datasets
			helpers.each(valuesArray,function(value,datasetIndex){
                   if (isNumber(value)) {
                        value = {
                            value: value
                        };
                    }

				//Add a new point for each piece of data, passing any required data to draw.
				this.datasets[datasetIndex].bars.push(new this.BarClass({
					value : value.value,
					label : label,
					x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
					y: this.scale.endPoint,
					width : this.scale.calculateBarWidth(this.datasets.length),
					base : this.scale.endPoint,
					strokeColor : value.strokeColor || this.datasets[datasetIndex].strokeColor,
					fillColor : value.fillColor || this.datasets[datasetIndex].fillColor
				}));
			},this);

			this.scale.addXLabel(label);
			//Then re-render the chart.
			this.update();
		},
    });

}).call(this);
<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  computed,
  Mixin
  } = Ember;

const STATE_LIST = ['culled', 'hidden', 'visible'];

const STATE_TRANSITIONS_UP = [
  { state: 'culled', method: '' },
  { state: 'hidden', method: '_ov_insert' },
  { state: 'visible', method: '_ov_reveal' }
  ];

const STATE_TRANSITIONS_DOWN = [
  { state: 'visible', method: '' },
  { state: 'hidden', method: '_ov_obscure' },
  { state: 'culled', method: '_ov_teardown' }
  ];

export default Mixin.create({

  viewState: 'culled',

  contentVisible: false,
  contentHidden: false,
  contentInserted: false,
  contentCulled: false,

  show() {
    this._setState('visible');
  },

  hide() {
    this._setState('hidden');
  },

  cull() {
    this._setState('culled');
  },

  _setState(toState) {
    var fromState = this.get('viewState');
    var currentState = fromState;

    if (fromState === toState) {
      return;
    }

    var transitionMap = STATE_LIST.indexOf(fromState) < STATE_LIST.indexOf(toState) ? STATE_TRANSITIONS_UP : STATE_TRANSITIONS_DOWN;
    var i;

    for (i = 0; i < transitionMap.length; i++) {
      if (transitionMap[i].state === fromState) {
        break;
      }
    }
    i++;

    while (transitionMap[i] && currentState !== toState) {
      if (transitionMap[i].method) {
        this[transitionMap[i].method]();
        currentState = transitionMap[i].state;
      }
      i++;
    }

    this.set('viewState', toState);
  },

  /**!
   * Destroy the View/Element
   *
   * Unlike the other methods, this method
   * can be called from any state. It is still not valid
   * to transition to it directly, but willDestroy uses it
   * to teardown the instance.
   *
   * @private
   */
  _ov_teardown() {
    let heightProp = this.get('heightProperty');
    if (this.get('contentInserted') && this.element) {
      this.element.style[heightProp] = this.get('_position.rect.height') + 'px';
    }
    this.setProperties({ contentCulled: true, contentHidden: false, contentInserted: false });
  },

  /**!
   * Insert the Element into the DOM in a hidden state.
   *
   * @private
   */
  _ov_insert() {
    this.setProperties({ contentHidden: true, contentCulled: false, contentInserted: true });
  },

  /**!
   * Reveal the Element
   *
   * @private
   */
  _ov_reveal() {
    let heightProp = this.get('heightProperty');
    this.setProperties({ contentHidden: false, contentVisible: true, contentInserted: true });
    this.element.style[heightProp] = null;
    this.element.style.visibility = 'visible';
  },

  /**!
   * Hide the Element
   *
   * @private
   */
  _ov_obscure() {
    this.setProperties({ contentHidden: true, contentVisible: false, contentInserted: true });
    this.element.style.visibility = 'hidden';
  }

});
<commit_msg>optimizes use of heightProperty by making non-observable and if alwaysUseDefaultHeight is false forces a resize of the position tracker during teardown. This could be further optimized to only occur during the first teardown.<commit_after>import Ember from 'ember';

const {
  computed,
  Mixin
  } = Ember;

const STATE_LIST = ['culled', 'hidden', 'visible'];

const STATE_TRANSITIONS_UP = [
  { state: 'culled', method: '' },
  { state: 'hidden', method: '_ov_insert' },
  { state: 'visible', method: '_ov_reveal' }
  ];

const STATE_TRANSITIONS_DOWN = [
  { state: 'visible', method: '' },
  { state: 'hidden', method: '_ov_obscure' },
  { state: 'culled', method: '_ov_teardown' }
  ];

export default Mixin.create({

  viewState: 'culled',

  contentVisible: false,
  contentHidden: false,
  contentInserted: false,
  contentCulled: false,

  heightProperty: 'minHeight',

  show() {
    this._setState('visible');
  },

  hide() {
    this._setState('hidden');
  },

  cull() {
    this._setState('culled');
  },

  _setState(toState) {
    var fromState = this.get('viewState');
    var currentState = fromState;

    if (fromState === toState) {
      return;
    }

    var transitionMap = STATE_LIST.indexOf(fromState) < STATE_LIST.indexOf(toState) ? STATE_TRANSITIONS_UP : STATE_TRANSITIONS_DOWN;
    var i;

    for (i = 0; i < transitionMap.length; i++) {
      if (transitionMap[i].state === fromState) {
        break;
      }
    }
    i++;

    while (transitionMap[i] && currentState !== toState) {
      if (transitionMap[i].method) {
        this[transitionMap[i].method]();
        currentState = transitionMap[i].state;
      }
      i++;
    }

    this.set('viewState', toState);
  },

  /**!
   * Destroy the View/Element
   *
   * Unlike the other methods, this method
   * can be called from any state. It is still not valid
   * to transition to it directly, but willDestroy uses it
   * to teardown the instance.
   *
   * @private
   */
  _ov_teardown() {
    let heightProp = this.heightProperty;
    if (this.get('contentInserted') && this.element) {
      if (!this.get('alwaysUseDefaultHeight')) {
        this.get('_position._satellite').resize();
      }
      this.element.style[heightProp] = this.get('_position.rect.height') + 'px';
    }
    this.setProperties({ contentCulled: true, contentHidden: false, contentInserted: false });
  },

  /**!
   * Insert the Element into the DOM in a hidden state.
   *
   * @private
   */
  _ov_insert() {
    this.setProperties({ contentHidden: true, contentCulled: false, contentInserted: true });
  },

  /**!
   * Reveal the Element
   *
   * @private
   */
  _ov_reveal() {
    let heightProp = this.heightProperty;
    this.setProperties({ contentHidden: false, contentVisible: true, contentInserted: true });
    this.element.style[heightProp] = null;
    this.element.style.visibility = 'visible';
  },

  /**!
   * Hide the Element
   *
   * @private
   */
  _ov_obscure() {
    this.setProperties({ contentHidden: true, contentVisible: false, contentInserted: true });
    this.element.style.visibility = 'hidden';
  }

});
<|endoftext|>


<commit_before>/* eslint-disable */
var path = require('path');
var webpack = require('webpack');

module.exports = {

    entry: path.join(__dirname, 'index.js'),

    output: {
        path: __dirname,
        filename: 'bundle.js',
        publicPath: '/dist/',
    },

    resolve: {
        alias: {
            'react-perfect-scrollbar': path.join(__dirname, '..', 'src'),
        },
    },

    module: {
        loaders: [
            {
                test: /\.(jsx|js)$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015', 'stage-1', 'react'],
                },
            },
            {
                test: /\.(jpg|png|svg|ttf|eot)$/,
                loader: 'file-loader',
                query: {
                    name: 'img/[hash].[ext]',
                },
            },
            {
                test: /\.scss$/,
                loaders: [
                    'style-loader',
                    'css-loader',
                    'postcss-loader',
                    'sass-loader',
                ],
            },
        ],
    },

    devtool: 'source-map',
};
<commit_msg>upgrade example's webpack config (#22)<commit_after>/* eslint-disable */
var path = require('path');
var webpack = require('webpack');

module.exports = {

    entry: path.join(__dirname, 'index.js'),

    output: {
        path: __dirname,
        filename: 'bundle.js',
        publicPath: '/dist/',
    },

    resolve: {
        alias: {
            'react-perfect-scrollbar': path.join(__dirname, '..', 'src'),
        },
    },

    module: {
        rules: [
            {
                test: /\.(jsx|js)$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                options: {
                    presets: ['es2015', 'stage-1', 'react'],
                },
            },
            {
                test: /\.(jpg|png|svg|ttf|eot)$/,
                loader: 'file-loader',
                options: {
                    name: 'img/[hash].[ext]',
                },
            },
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'postcss-loader',
                    'sass-loader',
                ],
            },
        ],
    },

    devtool: 'source-map',
};
<|endoftext|>