<commit_before>/**
 * Views for applicaiton.  A single View to handle most of
 * the applicaiton logic as the model side is light.
 */
(function($, window, undefined) {

// Check our namespace
window.MinnPost = window.MinnPost || {};

var BikeApplication = window.MinnPost.BikeApplication = Backbone.View.extend({

  // Various options, easy to set and change.
  options: {
    'mapOptions': {
      'minZoom': 8,
      'maxZoom': 15
    },
    'mapID': 'map',
    'mapDefaultCenter': new L.LatLng(44.96552667056723, -93.25298309326172),
    'mapDefaultZoom': 12,
    'dataDir': 'data',
    'loadingSelector': '.loading',
    'loadingMessageSelector': '.loading .loading-message',
    'controlsContainerSelector': '.leaflet-top.leaflet-left',
    'flotOptions': {
      xaxis: { mode: 'time', timeformat: '%h:%M %p' },
      yaxis: { ticks: 2 },
      crosshair: { mode: 'x', lineWidth: 3, color: '#1B8ADA' },
      legend: { backgroundColor: 'inherit', border: null },
      grid: { borderWidth: 1, borderColor: '#BBBBBB' }
    },
    'flotAverageData': {
      label: 'Avg bike density',
      data: [],
      color: '#7F3E10',
      lines: { show: true, fill: true }
    },
    'flotSelector': '#density-average-graph',
    'flotContainerSelector': '.leaflet-bottom.leaflet-left',
    'timeContainerSelector': '.leaflet-bottom.leaflet-right',
    'timeAnimDaySecs': 24 * 60 * 60,
    'timeAnimLengthSecs': 5 * 60,
    'timeAnimInterval': 100,
    'timeAnimDate': {
      'y': 2011,
      'm': 4, // Month is zero based
      'd': 18
    },
    'timeAnimTimeOffsetSecs': ((4 * 60) + 30) * 60 // 4:30
  },

  // Default objects for holding data.
  bikeAnimations: {},
  rentals: {},
  routes: {},
  densityAverage: {},
  timelineFlot: {},
  bikeAnimations: new MinnPost.BikeAnimations(),

  // Events
  events: {
    'click .control-play': 'play',
    'click .control-pause': 'pause'
  },

  // Initialize function when View is first initialized.
  initialize: function() {
    // Mark a loading; create map; load data;
    this.isLoading()
      .createMap()
      .loadDataRentals()
      .loadDataRoutes()
      .loadDataDensityAverage();
  },

  // Render function.
  render: function() {

  },

  // Called when loading all data.  We are not actually sure
  // what is done yet, so check and then fire if all is loaded.
  dataLoaded: function() {
    if (!_.isEmpty(this.rentals) && !_.isEmpty(this.routes) && !_.isEmpty(this.densityAverage)) {
      this.drawGraph()
        .makeBikeAnimations()
        .makeAnimator()
        .addControls()
        .doneLoading();
    }

    return this;
  },

  // Play main animation
  play: function(e) {
    e.preventDefault();

    // Change button to pause
    $('.control-play-pause').addClass('control-pause').removeClass('control-play').attr('title', 'Pause');

    // Ensure that the animation is loaded
    if (this.animation !== undefined && !_.isEmpty(this.animation)) {
      this.animation.seekTo(1);
      // Start all already started animations
      this.bikeAnimations.each(function(anim) {
        if (anim.animation.state > 0 && anim.animation.state < 1) {
          anim.animation.seekTo(1);
        }
      });
    }

    return this;
  },

  // Pause animation
  pause: function(e) {
    e.preventDefault();

    // Change button to play
    $('.control-play-pause').removeClass('control-pause').addClass('control-play').attr('title', 'Play');

    // Ensure that the animation is loaded
    if (this.animation !== undefined && !_.isEmpty(this.animation)) {
      this.animation.stop();
      // Pause all animations.  Do all as some are missed in the mixed
      this.bikeAnimations.each(function(anim) {
        anim.animation.stop();
      });
    }

    return this;
  },

  // Pause animation
  resetAnimations: function() {
    // Change button to play
    $('.control-play-pause').removeClass('control-pause').addClass('control-play').attr('title', 'Play');

    // Stop and change all states to 0.
    if (this.animation !== undefined && !_.isEmpty(this.animation)) {
      this.animation.stop();
      this.animation.state = 0;
      this.bikeAnimations.each(function(anim) {
        anim.resetAnimation();
      });
    }

    return this;
  },

  // Controls
  addControls: function() {
    // Play pause buttons
    var template = _.template($("#template-controls").html(), { });
    $(this.options.controlsContainerSelector).append(template);

    // Time
    template = _.template($("#template-time").html(), { });
    $(this.options.timeContainerSelector).append(template);

    return this;
  },

  // Mark as loading
  isLoading: function() {
    $(this.options.loadingSelector).addClass('is-loading');
    $(this.options.loadingMessageSelector).html('Loading data and animations...');

    return this;
  },

  // Done loading
  doneLoading: function() {
    $(this.options.loadingMessageSelector).html('Loaded.');
    $(this.options.loadingSelector).fadeOut('slow').removeClass('is-loading');

    return this;
  },

  // Create map.
  createMap: function() {
    this.map = new L.Map(this.options.mapID, this.options.mapOptions);
    this.addMapLayers();
    this.map.setView(this.options.mapDefaultCenter, this.options.mapDefaultZoom);
    this.map.addControl(new L.Control.Fullscreen());

    // Move attributioin to footnotes
    this.map.attributionControl.setPrefix('');
    $('.footnote').html($('.footnote').html() + ' ' + this.map.attributionControl._container.innerHTML);
    this.map.removeControl(this.map.attributionControl);

    return this;
  },

  // Add layers to map.
  addMapLayers: function() {
    var thisView = this;

    // Minnnpost base map
    var minnpost = new L.TileLayer('http://{s}.tiles.minnpost.com/minnpost-basemaps/minnpost-minnesota-greyscale-no-labels/{z}/{x}/{y}.png',
     { attribution: 'Map imagery from MinnPost; Map data from OpenStreetMap.',
      scheme: 'tms' });
    this.map.addLayer(minnpost);

    // Labels
    var minnpostLabels = new L.TileLayer('http://{s}.tiles.minnpost.com/minnpost-basemaps/minnpost-minnesota-greyscale-labels/{z}/{x}/{y}.png',
     { scheme: 'tms' });

    // Add route layer with TileJSON, then add labels on top
    wax.tilejson('http://a.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/tilejson.jsonp', function(tilejson) {
      // Hackaround
      tilejson.tiles = [
        'http://a.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
        'http://b.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
        'http://c.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
        'http://d.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
      ];
      tilejson.grids = [
        'http://a.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
        'http://b.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
        'http://c.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
        'http://d.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
      ];
      tilejson.scheme = 'tms';
      tilejson.attribution = 'Data provided by Nice Ride MN; ';

      thisView.map.addLayer(new wax.leaf.connector(tilejson));
      thisView.map.addLayer(minnpostLabels);
      wax.leaf.interaction()
        .map(thisView.map)
        .tilejson(tilejson)
        .on(wax.tooltip().parent(thisView.map._container).events());
    });

    return this;
  },

  // Create flot graph
  drawGraph: function() {
    // We want to insert this in the leaflet map container
    var template = _.template($("#template-flot-timeline").html(), { });
    $(this.options.flotContainerSelector).append(template);

    this.options.flotAverageData.data = this.densityAverage;
    this.timelineFlot = $.plot($(this.options.flotSelector), [this.options.flotAverageData], this.options.flotOptions);
    this.timelineFlot.setCrosshair();
    this.timelineFlot.lockCrosshair();

    return this;
  },

  // Make bike animations
  makeBikeAnimations: function() {
    var i;
    var ba;
    var route;
    var ratio = (this.options.timeAnimLengthSecs / this.options.timeAnimDaySecs);

    // Go through rentals, match route, make a bike animation then add to collection
    for (i in this.rentals) {
      // Check for the route
      if (this.routes[this.rentals[i].st + '-' + this.rentals[i].et] !== undefined) {
        route = this.routes[this.rentals[i].st + '-' + this.rentals[i].et];

        // Add some data
        this.rentals[i].timeRatio = ratio;
        this.rentals[i].animInterval = this.options.timeAnimInterval;
        this.rentals[i].map = this.map;

        // Create model and add to collection.  Model initializes stuff.
        var ba = new MinnPost.BikeAnimation(this.rentals[i], route);
        this.bikeAnimations.add(ba, { silent: true });
      }
    }

    return this;
  },

  // Makes fulls animator
  makeAnimator: function() {
    var thisView = this;

    this.animation = new Animator({
      duration: this.options.timeAnimLengthSecs * 1000,
      interval: this.options.timeAnimInterval,
      transition: Animator.tx.linear,
      onComplete: function() {
        // As stop will call this as well, check for state
        if (this.state == 1) {
          thisView.resetAnimations();
        }
      }
    });

    this.animation.addSubject(function(interval) {
      // Determine the time of day it is in the animation
      var totalSecElapsed = interval * thisView.options.timeAnimDaySecs + thisView.options.timeAnimTimeOffsetSecs;
      var aD = thisView.options.timeAnimDate;
      var timeObj = timeExtender.secondsToTime(totalSecElapsed);
      var currentTime = new XDate(aD.y, aD.m, aD.d, timeObj.h, timeObj.m, timeObj.s, 0, true);

      // Show time
      thisView.showTime(currentTime);

      // Find any animations that have not been played yet
      // that start before now and end after now.  Animation
      // state is between 0 and 1.
      thisView.bikeAnimations.each(function(anim) {
        if (anim.get('s') <= currentTime && anim.get('e') > currentTime && anim.animation.state === 0) {
          anim.animation.play();
        }
      });

      // Update graph.  TODO: Pull in bike density dat and overlay it
      // as it goes.
      //var graph_data = da_flot.getData();
      //graph_data[1].data.push([])
      thisView.timelineFlot.setCrosshair({ x: currentTime.getTime() });
    });

    return this;
  },

  showTime: function(date) {
    $('.time-day').html(date.toUTCString('h:ss tt'));

    return this;
  },

  // Load rental data.
  loadDataRentals: function() {
    var thisView = this;
    window.callback_rentals = function(rentals) {
      thisView.rentals = rentals;
      thisView.dataLoaded();
    }
    $.ajax({
      type: 'GET',
      dataType: 'jsonp',
      url: thisView.options.dataDir + '/rentals.jsonp?callback=callback_rentals',
      jsonp: 'callback'
    });

    return this;
  },

  // Load routes data.
  loadDataRoutes: function() {
    var thisView = this;
    window.callback_routes = function(routes) {
      thisView.routes = routes;
      thisView.dataLoaded();
    }
    $.ajax({
      type: 'GET',
      dataType: 'jsonp',
      url: thisView.options.dataDir + '/routes.jsonp?callback=callback_routes',
      jsonp: 'callback'
    });

    return this;
  },

  // Load density average data.
  loadDataDensityAverage: function() {
    var thisView = this;
    window.callback_density_average = function(avgs) {
      thisView.densityAverage = avgs;
      thisView.dataLoaded();
    }
    $.ajax({
      type: 'GET',
      dataType: 'jsonp',
      url: thisView.options.dataDir + '/density_average.jsonp?callback=callback_density_average',
      jsonp: 'callback'
    });

    return this;
  }

});

})(jQuery, window);<commit_msg>Simple check to use localhost data if on localhost.<commit_after>/**
 * Views for applicaiton.  A single View to handle most of
 * the applicaiton logic as the model side is light.
 */
(function($, window, undefined) {

// Check our namespace
window.MinnPost = window.MinnPost || {};

var BikeApplication = window.MinnPost.BikeApplication = Backbone.View.extend({

  // Various options, easy to set and change.
  options: {
    'mapOptions': {
      'minZoom': 8,
      'maxZoom': 15
    },
    'mapID': 'map',
    'mapDefaultCenter': new L.LatLng(44.96552667056723, -93.25298309326172),
    'mapDefaultZoom': 12,
    'dataDir': 'data',
    'dateDirRemote': 'https://s3.amazonaws.com/data.minnpost/projects/minnpost-nice-ride/data',
    'loadingSelector': '.loading',
    'loadingMessageSelector': '.loading .loading-message',
    'controlsContainerSelector': '.leaflet-top.leaflet-left',
    'flotOptions': {
      xaxis: { mode: 'time', timeformat: '%h:%M %p' },
      yaxis: { ticks: 2 },
      crosshair: { mode: 'x', lineWidth: 3, color: '#1B8ADA' },
      legend: { backgroundColor: 'inherit', border: null },
      grid: { borderWidth: 1, borderColor: '#BBBBBB' }
    },
    'flotAverageData': {
      label: 'Avg bike density',
      data: [],
      color: '#7F3E10',
      lines: { show: true, fill: true }
    },
    'flotSelector': '#density-average-graph',
    'flotContainerSelector': '.leaflet-bottom.leaflet-left',
    'timeContainerSelector': '.leaflet-bottom.leaflet-right',
    'timeAnimDaySecs': 24 * 60 * 60,
    'timeAnimLengthSecs': 5 * 60,
    'timeAnimInterval': 100,
    'timeAnimDate': {
      'y': 2011,
      'm': 4, // Month is zero based
      'd': 18
    },
    'timeAnimTimeOffsetSecs': ((4 * 60) + 30) * 60 // 4:30
  },

  // Default objects for holding data.
  bikeAnimations: {},
  rentals: {},
  routes: {},
  densityAverage: {},
  timelineFlot: {},
  bikeAnimations: new MinnPost.BikeAnimations(),

  // Events
  events: {
    'click .control-play': 'play',
    'click .control-pause': 'pause'
  },

  // Initialize function when View is first initialized.
  initialize: function() {
    // Mark a loading; create map; load data;
    this.isLoading()
      .checkHost()
      .createMap()
      .loadDataRentals()
      .loadDataRoutes()
      .loadDataDensityAverage();
  },

  // Render function.
  render: function() {

  },

  // Called when loading all data.  We are not actually sure
  // what is done yet, so check and then fire if all is loaded.
  dataLoaded: function() {
    if (!_.isEmpty(this.rentals) && !_.isEmpty(this.routes) && !_.isEmpty(this.densityAverage)) {
      this.drawGraph()
        .makeBikeAnimations()
        .makeAnimator()
        .addControls()
        .doneLoading();
    }

    return this;
  },

  // Play main animation
  play: function(e) {
    e.preventDefault();

    // Change button to pause
    $('.control-play-pause').addClass('control-pause').removeClass('control-play').attr('title', 'Pause');

    // Ensure that the animation is loaded
    if (this.animation !== undefined && !_.isEmpty(this.animation)) {
      this.animation.seekTo(1);
      // Start all already started animations
      this.bikeAnimations.each(function(anim) {
        if (anim.animation.state > 0 && anim.animation.state < 1) {
          anim.animation.seekTo(1);
        }
      });
    }

    return this;
  },

  // Pause animation
  pause: function(e) {
    e.preventDefault();

    // Change button to play
    $('.control-play-pause').removeClass('control-pause').addClass('control-play').attr('title', 'Play');

    // Ensure that the animation is loaded
    if (this.animation !== undefined && !_.isEmpty(this.animation)) {
      this.animation.stop();
      // Pause all animations.  Do all as some are missed in the mixed
      this.bikeAnimations.each(function(anim) {
        anim.animation.stop();
      });
    }

    return this;
  },

  // Pause animation
  resetAnimations: function() {
    // Change button to play
    $('.control-play-pause').removeClass('control-pause').addClass('control-play').attr('title', 'Play');

    // Stop and change all states to 0.
    if (this.animation !== undefined && !_.isEmpty(this.animation)) {
      this.animation.stop();
      this.animation.state = 0;
      this.bikeAnimations.each(function(anim) {
        anim.resetAnimation();
      });
    }

    return this;
  },

  // Controls
  addControls: function() {
    // Play pause buttons
    var template = _.template($("#template-controls").html(), { });
    $(this.options.controlsContainerSelector).append(template);

    // Time
    template = _.template($("#template-time").html(), { });
    $(this.options.timeContainerSelector).append(template);

    return this;
  },

  // Mark as loading
  isLoading: function() {
    $(this.options.loadingSelector).addClass('is-loading');
    $(this.options.loadingMessageSelector).html('Loading data and animations...');

    return this;
  },

  // Done loading
  doneLoading: function() {
    $(this.options.loadingMessageSelector).html('Loaded.');
    $(this.options.loadingSelector).fadeOut('slow').removeClass('is-loading');

    return this;
  },

  // Create map.
  createMap: function() {
    this.map = new L.Map(this.options.mapID, this.options.mapOptions);
    this.addMapLayers();
    this.map.setView(this.options.mapDefaultCenter, this.options.mapDefaultZoom);
    this.map.addControl(new L.Control.Fullscreen());

    // Move attributioin to footnotes
    this.map.attributionControl.setPrefix('');
    $('.footnote').html($('.footnote').html() + ' ' + this.map.attributionControl._container.innerHTML);
    this.map.removeControl(this.map.attributionControl);

    return this;
  },

  // Add layers to map.
  addMapLayers: function() {
    var thisView = this;

    // Minnnpost base map
    var minnpost = new L.TileLayer('http://{s}.tiles.minnpost.com/minnpost-basemaps/minnpost-minnesota-greyscale-no-labels/{z}/{x}/{y}.png',
     { attribution: 'Map imagery from MinnPost; Map data from OpenStreetMap.',
      scheme: 'tms' });
    this.map.addLayer(minnpost);

    // Labels
    var minnpostLabels = new L.TileLayer('http://{s}.tiles.minnpost.com/minnpost-basemaps/minnpost-minnesota-greyscale-labels/{z}/{x}/{y}.png',
     { scheme: 'tms' });

    // Add route layer with TileJSON, then add labels on top
    wax.tilejson('http://a.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/tilejson.jsonp', function(tilejson) {
      // Hackaround
      tilejson.tiles = [
        'http://a.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
        'http://b.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
        'http://c.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
        'http://d.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.png',
      ];
      tilejson.grids = [
        'http://a.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
        'http://b.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
        'http://c.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
        'http://d.tiles.minnpost.com/minnpost-nice-ride/minnpost-nice-ride-routes/{z}/{x}/{y}.grid.json',
      ];
      tilejson.scheme = 'tms';
      tilejson.attribution = 'Data provided by Nice Ride MN; ';

      thisView.map.addLayer(new wax.leaf.connector(tilejson));
      thisView.map.addLayer(minnpostLabels);
      wax.leaf.interaction()
        .map(thisView.map)
        .tilejson(tilejson)
        .on(wax.tooltip().parent(thisView.map._container).events());
    });

    return this;
  },

  // Create flot graph
  drawGraph: function() {
    // We want to insert this in the leaflet map container
    var template = _.template($("#template-flot-timeline").html(), { });
    $(this.options.flotContainerSelector).append(template);

    this.options.flotAverageData.data = this.densityAverage;
    this.timelineFlot = $.plot($(this.options.flotSelector), [this.options.flotAverageData], this.options.flotOptions);
    this.timelineFlot.setCrosshair();
    this.timelineFlot.lockCrosshair();

    return this;
  },

  // Make bike animations
  makeBikeAnimations: function() {
    var i;
    var ba;
    var route;
    var ratio = (this.options.timeAnimLengthSecs / this.options.timeAnimDaySecs);

    // Go through rentals, match route, make a bike animation then add to collection
    for (i in this.rentals) {
      // Check for the route
      if (this.routes[this.rentals[i].st + '-' + this.rentals[i].et] !== undefined) {
        route = this.routes[this.rentals[i].st + '-' + this.rentals[i].et];

        // Add some data
        this.rentals[i].timeRatio = ratio;
        this.rentals[i].animInterval = this.options.timeAnimInterval;
        this.rentals[i].map = this.map;

        // Create model and add to collection.  Model initializes stuff.
        var ba = new MinnPost.BikeAnimation(this.rentals[i], route);
        this.bikeAnimations.add(ba, { silent: true });
      }
    }

    return this;
  },

  // Makes fulls animator
  makeAnimator: function() {
    var thisView = this;

    this.animation = new Animator({
      duration: this.options.timeAnimLengthSecs * 1000,
      interval: this.options.timeAnimInterval,
      transition: Animator.tx.linear,
      onComplete: function() {
        // As stop will call this as well, check for state
        if (this.state == 1) {
          thisView.resetAnimations();
        }
      }
    });

    this.animation.addSubject(function(interval) {
      // Determine the time of day it is in the animation
      var totalSecElapsed = interval * thisView.options.timeAnimDaySecs + thisView.options.timeAnimTimeOffsetSecs;
      var aD = thisView.options.timeAnimDate;
      var timeObj = timeExtender.secondsToTime(totalSecElapsed);
      var currentTime = new XDate(aD.y, aD.m, aD.d, timeObj.h, timeObj.m, timeObj.s, 0, true);

      // Show time
      thisView.showTime(currentTime);

      // Find any animations that have not been played yet
      // that start before now and end after now.  Animation
      // state is between 0 and 1.
      thisView.bikeAnimations.each(function(anim) {
        if (anim.get('s') <= currentTime && anim.get('e') > currentTime && anim.animation.state === 0) {
          anim.animation.play();
        }
      });

      // Update graph.  TODO: Pull in bike density dat and overlay it
      // as it goes.
      //var graph_data = da_flot.getData();
      //graph_data[1].data.push([])
      thisView.timelineFlot.setCrosshair({ x: currentTime.getTime() });
    });

    return this;
  },

  showTime: function(date) {
    $('.time-day').html(date.toUTCString('h:ss tt'));

    return this;
  },

  // Check the host
  checkHost: function() {
    if (location.host.indexOf('localhost') == -1) {
      this.dataDir = this.dateDirRemote;
    }

    return this;
  },

  // Load rental data.
  loadDataRentals: function() {
    var thisView = this;
    window.callback_rentals = function(rentals) {
      thisView.rentals = rentals;
      thisView.dataLoaded();
    }
    $.ajax({
      type: 'GET',
      dataType: 'jsonp',
      url: thisView.options.dataDir + '/rentals.jsonp?callback=callback_rentals',
      jsonp: 'callback'
    });

    return this;
  },

  // Load routes data.
  loadDataRoutes: function() {
    var thisView = this;
    window.callback_routes = function(routes) {
      thisView.routes = routes;
      thisView.dataLoaded();
    }
    $.ajax({
      type: 'GET',
      dataType: 'jsonp',
      url: thisView.options.dataDir + '/routes.jsonp?callback=callback_routes',
      jsonp: 'callback'
    });

    return this;
  },

  // Load density average data.
  loadDataDensityAverage: function() {
    var thisView = this;
    window.callback_density_average = function(avgs) {
      thisView.densityAverage = avgs;
      thisView.dataLoaded();
    }
    $.ajax({
      type: 'GET',
      dataType: 'jsonp',
      url: thisView.options.dataDir + '/density_average.jsonp?callback=callback_density_average',
      jsonp: 'callback'
    });

    return this;
  }

});

})(jQuery, window);<|endoftext|>


<commit_before>const remote = Electron.remote;
const path = remote.require('path')

import { fileTreeController as controller } from '../filetree'
import FileTreeActions from '../filetree/actions'
import * as URIUtils from '../utils/URIUtils'
import { tabActions } from '../actions'

import {
  showInFinder,
  registerPath,
  removeRegisteredPath,
  markSaved,
  markUnsaved,
} from './fileActions'
import {
  openFile,
  closeTabWindow,
} from './compositeFileActions'
import {
  docIdChange,
} from './editorActions'
import {
  historyIdChange,
} from './historyActions'
import {
  confirmDelete
} from './uiActions'

import { CONTENT_PANES } from '../constants/LayoutConstants'

function buildMetadataFilePath(filePath, rootPath) {
    return path.join(rootPath, '.deco', 'metadata', filePath.replace(rootPath, '') + '.deco')
}

export const renameFile = (oldPath, newPath) => {
  return (dispatch, getState) => {
    const unsaved = getState().directory.unsaved
    const metadataFiles = getState().metadata.liveValues.liveValuesForFile
    const rootPath = getState().directory.rootPath
    controller.run('rename', oldPath, newPath).then(() => {
      dispatch(registerPath(newPath))
      if (unsaved[oldPath]) {
        markSaved(oldPath)
        markUnsaved(newPath)
      }
      if (metadataFiles[oldPath]) {
        const oldMetadataPath = buildMetadataFilePath(oldPath, rootPath)
        const newMetadataPath = buildMetadataFilePath(newPath, rootPath)
        controller.run('rename', oldMetadataPath, newMetadataPath)
      }
      dispatch(docIdChange(oldPath, newPath))
      dispatch(historyIdChange(oldPath, newPath))

      const oldURI = URIUtils.filePathToURI(oldPath)
      const newURI = URIUtils.filePathToURI(newPath)
      dispatch(tabActions.swapAllTabsForResource(CONTENT_PANES.CENTER, oldURI, newURI))

      dispatch(removeRegisteredPath(oldPath))
    })
  }
}

export const renameDir = (oldPath, newPath) => {
  return (dispatch, getState) => {
    const {filesById, unsaved, rootPath} = getState()

    const oldMetadataPath = buildMetadataFilePath(oldPath, rootPath)
    const newMetadataPath = buildMetadataFilePath(newPath, rootPath)

    controller.run('rename', oldMetadataPath, newMetadataPath)
    controller.run('rename', oldPath, newPath).then(() => {
      _.each(filesById, (file, id) => {
        if (id.indexOf(oldPath) == 0) {
          const newSubPath = id.replace(oldPath, newPath)
          if (unsaved[id]) {
            markSaved(id)
            markUnsaved(oldPath)
          }
          dispatch(registerPath(newSubPath))
          dispatch(docIdChange(id, newSubPath))
          dispatch(historyIdChange(id, newSubPath))

          const oldURI = URIUtils.filePathToURI(id)
          const newURI = URIUtils.filePathToURI(newSubPath)
          dispatch(tabActions.swapAllTabsForResource(CONTENT_PANES.CENTER, oldURI, newURI))

          dispatch(removeRegisteredPath(id))
        }
      })
    })
  }
}

export const createFile = (filePath, content = '') => async (dispatch) => {
  await controller.run('writeFile', filePath, content)

  const parentPath = path.dirname(filePath)

  FileTreeActions.clearSelections()
  FileTreeActions.expandNode(parentPath)

  dispatch(registerPath(filePath))
  dispatch(openFile(filePath))
}

export const createDir = (dirPath) => {
  return (dispatch, getState) => {
    controller.run('mkdir', dirPath)
  }
}

export const deleteFile = (filePath) => {
  return (dispatch, getState) => {
    dispatch(confirmDelete(filePath)).then((resp) => {
      const { shouldDelete } = resp
      if (!shouldDelete) {
        return
      }
      const unsaved = getState().directory.unsaved

      const uri = URIUtils.filePathToURI(filePath)
      dispatch(tabActions.closeAllTabsForResource(CONTENT_PANES.CENTER, uri))

      if (unsaved[filePath]) {
        dispatch(markSaved(filePath))
      }

      dispatch(removeRegisteredPath(filePath))
      controller.run('remove', filePath)

      // handle metadata
      const rootPath = getState().directory.rootPath
      const metadataPath = buildMetadataFilePath(filePath, rootPath)
      controller.run('remove', metadataPath)
    })
  }
}

export const deleteDir = (dirPath) => {
  return (dispatch, getState) => {
    const {filesById, unsaved, rootPath} = getState().directory

    dispatch(confirmDelete(dirPath)).then((resp) => {
      const {shouldDelete} = resp
      if (!shouldDelete) {
        return
      }
      controller.run('remove', dirPath)
      _.each(filesById, (file, id) => {
        if (id.indexOf(dirPath) == 0) {
          const uri = URIUtils.filePathToURI(id)
          dispatch(tabActions.closeAllTabsForResource(CONTENT_PANES.CENTER, uri))

          if (unsaved[id]) {
            dispatch(markSaved(id))
          }

          dispatch(removeRegisteredPath(id))
        }
      })

      // handle metadata
      const metadataPath = buildMetadataFilePath(dirPath, rootPath)
      controller.run('remove', metadataPath)
    })
  }
}
<commit_msg>Fix rename dir<commit_after>const remote = Electron.remote;
const path = remote.require('path')

import { fileTreeController as controller } from '../filetree'
import FileTreeActions from '../filetree/actions'
import * as URIUtils from '../utils/URIUtils'
import { tabActions } from '../actions'

import {
  showInFinder,
  registerPath,
  removeRegisteredPath,
  markSaved,
  markUnsaved,
} from './fileActions'
import {
  openFile,
  closeTabWindow,
} from './compositeFileActions'
import {
  docIdChange,
} from './editorActions'
import {
  historyIdChange,
} from './historyActions'
import {
  confirmDelete
} from './uiActions'

import { CONTENT_PANES } from '../constants/LayoutConstants'

function buildMetadataFilePath(filePath, rootPath) {
    return path.join(rootPath, '.deco', 'metadata', filePath.replace(rootPath, '') + '.deco')
}

export const renameFile = (oldPath, newPath) => {
  return (dispatch, getState) => {
    const unsaved = getState().directory.unsaved
    const metadataFiles = getState().metadata.liveValues.liveValuesForFile
    const rootPath = getState().directory.rootPath
    controller.run('rename', oldPath, newPath).then(() => {
      dispatch(registerPath(newPath))
      if (unsaved[oldPath]) {
        markSaved(oldPath)
        markUnsaved(newPath)
      }
      if (metadataFiles[oldPath]) {
        const oldMetadataPath = buildMetadataFilePath(oldPath, rootPath)
        const newMetadataPath = buildMetadataFilePath(newPath, rootPath)
        controller.run('rename', oldMetadataPath, newMetadataPath)
      }
      dispatch(docIdChange(oldPath, newPath))
      dispatch(historyIdChange(oldPath, newPath))

      const oldURI = URIUtils.filePathToURI(oldPath)
      const newURI = URIUtils.filePathToURI(newPath)
      dispatch(tabActions.swapAllTabsForResource(CONTENT_PANES.CENTER, oldURI, newURI))

      dispatch(removeRegisteredPath(oldPath))
    })
  }
}

export const renameDir = (oldPath, newPath) => {
  return (dispatch, getState) => {
    const {filesById, unsaved, directory: {rootPath}} = getState()

    const oldMetadataPath = buildMetadataFilePath(oldPath, rootPath)
    const newMetadataPath = buildMetadataFilePath(newPath, rootPath)

    controller.run('rename', oldMetadataPath, newMetadataPath)
    controller.run('rename', oldPath, newPath).then(() => {
      _.each(filesById, (file, id) => {
        if (id.indexOf(oldPath) == 0) {
          const newSubPath = id.replace(oldPath, newPath)
          if (unsaved[id]) {
            markSaved(id)
            markUnsaved(oldPath)
          }
          dispatch(registerPath(newSubPath))
          dispatch(docIdChange(id, newSubPath))
          dispatch(historyIdChange(id, newSubPath))

          const oldURI = URIUtils.filePathToURI(id)
          const newURI = URIUtils.filePathToURI(newSubPath)
          dispatch(tabActions.swapAllTabsForResource(CONTENT_PANES.CENTER, oldURI, newURI))

          dispatch(removeRegisteredPath(id))
        }
      })
    })
  }
}

export const createFile = (filePath, content = '') => async (dispatch) => {
  await controller.run('writeFile', filePath, content)

  const parentPath = path.dirname(filePath)

  FileTreeActions.clearSelections()
  FileTreeActions.expandNode(parentPath)

  dispatch(registerPath(filePath))
  dispatch(openFile(filePath))
}

export const createDir = (dirPath) => {
  return (dispatch, getState) => {
    controller.run('mkdir', dirPath)
  }
}

export const deleteFile = (filePath) => {
  return (dispatch, getState) => {
    dispatch(confirmDelete(filePath)).then((resp) => {
      const { shouldDelete } = resp
      if (!shouldDelete) {
        return
      }
      const unsaved = getState().directory.unsaved

      const uri = URIUtils.filePathToURI(filePath)
      dispatch(tabActions.closeAllTabsForResource(CONTENT_PANES.CENTER, uri))

      if (unsaved[filePath]) {
        dispatch(markSaved(filePath))
      }

      dispatch(removeRegisteredPath(filePath))
      controller.run('remove', filePath)

      // handle metadata
      const rootPath = getState().directory.rootPath
      const metadataPath = buildMetadataFilePath(filePath, rootPath)
      controller.run('remove', metadataPath)
    })
  }
}

export const deleteDir = (dirPath) => {
  return (dispatch, getState) => {
    const {filesById, unsaved, rootPath} = getState().directory

    dispatch(confirmDelete(dirPath)).then((resp) => {
      const {shouldDelete} = resp
      if (!shouldDelete) {
        return
      }
      controller.run('remove', dirPath)
      _.each(filesById, (file, id) => {
        if (id.indexOf(dirPath) == 0) {
          const uri = URIUtils.filePathToURI(id)
          dispatch(tabActions.closeAllTabsForResource(CONTENT_PANES.CENTER, uri))

          if (unsaved[id]) {
            dispatch(markSaved(id))
          }

          dispatch(removeRegisteredPath(id))
        }
      })

      // handle metadata
      const metadataPath = buildMetadataFilePath(dirPath, rootPath)
      controller.run('remove', metadataPath)
    })
  }
}
<|endoftext|>


<commit_before>// import React and Redux dependencies
var React = require('react');
var connect = require('react-redux').connect;
var _ = require('underscore');

var bindActionCreators = require('redux').bindActionCreators;
var Immutable = require('immutable');

var MeasureActions = require('../../actions/Measures');

function mapStatetoProps (state, ownProps) {
  return {
    scale: state.Measures.getIn([ownProps.measureId, 'scale']),
  };
}

function mapDispatchtoProps (dispatch) {
  return {
    actions: bindActionCreators(MeasureActions, dispatch)
  };
}

var MeasureQualitative = React.createClass({

  setScale: function () {
    var vals = [];
    var step = Number(this.refs.step.value);
    var max = Number(this.refs.max.value);
    var min = Number(this.refs.min.value);
    for (var i = min; i <= max; i += step) {
      vals.push(i);
    }
    this.props.actions.setScale(vals, this.props.measureId);
  },

  componentWillMount: function() {

  },

  render: function () {
    return (

        Experiment Name
        Min
        <input ref="min" type="text" />
        Max
        <input ref="max" type="text" />
        Step
        <input ref="step" type="text" />

        Current scale :
          {JSON.stringify(this.props.scale)}

        <button onClick={this.setScale}>save measure</button>

      );
  }
});

module.exports = connect(mapStatetoProps, mapDispatchtoProps)(MeasureQualitative);
<commit_msg>I was wrong<commit_after>// import React and Redux dependencies
var React = require('react');
var connect = require('react-redux').connect;
var _ = require('underscore');

var bindActionCreators = require('redux').bindActionCreators;
var Immutable = require('immutable');

var MeasureActions = require('../../actions/Measures');

function mapStatetoProps (state, ownProps) {
  return {
    scale: state.Measures.getIn([ownProps.measureId, 'scale']),
  };
}

function mapDispatchtoProps (dispatch) {
  return {
    actions: bindActionCreators(MeasureActions, dispatch)
  };
}

var MeasureQualitative = React.createClass({

  setScale: function () {
    var vals = [];
    var max = Number(this.refs.max.value);
    for (var i = 0; i <= max; i++) {
      vals.push(i);
    }
    this.props.actions.setScale(vals, this.props.measureId);
  },

  componentWillMount: function() {

  },

  render: function () {
    return (

        Experiment Name
        Max
        <input ref="max" type="text" />

        Current scale :
          {JSON.stringify(this.props.scale)}

        <button onClick={this.setScale}>save measure</button>

      );
  }
});

module.exports = connect(mapStatetoProps, mapDispatchtoProps)(MeasureQualitative);
<|endoftext|>


<commit_before>define([ "use!backbone",
         "use!handlebars",
         "text!activity/activity.handlebars",
         "user/UserEditView",
         "text!user/user_read_link.handlebars",
         "activity/Activity" ],
    function(Backbone, Handlebars, activityTemplate, User, UserEditView,
        userTemplate, Activity) {
      var ActivityView = Backbone.View.extend(
      /** @lends ActivityView.prototype */
      {
        /**
         * @class The layout of a single Activity. This view is used in the
         *        activity feeds.This view compiles the template that goes into
         *        the view of the collection which in turn goes into the
         *        activity feed.
         *
         * @extends Backbone.View
         * @constructs
         */
        initialize : function() {

        },

        model : Activity,
        userview : UserReadView,
        classname : "activity",
        template : Handlebars.compile(activityTemplate),
        render : function() {

          Handlebars.registerPartial("user", this.userview
              .template(this.userview.model.toJSON()));

          $(this.el).html(this.template(this.model.toJSON()));
          return this;
        },

      });

      return ActivityView;
    });<commit_msg>No code change. Reformatted.<commit_after>define([
    "use!backbone",
    "use!handlebars",
    "text!activity/activity.handlebars",
    "activity/Activity"
], function(
    Backbone,
    Handlebars,
    activityTemplate,
    Activity
) {
  var ActivityView = Backbone.View.extend(
  /** @lends ActivityView.prototype */
  {
    /**
     * @class The layout of a single Activity. This view is used in the
     *        activity feeds.This view compiles the template that goes into
     *        the view of the collection which in turn goes into the
     *        activity feed.
     *
     * @extends Backbone.View
     * @constructs
     */
    initialize : function() {
    },

    model : Activity,

    userview : UserReadView,

    classname : "activity",

    template : Handlebars.compile(activityTemplate),

    render : function() {
      Handlebars.registerPartial("user", this.userview
          .template(this.userview.model.toJSON()));

      $(this.el).html(this.template(this.model.toJSON()));

      return this;
    }
  });

  return ActivityView;
});<|endoftext|>


<commit_before>'use strict';
/* global urlContainsParams*/
describe('Service: solrSearchSvc', function () {

  // load the service's module
  beforeEach(module('splain-app'));

  // instantiate service
  var solrSearchSvc;
  var $httpBackend = null;
  var fieldSpecSvc = null;
  var mockSolrUrl = 'http://example.com:1234/solr/select';
  var mockSolrParams = {
    q: ['#$query##'],
    fq: ['field:value', 'field1:value', 'field2:#$query##']
  };
  var expectedParams = angular.copy(mockSolrParams);
  var mockQueryText = 'query text';
  var mockFieldSpec = null;
  expectedParams.q[0] =   encodeURIComponent(mockQueryText);
  expectedParams.fq[2] = 'field2:' + encodeURIComponent(mockQueryText);
  var mockResults = {
    response: {
      numFound: 2,
      docs : [
        {id: 'doc1'}, {id: 'doc2'}
      ]
    }
  };

  beforeEach(inject(function($injector) {
    $httpBackend = $injector.get('$httpBackend');
  }));

  beforeEach(inject(function (_solrSearchSvc_, _fieldSpecSvc_) {
    solrSearchSvc = _solrSearchSvc_;
    fieldSpecSvc = _fieldSpecSvc_;
    mockFieldSpec = fieldSpecSvc.createFieldSpec('field field1');
  }));

  it('access solr with mock solr params', function() {
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search();
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('tracks active queries', function() {
    expect(solrSearchSvc.activeQueries()).toEqual(0);
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search();
    expect(solrSearchSvc.activeQueries()).toEqual(1);
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
    expect(solrSearchSvc.activeQueries()).toEqual(0);
  });

  describe('explain info ', function() {

    var fullSolrResp = {'responseHeader':{
      'status':0,
      'QTime':1,
      'params':{
          'df':'content',
          'echoParams':'all',
          'rows':'2',
          'debugQuery':'true',
          'fl':'path content',
          'indent':['true',
            'true'],
          'q':'*:*',
          'wt':'json',
        }
      },
      'response':{'numFound':100,'start':0,'docs':[
          {
            'content':'stuff',
            'path':'http://larkin.com/index/'
          },
          {
            'content':'more stuff',
            'path':'http://www.rogahnbins.com/main.html'
          }
        ]
      },
      'debug':{
          'rawquerystring':'*:*',
          'querystring':'*:*',
          'parsedquery':'MatchAllDocsQuery(*:*)',
          'parsedquery_toString':'*:*',
          'explain':{
            'http://larkin.com/index/':'\n1.0 = (MATCH) MatchAllDocsQuery, product of:\n  1.0 = queryNorm\n',
            'http://www.rogahnbins.com/main.html':'\n1.0 = (MATCH) MatchAllDucksQuery, product of:\n  1.0 = queryNorm\n'
          },
          'QParser':'LuceneQParser',
          'timing':{
            'time':1.0,
            'prepare':{
              'time':1.0,
              'query':{
                'time':0.0
              },
              'facet':{
                'time':0.0
              },
              'mlt':{
                'time':0.0
              },
              'highlight':{
                'time':0.0
              },
              'stats':{
                'time':0.0
              },
              'debug':{
                'time':0.0
              }
            },
            'process':{
              'time':0.0,
              'query':{
                'time':0.0
              },
              'facet':{
                'time':0.0
              },
              'mlt':{
                'time':0.0
              },
              'highlight':{
                'time':0.0
              },
              'stats':{
                'time':0.0
              },
              'debug':{
                'time':0.0
              }
            }
          }
        }
    };
    var fieldSpec = null;
    var searcher = null;

    beforeEach(function() {
      fieldSpec = fieldSpecSvc.createFieldSpec('id:path content');
      searcher = solrSearchSvc.createSearcher(fieldSpec.fieldList(), mockSolrUrl,
                                                  mockSolrParams, mockQueryText);
    });

    it('populates explain()', function() {
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                              .respond(200, fullSolrResp);
      searcher.search().then(function() {
        var solrDocs = searcher.docs;
        expect(solrDocs[0].explain('http://larkin.com/index/')).toEqual('\n1.0 = (MATCH) MatchAllDocsQuery, product of:\n  1.0 = queryNorm\n');
        expect(solrDocs[1].explain('http://www.rogahnbins.com/main.html')).toEqual('\n1.0 = (MATCH) MatchAllDucksQuery, product of:\n  1.0 = queryNorm\n');
      });
      $httpBackend.flush();
      $httpBackend.verifyNoOutstandingExpectation();
    });
  });

  // For tests where "id" is not the id field

  describe('alt id field tests', function() {
    var mockResultsAltId = {
      response: {
        numFound: 2,
        docs : [
          {altId: 'alt_doc1'}, {altId: 'alt_doc2'}
        ]
      }
    };

    var fieldSpec = null;
    var searcher = null;
    beforeEach(function() {
      fieldSpec = fieldSpecSvc.createFieldSpec('id:altId');
      searcher = solrSearchSvc.createSearcher(fieldSpec.fieldList(), mockSolrUrl,
                                                  mockSolrParams, mockQueryText);
    });

    it('works with an alternate id field', function() {
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                              .respond(200, mockResultsAltId);
      searcher.search().then(function() {
        var numDocs = searcher.numFound;
        var solrDocs = searcher.docs;
        expect(solrDocs.length).toEqual(2);
        expect(numDocs).toEqual(2);
        // sanity check on these docs
        angular.forEach(solrDocs, function(queryDoc) {
          expect(['alt_doc1', 'alt_doc2']).toContain(queryDoc.altId);
        });
      });
      $httpBackend.flush();
      $httpBackend.verifyNoOutstandingExpectation();
    });

    it('creates docs that can construct tokens URL', function() {
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                              .respond(200, mockResultsAltId);
      searcher.search().then(function() {
        var numDocs = searcher.numFound;
        var solrDocs = searcher.docs;
        expect(solrDocs.length).toEqual(2);
        expect(numDocs).toEqual(2);
        // sanity check on these docs
        angular.forEach(solrDocs, function(queryDoc) {
          var generatedUrl = queryDoc.url('altId', queryDoc.altId);
          expect(generatedUrl.indexOf('q=altId:' + queryDoc.altId)).toNotBe(-1);
        });
      });
      $httpBackend.flush();
      $httpBackend.verifyNoOutstandingExpectation();

    });

    it('escapes percents in the query', function() {
      var mockSolrParamsWithMm = angular.copy(mockSolrParams);
      mockSolrParamsWithMm.mm = ['100%'];
      var expectedParamsMm = angular.copy(expectedParams);
      mockSolrParamsWithMm.mm = ['100%25'];
      var fieldSpec = fieldSpecSvc.createFieldSpec('id:altId');
      var searcher = solrSearchSvc.createSearcher(fieldSpec.fieldList(), mockSolrUrl,
                                                  mockSolrParamsWithMm, mockQueryText);
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParamsMm))
                              .respond(200, mockResultsAltId);
      searcher.search();
      $httpBackend.verifyNoOutstandingExpectation();

    });

  });

  it('encodes the url characters', function() {
    var s = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
    var e = '%20!%22%23%24%25%26\'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~';
    expect(e).toEqual(encodeURIComponent(s));
  });

  it('escapes solr special characters', function() {
    var textToEscape = '+-&!(){}[]^"~*?:\\';
    var escapedText = solrSearchSvc.escapeUserQuery(textToEscape);
    expect(escapedText).toEqual('\\+\\-\\&\\!\\(\\)\\{\\}\\[\\]\\^\\"\\~\\*\\?\\:\\\\');
  });

  it('makes querydocs with tokensUrl', function() {
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search().then(function() {
      var solrDocs = searcher.docs;
      var expectedFacetField = {
        'facet.field': ['field1', 'field']
      };
      angular.forEach(solrDocs, function(doc) {
        expect(urlContainsParams(mockSolrUrl, expectedFacetField).test(doc.url('id', '12'))).toBeTruthy();
      });
    });
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('escapes ids passed into url', function() {
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search().then(function() {
      var solrDocs = searcher.docs;
      var expectedFacetField = {
        'facet.field': ['field1', 'field']
      };
      angular.forEach(solrDocs, function(doc) {
        var tokenUrl = doc.url('id', 'http://12');
        expect(tokenUrl.indexOf('http://12')).toBe(-1);
        var encId = encodeURIComponent('http\\://12');
        expect(tokenUrl.indexOf(encId)).toNotBe(-1);
      });
    });
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('doesnt include score in facetfield', function() {
    var fieldSpecWithScore = fieldSpecSvc.createFieldSpec('field field1 score');
    var searcher = solrSearchSvc.createSearcher(fieldSpecWithScore.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search().then(function() {
      var solrDocs = searcher.docs;
      var expectedFacetField = {
        'facet.field': ['field1', 'field']
      };
      angular.forEach(solrDocs, function(doc) {
        expect(urlContainsParams(mockSolrUrl, expectedFacetField).test(doc.url('id', 'foo'))).toBeTruthy();
        expect(doc.url('id', 'foo').match(/facet.field=score/)).toBeFalsy();
      });
    });

    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('linkurl has wt=xml', function() {
    var fieldSpecWithScore = fieldSpecSvc.createFieldSpec('field field1 score');
    var searcher = solrSearchSvc.createSearcher(fieldSpecWithScore.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    expect(searcher.linkUrl.indexOf('wt=xml')).toNotBe(-1);

  });

  it('parses solr args', function() {
    var argStr = 'q=1234&q=5678&fq=cat:foo';
    var parsedArgs = solrSearchSvc.parseSolrArgs(argStr);

    expect(parsedArgs.q).toContain('1234');
    expect(parsedArgs.q).toContain('5678');
    expect(parsedArgs.q.length).toEqual(2);
    expect(parsedArgs.fq).toContain('cat:foo');
    expect(parsedArgs.fq.length).toEqual(1);
    expect(parsedArgs.q.length).toEqual(2);
  });

  it('parses solr url', function() {
    var urlStr = 'http://localhost:8983/solr/collection1/select?q=*:*';

    var parsedSolrUrl = solrSearchSvc.parseSolrUrl(urlStr);
    expect(parsedSolrUrl.protocol).toEqual('http:');
    expect(parsedSolrUrl.host).toEqual('localhost:8983');
    expect(parsedSolrUrl.collectionName).toEqual('collection1');
    expect(parsedSolrUrl.requestHandler).toEqual('select');
    expect(parsedSolrUrl.solrArgs.q).toContain('*:*');
  });

  describe('solr args parse/format', function() {

    var formatThenParse = function(solrArgs) {
      var formatted = solrSearchSvc.formatSolrArgs(solrArgs);
      return solrSearchSvc.parseSolrArgs(formatted);
    };

    var solrArgsEqual = function(args1, args2) {
      angular.forEach(args1, function(values, key) {
        expect(args2.hasOwnProperty(key));
        var values2 = args2[key];
        expect(values).toEqual(values2);
      });
    };

    it('formats/parses basic', function() {
      var solrArgs = {q: ['*:*'], fq: ['title:bar', 'text:foo']};
      var parsedBack = formatThenParse(solrArgs);
      solrArgsEqual(solrArgs, parsedBack);
    });

  });

});
<commit_msg>Add args formatting<commit_after>'use strict';
/* global urlContainsParams*/
describe('Service: solrSearchSvc', function () {

  // load the service's module
  beforeEach(module('splain-app'));

  // instantiate service
  var solrSearchSvc;
  var $httpBackend = null;
  var fieldSpecSvc = null;
  var mockSolrUrl = 'http://example.com:1234/solr/select';
  var mockSolrParams = {
    q: ['#$query##'],
    fq: ['field:value', 'field1:value', 'field2:#$query##']
  };
  var expectedParams = angular.copy(mockSolrParams);
  var mockQueryText = 'query text';
  var mockFieldSpec = null;
  expectedParams.q[0] =   encodeURIComponent(mockQueryText);
  expectedParams.fq[2] = 'field2:' + encodeURIComponent(mockQueryText);
  var mockResults = {
    response: {
      numFound: 2,
      docs : [
        {id: 'doc1'}, {id: 'doc2'}
      ]
    }
  };

  beforeEach(inject(function($injector) {
    $httpBackend = $injector.get('$httpBackend');
  }));

  beforeEach(inject(function (_solrSearchSvc_, _fieldSpecSvc_) {
    solrSearchSvc = _solrSearchSvc_;
    fieldSpecSvc = _fieldSpecSvc_;
    mockFieldSpec = fieldSpecSvc.createFieldSpec('field field1');
  }));

  it('access solr with mock solr params', function() {
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search();
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('tracks active queries', function() {
    expect(solrSearchSvc.activeQueries()).toEqual(0);
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search();
    expect(solrSearchSvc.activeQueries()).toEqual(1);
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
    expect(solrSearchSvc.activeQueries()).toEqual(0);
  });

  describe('explain info ', function() {

    var fullSolrResp = {'responseHeader':{
      'status':0,
      'QTime':1,
      'params':{
          'df':'content',
          'echoParams':'all',
          'rows':'2',
          'debugQuery':'true',
          'fl':'path content',
          'indent':['true',
            'true'],
          'q':'*:*',
          'wt':'json',
        }
      },
      'response':{'numFound':100,'start':0,'docs':[
          {
            'content':'stuff',
            'path':'http://larkin.com/index/'
          },
          {
            'content':'more stuff',
            'path':'http://www.rogahnbins.com/main.html'
          }
        ]
      },
      'debug':{
          'rawquerystring':'*:*',
          'querystring':'*:*',
          'parsedquery':'MatchAllDocsQuery(*:*)',
          'parsedquery_toString':'*:*',
          'explain':{
            'http://larkin.com/index/':'\n1.0 = (MATCH) MatchAllDocsQuery, product of:\n  1.0 = queryNorm\n',
            'http://www.rogahnbins.com/main.html':'\n1.0 = (MATCH) MatchAllDucksQuery, product of:\n  1.0 = queryNorm\n'
          },
          'QParser':'LuceneQParser',
          'timing':{
            'time':1.0,
            'prepare':{
              'time':1.0,
              'query':{
                'time':0.0
              },
              'facet':{
                'time':0.0
              },
              'mlt':{
                'time':0.0
              },
              'highlight':{
                'time':0.0
              },
              'stats':{
                'time':0.0
              },
              'debug':{
                'time':0.0
              }
            },
            'process':{
              'time':0.0,
              'query':{
                'time':0.0
              },
              'facet':{
                'time':0.0
              },
              'mlt':{
                'time':0.0
              },
              'highlight':{
                'time':0.0
              },
              'stats':{
                'time':0.0
              },
              'debug':{
                'time':0.0
              }
            }
          }
        }
    };
    var fieldSpec = null;
    var searcher = null;

    beforeEach(function() {
      fieldSpec = fieldSpecSvc.createFieldSpec('id:path content');
      searcher = solrSearchSvc.createSearcher(fieldSpec.fieldList(), mockSolrUrl,
                                                  mockSolrParams, mockQueryText);
    });

    it('populates explain()', function() {
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                              .respond(200, fullSolrResp);
      searcher.search().then(function() {
        var solrDocs = searcher.docs;
        expect(solrDocs[0].explain('http://larkin.com/index/')).toEqual('\n1.0 = (MATCH) MatchAllDocsQuery, product of:\n  1.0 = queryNorm\n');
        expect(solrDocs[1].explain('http://www.rogahnbins.com/main.html')).toEqual('\n1.0 = (MATCH) MatchAllDucksQuery, product of:\n  1.0 = queryNorm\n');
      });
      $httpBackend.flush();
      $httpBackend.verifyNoOutstandingExpectation();
    });
  });

  // For tests where "id" is not the id field

  describe('alt id field tests', function() {
    var mockResultsAltId = {
      response: {
        numFound: 2,
        docs : [
          {altId: 'alt_doc1'}, {altId: 'alt_doc2'}
        ]
      }
    };

    var fieldSpec = null;
    var searcher = null;
    beforeEach(function() {
      fieldSpec = fieldSpecSvc.createFieldSpec('id:altId');
      searcher = solrSearchSvc.createSearcher(fieldSpec.fieldList(), mockSolrUrl,
                                                  mockSolrParams, mockQueryText);
    });

    it('works with an alternate id field', function() {
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                              .respond(200, mockResultsAltId);
      searcher.search().then(function() {
        var numDocs = searcher.numFound;
        var solrDocs = searcher.docs;
        expect(solrDocs.length).toEqual(2);
        expect(numDocs).toEqual(2);
        // sanity check on these docs
        angular.forEach(solrDocs, function(queryDoc) {
          expect(['alt_doc1', 'alt_doc2']).toContain(queryDoc.altId);
        });
      });
      $httpBackend.flush();
      $httpBackend.verifyNoOutstandingExpectation();
    });

    it('creates docs that can construct tokens URL', function() {
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                              .respond(200, mockResultsAltId);
      searcher.search().then(function() {
        var numDocs = searcher.numFound;
        var solrDocs = searcher.docs;
        expect(solrDocs.length).toEqual(2);
        expect(numDocs).toEqual(2);
        // sanity check on these docs
        angular.forEach(solrDocs, function(queryDoc) {
          var generatedUrl = queryDoc.url('altId', queryDoc.altId);
          expect(generatedUrl.indexOf('q=altId:' + queryDoc.altId)).toNotBe(-1);
        });
      });
      $httpBackend.flush();
      $httpBackend.verifyNoOutstandingExpectation();

    });

    it('escapes percents in the query', function() {
      var mockSolrParamsWithMm = angular.copy(mockSolrParams);
      mockSolrParamsWithMm.mm = ['100%'];
      var expectedParamsMm = angular.copy(expectedParams);
      mockSolrParamsWithMm.mm = ['100%25'];
      var fieldSpec = fieldSpecSvc.createFieldSpec('id:altId');
      var searcher = solrSearchSvc.createSearcher(fieldSpec.fieldList(), mockSolrUrl,
                                                  mockSolrParamsWithMm, mockQueryText);
      $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParamsMm))
                              .respond(200, mockResultsAltId);
      searcher.search();
      $httpBackend.verifyNoOutstandingExpectation();

    });

  });

  it('encodes the url characters', function() {
    var s = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
    var e = '%20!%22%23%24%25%26\'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~';
    expect(e).toEqual(encodeURIComponent(s));
  });

  it('escapes solr special characters', function() {
    var textToEscape = '+-&!(){}[]^"~*?:\\';
    var escapedText = solrSearchSvc.escapeUserQuery(textToEscape);
    expect(escapedText).toEqual('\\+\\-\\&\\!\\(\\)\\{\\}\\[\\]\\^\\"\\~\\*\\?\\:\\\\');
  });

  it('makes querydocs with tokensUrl', function() {
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search().then(function() {
      var solrDocs = searcher.docs;
      var expectedFacetField = {
        'facet.field': ['field1', 'field']
      };
      angular.forEach(solrDocs, function(doc) {
        expect(urlContainsParams(mockSolrUrl, expectedFacetField).test(doc.url('id', '12'))).toBeTruthy();
      });
    });
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('escapes ids passed into url', function() {
    var searcher = solrSearchSvc.createSearcher(mockFieldSpec.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search().then(function() {
      var solrDocs = searcher.docs;
      var expectedFacetField = {
        'facet.field': ['field1', 'field']
      };
      angular.forEach(solrDocs, function(doc) {
        var tokenUrl = doc.url('id', 'http://12');
        expect(tokenUrl.indexOf('http://12')).toBe(-1);
        var encId = encodeURIComponent('http\\://12');
        expect(tokenUrl.indexOf(encId)).toNotBe(-1);
      });
    });
    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('doesnt include score in facetfield', function() {
    var fieldSpecWithScore = fieldSpecSvc.createFieldSpec('field field1 score');
    var searcher = solrSearchSvc.createSearcher(fieldSpecWithScore.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    $httpBackend.expectJSONP(urlContainsParams(mockSolrUrl, expectedParams))
                            .respond(200, mockResults);
    searcher.search().then(function() {
      var solrDocs = searcher.docs;
      var expectedFacetField = {
        'facet.field': ['field1', 'field']
      };
      angular.forEach(solrDocs, function(doc) {
        expect(urlContainsParams(mockSolrUrl, expectedFacetField).test(doc.url('id', 'foo'))).toBeTruthy();
        expect(doc.url('id', 'foo').match(/facet.field=score/)).toBeFalsy();
      });
    });

    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
  });

  it('linkurl has wt=xml', function() {
    var fieldSpecWithScore = fieldSpecSvc.createFieldSpec('field field1 score');
    var searcher = solrSearchSvc.createSearcher(fieldSpecWithScore.fieldList(), mockSolrUrl,
                                                mockSolrParams, mockQueryText);
    expect(searcher.linkUrl.indexOf('wt=xml')).toNotBe(-1);

  });

  it('parses solr args', function() {
    var argStr = 'q=1234&q=5678&fq=cat:foo';
    var parsedArgs = solrSearchSvc.parseSolrArgs(argStr);

    expect(parsedArgs.q).toContain('1234');
    expect(parsedArgs.q).toContain('5678');
    expect(parsedArgs.q.length).toEqual(2);
    expect(parsedArgs.fq).toContain('cat:foo');
    expect(parsedArgs.fq.length).toEqual(1);
    expect(parsedArgs.q.length).toEqual(2);
  });

  it('parses solr url', function() {
    var urlStr = 'http://localhost:8983/solr/collection1/select?q=*:*';

    var parsedSolrUrl = solrSearchSvc.parseSolrUrl(urlStr);
    expect(parsedSolrUrl.protocol).toEqual('http:');
    expect(parsedSolrUrl.host).toEqual('localhost:8983');
    expect(parsedSolrUrl.collectionName).toEqual('collection1');
    expect(parsedSolrUrl.requestHandler).toEqual('select');
    expect(parsedSolrUrl.solrArgs.q).toContain('*:*');
  });

  describe('solr args parse/format', function() {

    var formatThenParse = function(solrArgs) {
      var formatted = solrSearchSvc.formatSolrArgs(solrArgs);
      return solrSearchSvc.parseSolrArgs(formatted);
    };

    var solrArgsEqual = function(args1, args2) {
      angular.forEach(args1, function(values, key) {
        expect(args2.hasOwnProperty(key));
        var values2 = args2[key];
        expect(values).toEqual(values2);
      });
    };

    it('formats/parses basic', function() {
      console.log('wut');
      var solrArgs = {q: ['*:*'], fq: ['title:bar', 'text:foo']};
      var parsedBack = formatThenParse(solrArgs);
      solrArgsEqual(solrArgs, parsedBack);
    });

  });

});
<|endoftext|>


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

    angular
        .module('app.core')
        .config(RouteConfig);

    RouteConfig.$inject = ['$stateProvider'];

    function RouteConfig($stateProvider) {
        $stateProvider
            .state('login', {
                url: '/login',
                templateUrl: 'app/login/login.html',
                controller: 'LoginController',
                controllerAs: 'vm',
                data: {
                    title: 'Login',
                    protected: false
                }
            })
            .state('callback', {
                url: '/login/callback?code&state',
                templateUrl: 'app/login/callback/callback.html',
                controller: 'CallbackController',
                controllerAs: 'vm',
                data: {
                    title: 'Callback',
                    protected: false
                }
            })
            .state('main', {
                url: '/main',
                templateUrl: 'app/main//main.html',
                controller: 'MainController',
                controllerAs: 'vm',
                data: {
                    title: 'Main',
                    protected: true
                }
            })
            .state('main.step1', {
                url: '/step1',
                templateUrl: 'app/main/step1/step1.html',
                controller: 'Step1Controller',
                controllerAs: 'vm',
                data: {
                    title: 'Step 1',
                    protected: true
                }
            })
            .state('main.step2', {
                url: '/step2',
                templateUrl: 'app/main/step2/step2.html',
                controller: 'Step2Controller',
                controllerAs: 'vm',
                data: {
                    title: 'Step 2',
                    protected: true
                }
            })
            .state('main.result', {
                url: '/step2',
                templateUrl: 'app/main/result/result.html',
                controller: 'ResultController',
                controllerAs: 'vm',
                data: {
                    title: 'Result',
                    protected: true
                }
            })
            .state('about', {
                url: '/about',
                templateUrl: 'app/about/about.html',
                controller: 'AboutController',
                controllerAs: 'vm',
                data: {
                    title: 'About',
                    protected: false
                }
            });
    }
})();<commit_msg>Fixed incorrect state url<commit_after>(function() {
    'use strict';

    angular
        .module('app.core')
        .config(RouteConfig);

    RouteConfig.$inject = ['$stateProvider'];

    function RouteConfig($stateProvider) {
        $stateProvider
            .state('login', {
                url: '/login',
                templateUrl: 'app/login/login.html',
                controller: 'LoginController',
                controllerAs: 'vm',
                data: {
                    title: 'Login',
                    protected: false
                }
            })
            .state('callback', {
                url: '/login/callback?code&state',
                templateUrl: 'app/login/callback/callback.html',
                controller: 'CallbackController',
                controllerAs: 'vm',
                data: {
                    title: 'Callback',
                    protected: false
                }
            })
            .state('main', {
                url: '/main',
                templateUrl: 'app/main//main.html',
                controller: 'MainController',
                controllerAs: 'vm',
                data: {
                    title: 'Main',
                    protected: true
                }
            })
            .state('main.step1', {
                url: '/step1',
                templateUrl: 'app/main/step1/step1.html',
                controller: 'Step1Controller',
                controllerAs: 'vm',
                data: {
                    title: 'Step 1',
                    protected: true
                }
            })
            .state('main.step2', {
                url: '/step2',
                templateUrl: 'app/main/step2/step2.html',
                controller: 'Step2Controller',
                controllerAs: 'vm',
                data: {
                    title: 'Step 2',
                    protected: true
                }
            })
            .state('main.result', {
                url: '/result',
                templateUrl: 'app/main/result/result.html',
                controller: 'ResultController',
                controllerAs: 'vm',
                data: {
                    title: 'Result',
                    protected: true
                }
            })
            .state('about', {
                url: '/about',
                templateUrl: 'app/about/about.html',
                controller: 'AboutController',
                controllerAs: 'vm',
                data: {
                    title: 'About',
                    protected: false
                }
            });
    }
})();<|endoftext|>