<commit_before>//////////////////////////////////////////////////////////////////////////////
/**
 * @module ggl
 */

/*jslint devel: true, forin: true, newcap: true, plusplus: true*/
/*jslint white: true, indent: 2*/

/*global ggl, geo, ogs, inherit, $, HTMLCanvasElement, Image*/
/*global vgl, vec4, document*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of mapInteractorStyle
 *
 * @class geo.mapInteractorStyle
 * @returns {geo.mapInteractorStyle}
 */
//////////////////////////////////////////////////////////////////////////////
ggl.mapInteractorStyle = function() {
  "use strict";
  if (!(this instanceof ggl.mapInteractorStyle)) {
    return new ggl.mapInteractorStyle();
  }
  vgl.interactorStyle.call(this);
  var m_map, m_this = this, m_mapZoomLevel = 3, m_leftMouseButtonDown = false,
      m_rightMouseButtonDown = false, m_middileMouseButtonDown = false,
      m_initRightBtnMouseDown = false, m_drawRegionMode = false,
      m_drawRegionLayer, m_clickLatLng, m_width, m_height,
      m_renderer, m_renderWindow, m_camera, m_outsideCanvas,
      m_currentMousePos, m_focusDisplayPoint, m_zTrans,
      m_coords, m_mouseLastPos = { x: 0, y: 0 }, m_picker = new vgl.picker(),
      m_updateRenderParamsTime = vgl.timestamp();

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets map for this interactor, adds draw region layer if needed
   *
   * @param {geo.map} newMap optional
   * @returns {geo.mapInteractorStyle|geo.map}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.map = function(val) {
    if (typeof val !== 'undefined') {
      m_map = val;
      return m_this;
    }
    return m_map;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets drawRegionMode for this interactor
   *
   * @param {Boolean} newValue optional
   * @returns {geo.mapInteractorStyle|Boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.drawRegionMode = function(val) {
    if (typeof val !== 'undefined') {
      m_drawRegionMode = val;
      if(m_drawRegionLayer) {
        m_drawRegionLayer.setVisible(val);
      }
      m_map.draw();
      return m_this;
    }
    return m_drawRegionMode;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Internal function to pan a camera associated with a renderer.
   *
   * @param {vgl.renderer} the renderer whose camera should be panned.
   */
  ////////////////////////////////////////////////////////////////////////////
  this._panCamera = function(renderer) {
    var worldPt1, worldPt2, dx, dy, dz, focusDisplayPoint = renderer.focusDisplayPoint();

    worldPt1 = m_renderWindow.displayToWorld(m_currentMousePos.x,
      m_currentMousePos.y, focusDisplayPoint, renderer);
    worldPt2 = m_renderWindow.displayToWorld(m_mouseLastPos.x,
      m_mouseLastPos.y, focusDisplayPoint, renderer);

    dx = worldPt1[0] - worldPt2[0];
    dy = worldPt1[1] - worldPt2[1];
    dz = worldPt1[2] - worldPt2[2];

    renderer.camera().pan(-dx, -dy, -dz);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseMove = function(event) {
    /// Local vars
    var canvas = m_this.map(), xrot = null, a = null,
        angle = null, mouseWorldPoint, features, lastWorldPos,
        currWorldPos, lastZoom, evt, newMercPerPixel, oldMercPerPixel, i,
        renderers;

    /// Update render params
    m_this.updateRenderParams();

    /// Compute current mouse position
    m_this._computeCurrentMousePos(event);

    if (m_outsideCanvas === true) {
      return true; // allow bubbling up the event
    }
    if (m_leftMouseButtonDown) {
      if(m_drawRegionMode) {
        mouseWorldPoint = m_map.displayToMap(m_currentMousePos.x,
          m_currentMousePos.y);
        m_this.setDrawRegion(m_clickLatLng.lat(), m_clickLatLng.lng(),
          mouseWorldPoint.y, mouseWorldPoint.x);
      } else {
        lastWorldPos = m_camera.position();
        // Pan all cameras associated with the render window.
        renderers = m_renderWindow.renderers();
        for(i = 0; i < renderers.length; i++) {
          m_this._panCamera(renderers[i]);
        }
        currWorldPos = m_camera.position();

        // TODO Do we need to emit an event for each ?
        evt = {type: geo.event.pan,
               last_display_pos: m_mouseLastPos,
               curr_display_pos: m_currentMousePos,
               last_world_pos: lastWorldPos,
               curr_world_pos: currWorldPos};

        $(m_this).trigger(evt);
      }
    }
    if (m_middileMouseButtonDown) {
      /// DO NOTHING AS OF NOW
    }
    if (m_rightMouseButtonDown && m_height > 0) {
      /// 2.0 is sort of speed up factor
      m_zTrans = 2.0 * (m_currentMousePos.y - m_mouseLastPos.y) / m_height;
      m_this._zoomCameras();

      /// For now just trigger the render. Later on, we may want to
      /// trigger an external event
      m_renderWindow.render();
    }

    m_mouseLastPos.x = m_currentMousePos.x;
    m_mouseLastPos.y = m_currentMousePos.y;
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse down event
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseDown = function(event) {
    var canvas = m_this.map(), point, plane;

    /// Update render parameters
    m_this.updateRenderParams();

    if (event.button === 0) {
      m_leftMouseButtonDown = true;
    }
    if (event.button === 1) {
      m_middileMouseButtonDown = true;
    }
    if (event.button === 2) {
      m_rightMouseButtonDown = true;
    }

    m_coords = m_this.viewer().relMouseCoords(event);
    if (m_coords.x < 0) {
      m_mouseLastPos.x = 0;
    } else {
      m_mouseLastPos.x = m_coords.x;
    }
    if (m_coords.y < 0) {
      m_mouseLastPos.y = 0;
    } else {
      m_mouseLastPos.y = m_coords.y;
    }

    if(m_drawRegionMode && m_leftMouseButtonDown) {
      point = m_map.displayToMap(m_mouseLastPos.x, m_mouseLastPos.y);
      m_clickLatLng = geo.latlng(point.y, point.x);
      m_this.setDrawRegion(point.y, point.x, point.y, point.x);
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @NOTE We never get mouse up from scroll bar: See the bug report here
   * http://bugs.jquery.com/ticket/8184
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseUp = function(event) {
    var canvas = m_this.map(),
        width = null,
        height = null,
        num = null;

    /// Update render params
    m_this.updateRenderParams();

    if (event.button === 0) {
      m_leftMouseButtonDown = false;
      width = m_this.viewer().renderWindow().windowSize()[0];
      height = m_this.viewer().renderWindow().windowSize()[1];
      m_renderer = m_this.viewer().renderWindow().activeRenderer();
      if(m_mouseLastPos.x >= 0 && m_mouseLastPos.x <= width &&
        m_mouseLastPos.y >= 0 && m_mouseLastPos.y <= height) {
        num = m_picker.pick(m_mouseLastPos.x, m_mouseLastPos.y, m_renderer);
      }
    }
    if (event.button === 1) {
      m_middileMouseButtonDown = false;
    }
    if (event.button === 2) {
      m_rightMouseButtonDown = false;
      m_initRightBtnMouseDown = false;

      /// Now zoom
      m_this.zoom();
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle event when mouse goes out of canvas
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseOut = function(event) {
    /// Update render params
    m_this.updateRenderParams();

    if (m_leftMouseButtonDown) {
      m_leftMouseButtonDown = false;
    } else if (m_middileMouseButtonDown) {
      m_middileMouseButtonDown = false;
    } if (m_rightMouseButtonDown) {
      m_rightMouseButtonDown = false;
      m_initRightBtnMouseDown = false;

      /// Perform zoom when the mouse goes out of canvas as we
      /// are treating mouse out as right button up.
      m_this.zoom();
    }
    return false;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse wheel event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseWheel = function(event) {
    /// Update render params
    m_this.updateRenderParams();

    var delta = event.originalEvent.wheelDelta / 120.0;
    delta = Math.pow(1 + Math.abs(delta)/2 , delta > 0 ? 1 : -1);

    /// Compute current mouse position
    m_this._computeCurrentMousePos(event);

    m_this.zoom(delta);
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Internal function to zoom cameras
   * @param {Number} optional value to zoom by
   */
  ////////////////////////////////////////////////////////////////////////////
  this._zoomCameras = function(val) {
    var val, i, renderers;

    if (val === undefined) {
      if (m_zTrans > 0) {
        val = 1 - Math.abs(m_zTrans);
      } else {
        val = 1 + Math.abs(m_zTrans);
      }
    }

    m_camera.zoom(val);
    m_renderer.resetCameraClippingRange();

    renderers = m_renderWindow.renderers();
    for (i = 0; i < renderers.length; i++) {
      if (renderers[i].camera() !== m_camera) {
        renderers[i].camera().zoom(val);
        renderers[i].resetCameraClippingRange();
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Update view in response to a zoom request
   */
  ////////////////////////////////////////////////////////////////////////////
  this.zoom = function(val) {
    var oldMercPerPixel, newMercPerPixel, evt, i, renderers, worldPt1,
        worldPt2;

    /// Update render params
    m_this.updateRenderParams();

    m_zTrans = (m_currentMousePos.y - m_mouseLastPos.y) / m_height;

    /// Compute meters per pixel here and based on that decide the
    /// zoom level
    worldPt1 = m_renderWindow.displayToWorld(0, 0, m_focusDisplayPoint);
    worldPt2 = m_renderWindow.displayToWorld(m_width, m_height,
                                             m_focusDisplayPoint);

    /// Computer mercator per pixel before changing the camera position
    oldMercPerPixel = (worldPt2[0] - worldPt1[0]) / m_width;

    this._zoomCameras(val);

    worldPt1 = m_renderWindow.displayToWorld(0, 0, m_focusDisplayPoint);
    worldPt2 = m_renderWindow.displayToWorld(m_width, m_height,
                   m_focusDisplayPoint);

    /// Computer mercator per pixel as of now
    newMercPerPixel = (worldPt2[0] - worldPt1[0]) / m_width;

    /// Compute meters per pixel here and based on that decide the
    /// zoom level
    evt = { type: geo.event.zoom,
            curr_zoom: computeZoomLevel(newMercPerPixel),
            last_zoom: computeZoomLevel(oldMercPerPixel) };
    $(m_this).trigger(evt);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets the lastMousePosition for this interactor
   *
   * @param newPosition optional
   * @param {Number} newPosition.x
   * @param {Number} newPosition.y
   * @returns {geo.mapInteractorStyle|Object}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.lastMousePosition = function(newPosition) {
    if(typeof newPosition !== 'undefined') {
      m_mouseLastPos = newPosition;
      return m_this;
    }
    return m_mouseLastPos;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets leftMouseDown for this interactor
   *
   * @param {Boolean} newValue optional
   * @returns {geo.mapInteractorStyle|Boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.leftMouseDown = function(newValue) {
    if(typeof newValue !== 'undefined') {
      m_leftMouseButtonDown = newValue;
      return m_this;
    }
    return m_leftMouseButtonDown;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets the draw region coordinates for this interactor
   *
   * @param {Number} lat1
   * @param {Number} lon1
   * @param {Number} lat2
   * @param {Number} lon2
   * @returns {geo.mapInteractorStyle}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setDrawRegion = function(lat1, lon1, lat2, lon2) {
    // TODO
    // Use z-indexing or some other technique for the offsetting
    var evt, plane = geo.planeFeature(
      geo.latlng(lat1, lon1),
      geo.latlng(lat2, lon2),
      99
    );

    m_map.removeLayer(m_drawRegionLayer);

    m_drawRegionLayer = geo.featureLayer({
      "opacity": 0.5,
      "showAttribution": 1
    }, plane);

    m_map.addLayer(m_drawRegionLayer);

    /// TODO pass bounding box
    evt = jQuery.Event(geo.event.updateDrawRegionEvent);
    $(m_this).trigger(geo.command.updateDrawRegionEvent, evt);

    return m_this;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Gets the draw region coordinates for this interactor
   *
   * @returns {Array} [lat1, lon1, lat2, lon2]
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getDrawRegion = function() {
    return m_drawRegionLayer.features()[0].getCoords();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute current mouse position
   *
   * @protected
   */
  ////////////////////////////////////////////////////////////////////////////
  this._computeCurrentMousePos = function(event) {
    /// Update render params
    m_this.updateRenderParams();

    m_outsideCanvas = false;
    m_coords = m_this.viewer().relMouseCoords(event);
    m_currentMousePos = {
      x: 0,
      y: 0
    };
    if ((m_coords.x < 0) || (m_coords.x > m_width)) { // off-by-one error
      m_currentMousePos.x = 0;
      m_outsideCanvas = true;
    } else {
      m_currentMousePos.x = m_coords.x;
    }
    if ((m_coords.y < 0) || (m_coords.y > m_height)) { // off-by-one error
      m_currentMousePos.y = 0;
      m_outsideCanvas = true;
    } else {
      m_currentMousePos.y = m_coords.y;
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute current mouse position
   *
   * @protected
   */
  ////////////////////////////////////////////////////////////////////////////
  this.updateRenderParams = function() {
    m_renderWindow = m_this.viewer().renderWindow();
    m_width = m_renderWindow.windowSize()[0];
    m_height = m_renderWindow.windowSize()[1];
    m_renderer = m_this.viewer().renderWindow().activeRenderer();
    m_camera = m_renderer.camera();
    m_updateRenderParamsTime.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute zoom level
   *
   * @param deltaMerc mercator/per pixel
   * @returns {Number} zoom level
   *
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  function computeZoomLevel(deltaMerc) {
    var i, metersPerPixel, metersPerPixelFull = 156412;
    metersPerPixel = geo.mercator.deg2rad(Math.abs(deltaMerc)) *
                     geo.mercator.r_major;
    for (i = 4; i < 20; ++i) {
      if (metersPerPixel > (metersPerPixelFull / Math.pow(2, i))) {
        return (i - 1);
      }
    }
  };

  return this;
};

inherit(ggl.mapInteractorStyle, vgl.interactorStyle);
<commit_msg>Fixed missing var initialization<commit_after>//////////////////////////////////////////////////////////////////////////////
/**
 * @module ggl
 */

/*jslint devel: true, forin: true, newcap: true, plusplus: true*/
/*jslint white: true, indent: 2*/

/*global ggl, geo, ogs, inherit, $, HTMLCanvasElement, Image*/
/*global vgl, vec4, document*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of mapInteractorStyle
 *
 * @class geo.mapInteractorStyle
 * @returns {geo.mapInteractorStyle}
 */
//////////////////////////////////////////////////////////////////////////////
ggl.mapInteractorStyle = function() {
  "use strict";
  if (!(this instanceof ggl.mapInteractorStyle)) {
    return new ggl.mapInteractorStyle();
  }
  vgl.interactorStyle.call(this);
  var m_map, m_this = this, m_mapZoomLevel = 3, m_leftMouseButtonDown = false,
      m_rightMouseButtonDown = false, m_middileMouseButtonDown = false,
      m_initRightBtnMouseDown = false, m_drawRegionMode = false,
      m_drawRegionLayer, m_clickLatLng, m_width, m_height,
      m_renderer, m_renderWindow, m_camera, m_outsideCanvas,
      m_currentMousePos, m_focusDisplayPoint, m_zTrans,
      m_coords, m_mouseLastPos = { x: 0, y: 0 }, m_picker = new vgl.picker(),
      m_updateRenderParamsTime = vgl.timestamp();

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets map for this interactor, adds draw region layer if needed
   *
   * @param {geo.map} newMap optional
   * @returns {geo.mapInteractorStyle|geo.map}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.map = function(val) {
    if (typeof val !== 'undefined') {
      m_map = val;
      return m_this;
    }
    return m_map;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets drawRegionMode for this interactor
   *
   * @param {Boolean} newValue optional
   * @returns {geo.mapInteractorStyle|Boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.drawRegionMode = function(val) {
    if (typeof val !== 'undefined') {
      m_drawRegionMode = val;
      if(m_drawRegionLayer) {
        m_drawRegionLayer.setVisible(val);
      }
      m_map.draw();
      return m_this;
    }
    return m_drawRegionMode;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Internal function to pan a camera associated with a renderer.
   *
   * @param {vgl.renderer} the renderer whose camera should be panned.
   */
  ////////////////////////////////////////////////////////////////////////////
  this._panCamera = function(renderer) {
    var worldPt1, worldPt2, dx, dy, dz, focusDisplayPoint = renderer.focusDisplayPoint();

    worldPt1 = m_renderWindow.displayToWorld(m_currentMousePos.x,
      m_currentMousePos.y, focusDisplayPoint, renderer);
    worldPt2 = m_renderWindow.displayToWorld(m_mouseLastPos.x,
      m_mouseLastPos.y, focusDisplayPoint, renderer);

    dx = worldPt1[0] - worldPt2[0];
    dy = worldPt1[1] - worldPt2[1];
    dz = worldPt1[2] - worldPt2[2];

    renderer.camera().pan(-dx, -dy, -dz);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseMove = function(event) {
    /// Local vars
    var canvas = m_this.map(), xrot = null, a = null,
        angle = null, mouseWorldPoint, features, lastWorldPos,
        currWorldPos, lastZoom, evt, newMercPerPixel, oldMercPerPixel, i,
        renderers;

    /// Update render params
    m_this.updateRenderParams();

    /// Compute current mouse position
    m_this._computeCurrentMousePos(event);

    if (m_outsideCanvas === true) {
      return true; // allow bubbling up the event
    }
    if (m_leftMouseButtonDown) {
      if(m_drawRegionMode) {
        mouseWorldPoint = m_map.displayToMap(m_currentMousePos.x,
          m_currentMousePos.y);
        m_this.setDrawRegion(m_clickLatLng.lat(), m_clickLatLng.lng(),
          mouseWorldPoint.y, mouseWorldPoint.x);
      } else {
        lastWorldPos = m_camera.position();
        // Pan all cameras associated with the render window.
        renderers = m_renderWindow.renderers();
        for(i = 0; i < renderers.length; i++) {
          m_this._panCamera(renderers[i]);
        }
        currWorldPos = m_camera.position();

        // TODO Do we need to emit an event for each ?
        evt = {type: geo.event.pan,
               last_display_pos: m_mouseLastPos,
               curr_display_pos: m_currentMousePos,
               last_world_pos: lastWorldPos,
               curr_world_pos: currWorldPos};

        $(m_this).trigger(evt);
      }
    }
    if (m_middileMouseButtonDown) {
      /// DO NOTHING AS OF NOW
    }
    if (m_rightMouseButtonDown && m_height > 0) {
      /// 2.0 is sort of speed up factor
      m_zTrans = 2.0 * (m_currentMousePos.y - m_mouseLastPos.y) / m_height;
      m_this._zoomCameras();

      /// For now just trigger the render. Later on, we may want to
      /// trigger an external event
      m_renderWindow.render();
    }

    m_mouseLastPos.x = m_currentMousePos.x;
    m_mouseLastPos.y = m_currentMousePos.y;
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse down event
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseDown = function(event) {
    var canvas = m_this.map(), point, plane;

    /// Update render parameters
    m_this.updateRenderParams();

    if (event.button === 0) {
      m_leftMouseButtonDown = true;
    }
    if (event.button === 1) {
      m_middileMouseButtonDown = true;
    }
    if (event.button === 2) {
      m_rightMouseButtonDown = true;
    }

    m_coords = m_this.viewer().relMouseCoords(event);
    if (m_coords.x < 0) {
      m_mouseLastPos.x = 0;
    } else {
      m_mouseLastPos.x = m_coords.x;
    }
    if (m_coords.y < 0) {
      m_mouseLastPos.y = 0;
    } else {
      m_mouseLastPos.y = m_coords.y;
    }

    if(m_drawRegionMode && m_leftMouseButtonDown) {
      point = m_map.displayToMap(m_mouseLastPos.x, m_mouseLastPos.y);
      m_clickLatLng = geo.latlng(point.y, point.x);
      m_this.setDrawRegion(point.y, point.x, point.y, point.x);
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @NOTE We never get mouse up from scroll bar: See the bug report here
   * http://bugs.jquery.com/ticket/8184
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseUp = function(event) {
    var canvas = m_this.map(),
        width = null,
        height = null,
        num = null;

    /// Update render params
    m_this.updateRenderParams();

    if (event.button === 0) {
      m_leftMouseButtonDown = false;
      width = m_this.viewer().renderWindow().windowSize()[0];
      height = m_this.viewer().renderWindow().windowSize()[1];
      m_renderer = m_this.viewer().renderWindow().activeRenderer();
      if(m_mouseLastPos.x >= 0 && m_mouseLastPos.x <= width &&
        m_mouseLastPos.y >= 0 && m_mouseLastPos.y <= height) {
        num = m_picker.pick(m_mouseLastPos.x, m_mouseLastPos.y, m_renderer);
      }
    }
    if (event.button === 1) {
      m_middileMouseButtonDown = false;
    }
    if (event.button === 2) {
      m_rightMouseButtonDown = false;
      m_initRightBtnMouseDown = false;

      /// Now zoom
      m_this.zoom();
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle event when mouse goes out of canvas
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseOut = function(event) {
    /// Update render params
    m_this.updateRenderParams();

    if (m_leftMouseButtonDown) {
      m_leftMouseButtonDown = false;
    } else if (m_middileMouseButtonDown) {
      m_middileMouseButtonDown = false;
    } if (m_rightMouseButtonDown) {
      m_rightMouseButtonDown = false;
      m_initRightBtnMouseDown = false;

      /// Perform zoom when the mouse goes out of canvas as we
      /// are treating mouse out as right button up.
      m_this.zoom();
    }
    return false;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse wheel event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseWheel = function(event) {
    /// Update render params
    m_this.updateRenderParams();

    var delta = event.originalEvent.wheelDelta / 120.0;
    delta = Math.pow(1 + Math.abs(delta)/2 , delta > 0 ? 1 : -1);

    /// Compute current mouse position
    m_this._computeCurrentMousePos(event);

    m_this.zoom(delta);
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Internal function to zoom cameras
   * @param {Number} optional value to zoom by
   */
  ////////////////////////////////////////////////////////////////////////////
  this._zoomCameras = function(val) {
    var val, i, renderers;

    if (val === undefined) {
      if (m_zTrans > 0) {
        val = 1 - Math.abs(m_zTrans);
      } else {
        val = 1 + Math.abs(m_zTrans);
      }
    }

    m_camera.zoom(val);
    m_renderer.resetCameraClippingRange();

    renderers = m_renderWindow.renderers();
    for (i = 0; i < renderers.length; i++) {
      if (renderers[i].camera() !== m_camera) {
        renderers[i].camera().zoom(val);
        renderers[i].resetCameraClippingRange();
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Update view in response to a zoom request
   */
  ////////////////////////////////////////////////////////////////////////////
  this.zoom = function(val) {
    var oldMercPerPixel, newMercPerPixel, evt, i, renderers, worldPt1,
        worldPt2;

    /// Update render params
    m_this.updateRenderParams();

    m_zTrans = (m_currentMousePos.y - m_mouseLastPos.y) / m_height;

    /// Compute meters per pixel here and based on that decide the
    /// zoom level
    worldPt1 = m_renderWindow.displayToWorld(0, 0, m_focusDisplayPoint);
    worldPt2 = m_renderWindow.displayToWorld(m_width, m_height,
                                             m_focusDisplayPoint);

    /// Computer mercator per pixel before changing the camera position
    oldMercPerPixel = (worldPt2[0] - worldPt1[0]) / m_width;

    this._zoomCameras(val);

    worldPt1 = m_renderWindow.displayToWorld(0, 0, m_focusDisplayPoint);
    worldPt2 = m_renderWindow.displayToWorld(m_width, m_height,
                   m_focusDisplayPoint);

    /// Computer mercator per pixel as of now
    newMercPerPixel = (worldPt2[0] - worldPt1[0]) / m_width;

    /// Compute meters per pixel here and based on that decide the
    /// zoom level
    evt = { type: geo.event.zoom,
            curr_zoom: computeZoomLevel(newMercPerPixel),
            last_zoom: computeZoomLevel(oldMercPerPixel) };
    $(m_this).trigger(evt);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets the lastMousePosition for this interactor
   *
   * @param newPosition optional
   * @param {Number} newPosition.x
   * @param {Number} newPosition.y
   * @returns {geo.mapInteractorStyle|Object}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.lastMousePosition = function(newPosition) {
    if(typeof newPosition !== 'undefined') {
      m_mouseLastPos = newPosition;
      return m_this;
    }
    return m_mouseLastPos;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets or gets leftMouseDown for this interactor
   *
   * @param {Boolean} newValue optional
   * @returns {geo.mapInteractorStyle|Boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.leftMouseDown = function(newValue) {
    if(typeof newValue !== 'undefined') {
      m_leftMouseButtonDown = newValue;
      return m_this;
    }
    return m_leftMouseButtonDown;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Sets the draw region coordinates for this interactor
   *
   * @param {Number} lat1
   * @param {Number} lon1
   * @param {Number} lat2
   * @param {Number} lon2
   * @returns {geo.mapInteractorStyle}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setDrawRegion = function(lat1, lon1, lat2, lon2) {
    // TODO
    // Use z-indexing or some other technique for the offsetting
    var evt, plane = geo.planeFeature(
      geo.latlng(lat1, lon1),
      geo.latlng(lat2, lon2),
      99
    );

    m_map.removeLayer(m_drawRegionLayer);

    m_drawRegionLayer = geo.featureLayer({
      "opacity": 0.5,
      "showAttribution": 1
    }, plane);

    m_map.addLayer(m_drawRegionLayer);

    /// TODO pass bounding box
    evt = jQuery.Event(geo.event.updateDrawRegionEvent);
    $(m_this).trigger(geo.command.updateDrawRegionEvent, evt);

    return m_this;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Gets the draw region coordinates for this interactor
   *
   * @returns {Array} [lat1, lon1, lat2, lon2]
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getDrawRegion = function() {
    return m_drawRegionLayer.features()[0].getCoords();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute current mouse position
   *
   * @protected
   */
  ////////////////////////////////////////////////////////////////////////////
  this._computeCurrentMousePos = function(event) {
    /// Update render params
    m_this.updateRenderParams();

    m_outsideCanvas = false;
    m_coords = m_this.viewer().relMouseCoords(event);
    m_currentMousePos = {
      x: 0,
      y: 0
    };
    if ((m_coords.x < 0) || (m_coords.x > m_width)) { // off-by-one error
      m_currentMousePos.x = 0;
      m_outsideCanvas = true;
    } else {
      m_currentMousePos.x = m_coords.x;
    }
    if ((m_coords.y < 0) || (m_coords.y > m_height)) { // off-by-one error
      m_currentMousePos.y = 0;
      m_outsideCanvas = true;
    } else {
      m_currentMousePos.y = m_coords.y;
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute current mouse position
   *
   * @protected
   */
  ////////////////////////////////////////////////////////////////////////////
  this.updateRenderParams = function() {
    m_renderWindow = m_this.viewer().renderWindow();
    m_width = m_renderWindow.windowSize()[0];
    m_height = m_renderWindow.windowSize()[1];
    m_renderer = m_this.viewer().renderWindow().activeRenderer();
    m_camera = m_renderer.camera();
    m_focusDisplayPoint = m_renderWindow.focusDisplayPoint();
    m_updateRenderParamsTime.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute zoom level
   *
   * @param deltaMerc mercator/per pixel
   * @returns {Number} zoom level
   *
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  function computeZoomLevel(deltaMerc) {
    var i, metersPerPixel, metersPerPixelFull = 156412;
    metersPerPixel = geo.mercator.deg2rad(Math.abs(deltaMerc)) *
                     geo.mercator.r_major;
    for (i = 4; i < 20; ++i) {
      if (metersPerPixel > (metersPerPixelFull / Math.pow(2, i))) {
        return (i - 1);
      }
    }
  };

  return this;
};

inherit(ggl.mapInteractorStyle, vgl.interactorStyle);
<|endoftext|>


<commit_before>(function(w, d) {
  var raf = w.requestAnimationFrame || w.setImmediate || function(c) { return setTimeout(c, 0); };

  function initEl(el) {
    if (el.hasOwnProperty('data-simple-scrollbar')) return;
    Object.defineProperty(el, 'data-simple-scrollbar', new SimpleScrollbar(el));
  }

  // Mouse drag handler
  function dragDealer(el, context) {
    var lastPageY;

    el.addEventListener('mousedown', function(e) {
      lastPageY = e.pageY;
      el.classList.add('ss-grabbed');
      d.body.classList.add('ss-grabbed');

      d.addEventListener('mousemove', drag);
      d.addEventListener('mouseup', stop);

      return false;
    });

    function drag(e) {
      var delta = e.pageY - lastPageY;
      lastPageY = e.pageY;

      raf(function() {
        context.el.scrollTop += delta / context.scrollRatio;
      });
    }

    function stop() {
      el.classList.remove('ss-grabbed');
      d.body.classList.remove('ss-grabbed');
      d.removeEventListener('mousemove', drag);
      d.removeEventListener('mouseup', stop);
    }
  }

  // Constructor
  function ss(el) {
    this.target = el;
    this.bar = '
';

    this.wrapper = d.createElement('div');
    this.wrapper.setAttribute('class', 'ss-wrapper');

    this.el = d.createElement('div');
    this.el.setAttribute('class', 'ss-content');

    this.wrapper.appendChild(this.el);

    while (this.target.firstChild) {
      this.el.appendChild(this.target.firstChild);
    }
    this.target.appendChild(this.wrapper);

    this.target.insertAdjacentHTML('beforeend', this.bar);
    this.bar = this.target.lastChild;

    dragDealer(this.bar, this);
    this.moveBar();

    this.el.addEventListener('scroll', this.moveBar.bind(this));
    this.el.addEventListener('mouseenter', this.moveBar.bind(this));

    this.target.classList.add('ss-container');
  }

  ss.prototype = {
    moveBar: function(e) {
      var totalHeight = this.el.scrollHeight,
          ownHeight = this.el.clientHeight,
          _this = this;

      this.scrollRatio = ownHeight / totalHeight;

      raf(function() {
        // Hide scrollbar if no scrolling is possible
        if(_this.scrollRatio === 1) {
          _this.bar.classList.add('ss-hidden')
        } else {
          _this.bar.classList.remove('ss-hidden')
          _this.bar.style.cssText = 'height:' + (_this.scrollRatio) * 100 + '%; top:' + (_this.el.scrollTop / totalHeight ) * 100 + '%;right:-' + (_this.target.clientWidth - _this.bar.clientWidth) + 'px;';
        }
      });
    }
  }

  function initAll() {
    var nodes = d.querySelectorAll('*[ss-container]');

    for (var i = 0; i < nodes.length; i++) {
      initEl(nodes[i]);
    }
  }

  d.addEventListener('DOMContentLoaded', initAll);
  ss.initEl = initEl;
  ss.initAll = initAll;

  w.SimpleScrollbar = ss;
})(window, document);
<commit_msg>Fixes #4<commit_after>(function(w, d) {
  var raf = w.requestAnimationFrame || w.setImmediate || function(c) { return setTimeout(c, 0); };

  function initEl(el) {
    if (el.hasOwnProperty('data-simple-scrollbar')) return;
    Object.defineProperty(el, 'data-simple-scrollbar', new SimpleScrollbar(el));
  }

  // Mouse drag handler
  function dragDealer(el, context) {
    var lastPageY;

    el.addEventListener('mousedown', function(e) {
      lastPageY = e.pageY;
      el.classList.add('ss-grabbed');
      d.body.classList.add('ss-grabbed');

      d.addEventListener('mousemove', drag);
      d.addEventListener('mouseup', stop);

      return false;
    });

    function drag(e) {
      var delta = e.pageY - lastPageY;
      lastPageY = e.pageY;

      raf(function() {
        context.el.scrollTop += delta / context.scrollRatio;
      });
    }

    function stop() {
      el.classList.remove('ss-grabbed');
      d.body.classList.remove('ss-grabbed');
      d.removeEventListener('mousemove', drag);
      d.removeEventListener('mouseup', stop);
    }
  }

  // Constructor
  function ss(el) {
    this.target = el;

    this.bar = '
';

    this.wrapper = d.createElement('div');
    this.wrapper.setAttribute('class', 'ss-wrapper');

    this.el = d.createElement('div');
    this.el.setAttribute('class', 'ss-content');

    this.wrapper.appendChild(this.el);

    while (this.target.firstChild) {
      this.el.appendChild(this.target.firstChild);
    }
    this.target.appendChild(this.wrapper);

    this.target.insertAdjacentHTML('beforeend', this.bar);
    this.bar = this.target.lastChild;

    dragDealer(this.bar, this);
    this.moveBar();

    this.el.addEventListener('scroll', this.moveBar.bind(this));
    this.el.addEventListener('mouseenter', this.moveBar.bind(this));

    this.target.classList.add('ss-container');

    var css = window.getComputedStyle(el);
  	if (css['height'] === '0px' && css['max-height'] !== '0px') {
    	el.style.height = css['max-height'];
    }
  }

  ss.prototype = {
    moveBar: function(e) {
      var totalHeight = this.el.scrollHeight,
          ownHeight = this.el.clientHeight,
          _this = this;

      this.scrollRatio = ownHeight / totalHeight;

      raf(function() {
        // Hide scrollbar if no scrolling is possible
        if(_this.scrollRatio === 1) {
          _this.bar.classList.add('ss-hidden')
        } else {
          _this.bar.classList.remove('ss-hidden')
          _this.bar.style.cssText = 'height:' + (_this.scrollRatio) * 100 + '%; top:' + (_this.el.scrollTop / totalHeight ) * 100 + '%;right:-' + (_this.target.clientWidth - _this.bar.clientWidth) + 'px;';
        }
      });
    }
  }

  function initAll() {
    var nodes = d.querySelectorAll('*[ss-container]');

    for (var i = 0; i < nodes.length; i++) {
      initEl(nodes[i]);
    }
  }

  d.addEventListener('DOMContentLoaded', initAll);
  ss.initEl = initEl;
  ss.initAll = initAll;

  w.SimpleScrollbar = ss;
})(window, document);
<|endoftext|>


<commit_before>angular.module('updateme')
.factory('QuickToast', function($mdToast) {
  return function(text, {
    callback = null,
    action = 'OK',
    hideDelay = 5000,
    highlightAction = false,
    position = 'top right'
  } = {}) {
    $mdToast.show(
      $mdToast.simple()
        .textContent(text)
        .action(action)
        .highlightAction(highlightAction)
        .position(position)
        .hideDelay(hideDelay)
    ).then(callback);
  };
});
<commit_msg>handle action outside QuickToast<commit_after>angular.module('updateme')
.factory('QuickToast', function($mdToast) {
  return function(text, {
    action = 'OK',
    hideDelay = 5000,
    highlightAction = false,
    position = 'top right'
  } = {}) {
    return $mdToast.show(
      $mdToast.simple()
        .textContent(text)
        .action(action)
        .highlightAction(highlightAction)
        .position(position)
        .hideDelay(hideDelay)
    );
  };
});
<|endoftext|>


<commit_before>import React from 'react';
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Permalink from './permalink';
import { shortNumberFormat } from '../utils/numbers';

const Hashtag = ({ hashtag }) => (

      <Permalink href={hashtag.get('url')} to={`/timelines/tag/${hashtag.get('name')}`}>
        #{hashtag.get('name')}
      </Permalink>

      <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']), count: {shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']) + hashtag.getIn(['history', 1, 'accounts']))} }} />

      {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']) + hashtag.getIn(['history', 1, 'uses']))}

      <Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
        <SparklinesCurve style={{ fill: 'none' }} />
      </Sparklines>

);

Hashtag.propTypes = {
  hashtag: ImmutablePropTypes.map.isRequired,
};

export default Hashtag;
<commit_msg>Fix wrong pluralization in hashtag component in web UI (#11754)<commit_after>import React from 'react';
import { Sparklines, SparklinesCurve } from 'react-sparklines';
import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Permalink from './permalink';
import { shortNumberFormat } from '../utils/numbers';

const Hashtag = ({ hashtag }) => (

      <Permalink href={hashtag.get('url')} to={`/timelines/tag/${hashtag.get('name')}`}>
        #{hashtag.get('name')}
      </Permalink>

      <FormattedMessage id='trends.count_by_accounts' defaultMessage='{count} {rawCount, plural, one {person} other {people}} talking' values={{ rawCount: hashtag.getIn(['history', 0, 'accounts']) + hashtag.getIn(['history', 1, 'accounts']), count: {shortNumberFormat(hashtag.getIn(['history', 0, 'accounts']) + hashtag.getIn(['history', 1, 'accounts']))} }} />

      {shortNumberFormat(hashtag.getIn(['history', 0, 'uses']) + hashtag.getIn(['history', 1, 'uses']))}

      <Sparklines width={50} height={28} data={hashtag.get('history').reverse().map(day => day.get('uses')).toArray()}>
        <SparklinesCurve style={{ fill: 'none' }} />
      </Sparklines>

);

Hashtag.propTypes = {
  hashtag: ImmutablePropTypes.map.isRequired,
};

export default Hashtag;
<|endoftext|>


<commit_before>"use strict";

angular.module('arethusa.sg').directive('sgAncestors', [
  'sg',
  function(sg) {
    return {
      restrict: 'A',
      scope: {
        obj: '=sgAncestors'
      },
      link: function(scope, element, attrs) {
        scope.requestGrammar = function(el) {
          sg.readerRequested = el;
        };

        scope.$watchCollection('obj.ancestors', function(newVal, oldVal) {
          scope.hierarchy = scope.obj.definingAttrs.concat(newVal);
        });
      },
      templateUrl: './templates/arethusa.sg/ancestors.html'
    };
  }
]);
<commit_msg>Only request grammar when it's even possible<commit_after>"use strict";

angular.module('arethusa.sg').directive('sgAncestors', [
  'sg',
  function(sg) {
    return {
      restrict: 'A',
      scope: {
        obj: '=sgAncestors'
      },
      link: function(scope, element, attrs) {
        scope.requestGrammar = function(el) {
          if (el.sections) {
            sg.readerRequested = el;
          }
        };

        scope.$watchCollection('obj.ancestors', function(newVal, oldVal) {
          scope.hierarchy = scope.obj.definingAttrs.concat(newVal);
        });
      },
      templateUrl: './templates/arethusa.sg/ancestors.html'
    };
  }
]);
<|endoftext|>


<commit_before>import * as d3 from 'd3';
import {crossfilter, units, legend, lineChart, geoChoroplethChart, bubbleChart, renderAll, redrawAll, filterAll, pieChart, barChart, dataCount, dataTable, pluck, round} from 'dc';
import * as colorbrewer from "colorbrewer";
import reductio from 'reductio';
//we can call export at the top of the class declaration
export default class NOAADashDC {

  constructor(el, props = {}) {
    //we initiate charts in constructor
    this.myCharts = NOAADashDC.initCharts();
  }

  render() {
    //de-structure myCharts object
    //const {heightChart, periodChart} = this.myCharts;
    const {heightChart, periodChart, moveChart} = this.myCharts;

    d3.csv('data/fullyear_wv_dec_cc.csv', (error, data) => {
      //format the data
      const buoyData = this.formatData(data);
      const wavesx = crossfilter(buoyData);
      // build the x dimensions
      const xDims = this.buildXDimensions(wavesx);
      //destructure the xDims object
      const {heightDim, periodDim, monthDim} = xDims;
      //build the Y groups
      const yGroups = this.buildYGroups(wavesx, xDims);
      //de-structure they yGroups object
      const {heightGroup, periodGroup, waveMoveHeightGroup, myMonthGroup} = yGroups;
      //call number format
      const numberFormat =  this.numberFormat();
      //dc.js Charts chained configuration

      this.buildMoveChart(moveChart, data, monthDim, myMonthGroup);

      heightChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(heightDim)
        .group(heightGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(65)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 7]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter((filters) => {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "met -> " + numberFormat(filter[1]) + "met";
          return s;
        });

      // Customize axis
      heightChart.xAxis().tickFormat((v) => {
        return v + "met";
      });
      heightChart.yAxis().ticks(5);

      //dc.barChart("#period-chart")
      periodChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(periodDim)
        .group(periodGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(45)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 30]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter(function (filters) {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "sec -> " + numberFormat(filter[1]) + "sec";
          return s;
        });

      // Customize axis
      periodChart.xAxis().tickFormat(
        function (v) { return v + "sec"; });
      periodChart.yAxis().ticks(5);

      //draw the viz!
      renderAll();

    });
  }

  buildMoveChart(moveChart, data, monthDim, myMonthGroup){

    moveChart /* dc.lineChart('#monthly-move-chart', 'chartGroup') */
      .renderArea(false)
      .width(990)
      .height(200)
      .transitionDuration(1000)
      .margins({top: 30, right: 50, bottom: 25, left: 40})
      .dimension(monthDim)
      .mouseZoomable(true)
    // Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
    //   .rangeChart(volumeChart)
    //.x(d3.time.scale().domain([new Date(2004, 0, 1), new Date(2012, 11, 31)]))
      .x(d3.time.scale().domain(d3.extent(data, (d) => { return d.dd; })))  //use extent to auto scale the axis
      .round(d3.time.month.round)
      .xUnits(d3.time.months)
      .elasticY(true)
      .renderHorizontalGridLines(true)
    //##### Legend

    // Position the legend relative to the chart origin and specify items' height and separation.
      .legend(legend().x(800).y(10).itemHeight(13).gap(5))
      .brushOn(false)
    // Add the base layer of the stack with group. The second parameter specifies a series name for use in the
    // legend.
    // The `.valueAccessor` will be used for the base layer
      .group(myMonthGroup, 'Monthly Height Average')
      .valueAccessor((d) =>  {
        return d.value.avg;
      })
    // Stack additional layers with `.stack`. The first paramenter is a new group.
    // The second parameter is the series name. The third is a value accessor.
      .stack(myMonthGroup, 'Monthly Height Max', (d) => {
       //console.log("val " + d.value);
       return d.value.max;
       })
       // Title can be called by any stack layer.
      /* .title(function (d) {
       var value = d.value.avg ? d.value.avg : d.value;
       if (isNaN(value)) {
       value = 0;
       }
       return dateFormat(d.key) + '\n' + numberFormat(value);
       });*/

  }

  static initCharts() {
    const heightChart = barChart('#chart-height');
    const periodChart = barChart('#chart-period');
    const moveChart = lineChart('#chart-month-move');

    //const myCharts = {heightChart, periodChart}
    const myCharts = {heightChart, periodChart, moveChart}

    return myCharts;
  }

  resetChart(chartName) {

    let {heightChart, periodChart} = this.myCharts;

    switch (chartName) {
      case "height-chart":
        heightChart.filterAll();
        break;
      case "period-chart":
        heightChart.filterAll();
        break;
      default:
        //Statements executed when none of the values match the value of the expression
        break;
    }

    redrawAll();
  }

  formatData(data){

    const buoyData = data;

    const dateFormat = d3.time.format("%Y-%m-%d %H:%M:%S");

    buoyData.forEach(d => {
      d.dd = dateFormat.parse(d.origintime);
      d.day = d3.time.day(d.dd); // pre-calculate day for better performance
      d.week = d3.time.week(d.dd);
      d.month = d3.time.month(d.dd);
      d.year = d3.time.year(d.dd);
      d.wvdp   = d3.round(+d.wvdp,1);
      //d.wvht = d3.round((+d.wvht * 3.2),1); //convert to feet
      d.wvht = d3.round((+d.wvht * 3.2)); //convert to feet
      d.wndir = +d.wndir;
    });

    return buoyData;
  }

  numberFormat(){

    return d3.format(".2f");
  }

  buildXDimensions(xwaves){
    // create dimensions (x-axis values)

    const heightDim  = xwaves.dimension(pluck("wvht"));
    const periodDim  = xwaves.dimension(pluck("wvdp"));
    const monthDim  = xwaves.dimension(pluck("month"));

    const xDims = { heightDim, periodDim, monthDim };
    return xDims;

  }

  buildYGroups(wavesx, xDims){

    const {heightDim, periodDim, monthDim} = xDims;

    // create groups (y-axis values)
    const heightGroup = heightDim.group();
    const periodGroup = periodDim.group();
    let myMonthGroup = monthDim.group();
    let reducer = reductio().count(true).sum('wvht').avg(true).min('wvht').max(true).median(true);
    reducer(myMonthGroup);
    console.log(myMonthGroup.all());
    const waveMoveHeightGroup = monthDim.group().reduceSum((d) => {
      //console.log(d.wvht);
      //console.log("hello");
      return d.wvht;
    });
    //map reduce functions
    const yGroups = {heightGroup, periodGroup, waveMoveHeightGroup, myMonthGroup};
    return yGroups;

  }

  //end of class
}
<commit_msg>line charts with min max avg<commit_after>import * as d3 from 'd3';
import {crossfilter, units, legend, lineChart, geoChoroplethChart, bubbleChart, renderAll, redrawAll, filterAll, pieChart, barChart, dataCount, dataTable, pluck, round} from 'dc';
import * as colorbrewer from "colorbrewer";
import reductio from 'reductio';
//we can call export at the top of the class declaration
export default class NOAADashDC {

  constructor(el, props = {}) {
    //we initiate charts in constructor
    this.myCharts = NOAADashDC.initCharts();
  }

  render() {
    //de-structure myCharts object
    //const {heightChart, periodChart} = this.myCharts;
    const {heightChart, periodChart, moveChart} = this.myCharts;

    d3.csv('data/fullyear_wv_dec_cc.csv', (error, data) => {
      //format the data
      const buoyData = this.formatData(data);
      const wavesx = crossfilter(buoyData);
      // build the x dimensions
      const xDims = this.buildXDimensions(wavesx);
      //destructure the xDims object
      const {heightDim, periodDim, monthDim} = xDims;
      //build the Y groups
      const yGroups = this.buildYGroups(wavesx, xDims);
      //de-structure they yGroups object
      const {heightGroup, periodGroup, waveMoveHeightGroup, myMonthGroup} = yGroups;
      //call number format
      const numberFormat =  this.numberFormat();
      //dc.js Charts chained configuration

      this.buildMoveChart(moveChart, data, monthDim, myMonthGroup);

      heightChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(heightDim)
        .group(heightGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(65)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 7]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter((filters) => {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "met -> " + numberFormat(filter[1]) + "met";
          return s;
        });

      // Customize axis
      heightChart.xAxis().tickFormat((v) => {
        return v + "met";
      });
      heightChart.yAxis().ticks(5);

      //dc.barChart("#period-chart")
      periodChart
        .width(300)
        .height(180)
        .margins({top: 10, right: 50, bottom: 30, left: 40})
        .dimension(periodDim)
        .group(periodGroup)
        .elasticY(true)
      // (optional) whether bar should be center to its x value. Not needed for ordinal chart, :default=false
        .centerBar(true)
      // (optional) set gap between bars manually in px, :default=2
        .gap(45)
      // (optional) set filter brush rounding
        .round(round.floor)
        .x(d3.scale.linear().domain([0, 30]))
        .renderHorizontalGridLines(true)
      // customize the filter displayed in the control span
        .filterPrinter(function (filters) {
          var filter = filters[0], s = "";
          s += numberFormat(filter[0]) + "sec -> " + numberFormat(filter[1]) + "sec";
          return s;
        });

      // Customize axis
      periodChart.xAxis().tickFormat(
        function (v) { return v + "sec"; });
      periodChart.yAxis().ticks(5);

      //draw the viz!
      renderAll();

    });
  }

  buildMoveChart(moveChart, data, monthDim, myMonthGroup){

    moveChart /* dc.lineChart('#monthly-move-chart', 'chartGroup') */
      .renderArea(true)
      .width(990)
      .height(200)
      .transitionDuration(1000)
      .margins({top: 30, right: 50, bottom: 25, left: 40})
      .dimension(monthDim)
      .mouseZoomable(true)
    // Specify a "range chart" to link its brush extent with the zoom of the current "focus chart".
    //   .rangeChart(volumeChart)
    //.x(d3.time.scale().domain([new Date(2004, 0, 1), new Date(2012, 11, 31)]))
      .x(d3.time.scale().domain(d3.extent(data, (d) => { return d.dd; })))  //use extent to auto scale the axis
      .round(d3.time.month.round)
      .xUnits(d3.time.months)
      .elasticY(true)
      .renderHorizontalGridLines(true)
    //##### Legend

    // Position the legend relative to the chart origin and specify items' height and separation.
      .legend(legend().x(800).y(10).itemHeight(13).gap(5))
      .brushOn(false)
    // Add the base layer of the stack with group. The second parameter specifies a series name for use in the
    // legend.
    // The `.valueAccessor` will be used for the base layer
      .group(myMonthGroup, 'Monthly Height Min')
      .valueAccessor((d) =>  {
        return d.value.min;
      })
    // Stack additional layers with `.stack`. The first paramenter is a new group.
    // The second parameter is the series name. The third is a value accessor.
      .stack(myMonthGroup, 'Monthly Height Average', (d) => {
       //console.log("val " + d.value);
        return d.value.avg;
       })
       // Title can be called by any stack layer.
      /* .title(function (d) {
       var value = d.value.avg ? d.value.avg : d.value;
       if (isNaN(value)) {
       value = 0;
       }
       return dateFormat(d.key) + '\n' + numberFormat(value);
       });*/
      .stack(myMonthGroup, "Monthly Height Max", (d) => {
        return d.value.max;
      })

  }

  static initCharts() {
    const heightChart = barChart('#chart-height');
    const periodChart = barChart('#chart-period');
    const moveChart = lineChart('#chart-month-move');

    //const myCharts = {heightChart, periodChart}
    const myCharts = {heightChart, periodChart, moveChart}

    return myCharts;
  }

  resetChart(chartName) {

    let {heightChart, periodChart} = this.myCharts;

    switch (chartName) {
      case "height-chart":
        heightChart.filterAll();
        break;
      case "period-chart":
        heightChart.filterAll();
        break;
      default:
        //Statements executed when none of the values match the value of the expression
        break;
    }

    redrawAll();
  }

  formatData(data){

    const buoyData = data;

    const dateFormat = d3.time.format("%Y-%m-%d %H:%M:%S");

    buoyData.forEach(d => {
      d.dd = dateFormat.parse(d.origintime);
      d.day = d3.time.day(d.dd); // pre-calculate day for better performance
      d.week = d3.time.week(d.dd);
      d.month = d3.time.month(d.dd);
      d.year = d3.time.year(d.dd);
      d.wvdp   = d3.round(+d.wvdp,1);
      //d.wvht = d3.round((+d.wvht * 3.2),1); //convert to feet
      d.wvht = d3.round((+d.wvht * 3.2)); //convert to feet
      d.wndir = +d.wndir;
    });

    return buoyData;
  }

  numberFormat(){

    return d3.format(".2f");
  }

  buildXDimensions(xwaves){
    // create dimensions (x-axis values)

    const heightDim  = xwaves.dimension(pluck("wvht"));
    const periodDim  = xwaves.dimension(pluck("wvdp"));
    const monthDim  = xwaves.dimension(pluck("month"));

    const xDims = { heightDim, periodDim, monthDim };
    return xDims;

  }

  buildYGroups(wavesx, xDims){

    const {heightDim, periodDim, monthDim} = xDims;

    // create groups (y-axis values)
    const heightGroup = heightDim.group();
    const periodGroup = periodDim.group();
    let myMonthGroup = monthDim.group();
    let reducer = reductio().count(true).sum('wvht').avg(true).min('wvht').max(true).median(true);
    reducer(myMonthGroup);
    //console.log(myMonthGroup.all());
    const waveMoveHeightGroup = monthDim.group().reduceSum((d) => {
      //console.log(d.wvht);
      //console.log("hello");
      return d.wvht;
    });
    //map reduce functions
    const yGroups = {heightGroup, periodGroup, waveMoveHeightGroup, myMonthGroup};
    return yGroups;

  }

  //end of class
}
<|endoftext|>


<commit_before>//@flow
import React from 'react';
import {
  injectStripe,
  PaymentRequestButtonElement,
  Elements,
  StripeProvider
} from 'react-stripe-elements';
import config from 'app/config';
import StripeCheckout from 'react-stripe-checkout';
import Button from 'app/components/Button';
import styles from './StripeElement.css';
import logoImage from 'app/assets/kule.png';
import type { EventRegistrationChargeStatus, User, Event } from 'app/models';

import { paymentPending } from '../utils';

type Props = {
  event: Event,
  currentUser: User,
  onToken: (token: string) => Promise<*>,
  chargeStatus: EventRegistrationChargeStatus
};

type FormProps = Props & { stripe: { paymentRequest: Object => Object } };

type State = {
  paymentRequest: Object,
  canMakePayment: boolean
};

const paymentButtonWidth = 130;

class PaymentRequestForm extends React.Component<FormProps, State> {
  constructor(props) {
    super(props);

    const { event, onToken } = props;

    const paymentRequest = props.stripe.paymentRequest({
      currency: 'nok',
      total: {
        label: event.title,
        amount: event.price
      },
      requestPayerName: true,
      requestPayerEmail: true,
      requestPayerPhone: true,
      country: 'NO'
    });

    paymentRequest.on('token', async ({ complete, token, ...data }) => {
      await onToken(token);
      complete('success');
    });

    paymentRequest.canMakePayment().then(result => {
      this.setState({ canMakePayment: !!result });
    });

    this.state = {
      canMakePayment: false,
      paymentRequest
    };
  }

  render() {
    const { chargeStatus, event, onToken, currentUser } = this.props;
    const showOverlay = chargeStatus === paymentPending;
    return (

        {showOverlay && (

        )}
        {this.state.canMakePayment ? (

            <PaymentRequestButtonElement
              paymentRequest={this.state.paymentRequest}
              className="PaymentRequestButton"
              style={{
                paymentRequestButton: {
                  height: '41px'
                }
              }}
            />

        ) : (
          <StripeCheckout
            name="Abakus Linjeforening"
            description={event.title}
            image={logoImage}
            currency="NOK"
            allowRememberMe
            locale="no"
            token={onToken}
            stripeKey={config.stripeKey}
            amount={event.price}
            email={currentUser.email}
          >
            <Button style={{ width: 130 }}>Betal nå</Button>
          </StripeCheckout>
        )}

    );
  }
}
const InjectedForm = injectStripe(PaymentRequestForm);

// TODO Move this to a "global" thing
const WithProvider = (props: Props) => (
  <StripeProvider apiKey={config.stripeKey}>
    <Elements>
      <InjectedForm {...props} />
    </Elements>
  </StripeProvider>
);
export default WithProvider;
<commit_msg>Add delay before showing payment button<commit_after>//@flow
import React from 'react';
import {
  injectStripe,
  PaymentRequestButtonElement,
  Elements,
  StripeProvider
} from 'react-stripe-elements';
import config from 'app/config';
import StripeCheckout from 'react-stripe-checkout';
import Button from 'app/components/Button';
import styles from './StripeElement.css';
import logoImage from 'app/assets/kule.png';
import type { EventRegistrationChargeStatus, User, Event } from 'app/models';

import { paymentPending } from '../utils';

type Props = {
  event: Event,
  currentUser: User,
  onToken: (token: string) => Promise<*>,
  chargeStatus: EventRegistrationChargeStatus
};

type FormProps = Props & { stripe: { paymentRequest: Object => Object } };

type State = {
  paymentRequest: Object,
  canMakePayment?: boolean
};

const paymentButtonWidth = 130;

class PaymentRequestForm extends React.Component<FormProps, State> {
  constructor(props) {
    super(props);

    const { event, onToken } = props;

    const paymentRequest = props.stripe.paymentRequest({
      currency: 'nok',
      total: {
        label: event.title,
        amount: event.price
      },
      requestPayerName: true,
      requestPayerEmail: true,
      requestPayerPhone: true,
      country: 'NO'
    });

    paymentRequest.on('token', async ({ complete, token, ...data }) => {
      await onToken(token);
      complete('success');
    });

    paymentRequest.canMakePayment().then(result => {
      this.setState({ canMakePayment: !!result });
    });

    this.state = {
      canMakePayment: undefined,
      paymentRequest
    };
  }

  render() {
    const { chargeStatus, event, onToken, currentUser } = this.props;
    const showOverlay = chargeStatus === paymentPending;
    return (

        {showOverlay && (

        )}
        {this.state.canMakePayment && (

            <PaymentRequestButtonElement
              paymentRequest={this.state.paymentRequest}
              className="PaymentRequestButton"
              style={{
                paymentRequestButton: {
                  height: '41px'
                }
              }}
            />

        )}

        {this.state.canMakePayment === false && (
          <StripeCheckout
            name="Abakus Linjeforening"
            description={event.title}
            image={logoImage}
            currency="NOK"
            allowRememberMe
            locale="no"
            token={onToken}
            stripeKey={config.stripeKey}
            amount={event.price}
            email={currentUser.email}
          >
            <Button style={{ width: 130 }}>Betal nå</Button>
          </StripeCheckout>
        )}

    );
  }
}
const InjectedForm = injectStripe(PaymentRequestForm);

// TODO Move this to a "global" thing
const WithProvider = (props: Props) => (
  <StripeProvider apiKey={config.stripeKey}>
    <Elements>
      <InjectedForm {...props} />
    </Elements>
  </StripeProvider>
);
export default WithProvider;
<|endoftext|>


<commit_before>'use strict';

angular.module('confRegistrationWebApp')
  .controller('eventRegistrationsCtrl', function ($rootScope, $scope, $modal, $http, apiUrl, uuid, registrations, conference, RegViewCache, RegistrationsViewService, U, PaymentsViewService, permissions) {
    $rootScope.globalPage = {
      type: 'admin',
      mainClass: 'registrations',
      bodyClass: '',
      title: conference.name,
      confId: conference.id,
      footer: true
    };

    $scope.conference = conference;
    $scope.blocks = [];
    $scope.reversesort = false;
    $scope.showAllViewId = 'all';
    $scope.defaultViewId = 'default';
    $scope.activeRegViewId = $scope.defaultViewId;
    $scope.savedState = '';
    $scope.showRegistrationsCompleted = true;
    $scope.columnsDropdownToggle = false;
    $scope.registrations = registrations;
    $scope.registrants = _.flatten(registrations, 'registrants');

    // collect all 'Content' blocks from the conferences' pages
    angular.forEach(conference.registrationPages, function (page) {
      angular.forEach(page.blocks, function (block) {
        if (block.type.indexOf('Content') === -1) {
          $scope.blocks.push(angular.copy(block));
        }
      });
    });

    // toggle (show/hide) the column and auto save the registration view
    $scope.toggleColumn = function (block) {
      $scope.blocks[block].visible = !$scope.blocks[block].visible;
      $scope.updateRegView();
    };

    // set the registration view as per the active registration view id
    $scope.setRegView = function () {
      $scope.savedState = '';
      if ($scope.activeRegViewId === '') {

      } else {
        var visibleBlocks = _.find($scope.registrationViewsDropdown, { 'id': $scope.activeRegViewId }).visibleBlockIds;
        angular.forEach($scope.blocks, function (block) {
          if (visibleBlocks.indexOf(block.id) !== -1) {
            block.visible = true;
          } else if (block.type.indexOf('Content') === -1) {
            block.visible = false;
          }
        });
      }
    };

    // create a registration view
    $scope.createRegView = function () {
      $modal.open({
        templateUrl: 'views/modals/regViewCreate.html',
        controller: 'createEventCtrl',
        resolve: {
          defaultValue: function () {
            return '';
          }
        }
      }).result.then(function (viewName) {
        if (viewName !== '') {

          var regViewNames = _.pluck($scope.registrationViewsDropdown, 'name');
          if (regViewNames.indexOf(viewName) > -1) {
            var errorModalOptions = {
              templateUrl: 'views/modals/errorModal.html',
              controller: 'genericModal',
              resolve: {
                message: function () {
                  return 'View name "' + viewName + '" already exists. Please provide a different view name.';
                }
              }
            };
            $modal.open(errorModalOptions);

            return;
          }

          var newView = {
            id: uuid(),
            conferenceId: conference.id,
            name: viewName,
            visibleBlockIds: _.pluck(_.filter($scope.blocks, function (item) {
              return item.visible === true;
            }), 'id')
          };

          $http({method: 'POST',
            url: 'conferences/' + conference.id + '/registration-views',
            data: newView
          }).success(function () {
            $scope.registrationViews = $scope.registrationViews.concat(newView);
            $scope.registrationViewsDropdown = $scope.registrationViewsDropdown.concat(newView);
            $scope.activeRegViewId = newView.id;
          }).error(function () {
          });
        }
      });
    };

    // update a registration view
    $scope.updateRegView = function () {

      // don't update predefined view
      if ($scope.isPredefinedView($scope.activeRegViewId))
      {
        return;
      }

      var thisView = {
        id: $scope.activeRegViewId,
        conferenceId: conference.id,
        name: _.find($scope.registrationViewsDropdown, { 'id': $scope.activeRegViewId }).name,
        visibleBlockIds: _.pluck(_.filter($scope.blocks, function (item) {
          return item.visible === true;
        }), 'id')
      };

      $scope.savedState = 'Saving...';
      $http({method: 'PUT',
        url: 'registration-views/' + $scope.activeRegViewId,
        data: thisView
      }).success(function () {
        $scope.registrationViews = _.remove($scope.registrationViews, function (view) { return view.id !== $scope.activeRegViewId; });
        $scope.registrationViewsDropdown = _.remove($scope.registrationViewsDropdown, function (view) {
          return view.id !== $scope.activeRegViewId;
        });
        $scope.registrationViews = $scope.registrationViews.concat(thisView);
        $scope.registrationViewsDropdown = $scope.registrationViewsDropdown.concat(thisView);
        $scope.activeRegViewId = thisView.id;
        $scope.savedState = 'Saved';
        RegViewCache.update(conference.id, $scope.registrationViews);
      }).error(function () {
      });
    };

    // delete a registration view
    $scope.delRegView = function () {

      // don't delete predefined views
      if ($scope.isPredefinedView($scope.activeRegViewId))
      {
        return;
      }

      $http({method: 'DELETE',
        url: 'registration-views/' + $scope.activeRegViewId
      }).success(function () {
        $scope.registrationViews = _.remove($scope.registrationViews, function (view) { return view.id !== $scope.activeRegViewId; });
        $scope.registrationViewsDropdown = _.remove($scope.registrationViewsDropdown, function (view) {
          return view.id !== $scope.activeRegViewId;
        });

        $scope.activeRegViewId = $scope.defaultViewId;
        $scope.setRegView();
        RegViewCache.update(conference.id, $scope.registrationViews);
      }).error(function () {
      });
    };

    // get all the registration views for this conference
    RegViewCache.get(conference.id, function (data) {
      $scope.registrationViews = _.sortBy(data, 'name');

      var profileBlocks = function (blocks) {
        return _.filter(blocks, function (block) {
          var profileTypes = [ 'EMAIL', 'NAME' ];
          return profileTypes.indexOf(block.profileType) > -1;
        });
      };

      $scope.registrationViewsDropdown = [
        {
          id: $scope.defaultViewId,
          name: '-Name & Email-',
          visibleBlockIds: _.pluck(profileBlocks($scope.blocks), 'id')
        },
        {
          id: $scope.showAllViewId,
          name: '-Show All-',
          visibleBlockIds: _.pluck($scope.blocks, 'id')
        }
      ];

      $scope.registrationViewsDropdown = $scope.registrationViewsDropdown.concat($scope.registrationViews);
      $scope.setRegView();
    });

    $scope.findAnswer = function (registration, blockId) {
      return _.find(registration.answers, function (answer) {
        return angular.equals(answer.blockId, blockId);
      });
    };

    $scope.getSelectedCheckboxes = function (choices) {
      return _.keys(_.pick(choices, function (val) {
        return val === true;
      }));
    };

    $scope.answerSort = function (registration) {
      if (angular.isDefined($scope.order)) {
        if (angular.isDefined($scope.findAnswer(registration, $scope.order))) {
          if ($scope.findAnswer(registration, $scope.order).value) { //text field
            return $scope.findAnswer(registration, $scope.order).value;
          } else if ($scope.getSelectedCheckboxes($scope.findAnswer(registration, $scope.order).value).length > 0) {
            //mc
            return $scope.getSelectedCheckboxes($scope.findAnswer(registration, $scope.order).value).join(' ');
          } else if (typeof $scope.findAnswer(registration, $scope.order).value === 'object') { //name
            return _.values($scope.findAnswer(registration, $scope.order).value).join(' ');
          } else { //radio
            return $scope.findAnswer(registration, $scope.order).value;
          }
        }
      } else {
        return 0;
      }
    };

    $scope.setOrder = function (order) {
      if (order === $scope.order) {
        $scope.reversesort = !$scope.reversesort;
      } else {
        $scope.reversesort = false;
      }
      $scope.order = order;
    };

    $scope.viewPayments = function (registrationId) {
      var paymentModalOptions = {
        templateUrl: 'views/modals/paymentsModal.html',
        controller: 'paymentModal',
        backdrop: 'static',
        resolve: {
          registration: function () {
            return _.find(registrations, { 'id': registrationId });
          },
          conference: function () {
            return conference;
          }
        }
      };

      $modal.open(paymentModalOptions).result.then(function (updatedRegistration) {
        var localUpdatedRegistrationIndex = _.findIndex($scope.registrations, { 'id': updatedRegistration.id });
        $scope.registrations[localUpdatedRegistrationIndex] = updatedRegistration;
      });
    };

    $scope.isPredefinedView = function (regViewId) {
      var predefinedViews = [ $scope.showAllViewId, $scope.defaultViewId ];
      return predefinedViews.indexOf(regViewId) > -1;
    };

    // define payment categories
    $scope.paymentCategories = [
      {
        name: 'Show All',
        matches: function () {
          return true;
        }
      },
      {
        name: 'Full/Overpaid',
        matches: function (x, y) {
          return x >= y;
        }
      },
      {
        name: 'Partial',
        matches: function (x, y) {
          return x > 0 && x < y;
        }
      },
      {
        name: 'Full/Partial',
        matches: function (x) {
          return x > 0;
        }
      },
      {
        name: 'Not Paid',
        matches: function (x) {
          if (x === null) {
            return true;
          }

          return x <= 0;
        }
      },
      {
        name: 'Overpaid',
        matches: function (x, y) {
          return x > y;
        }
      }
    ];

    // set current to first in array
    $scope.currentPaymentCategory = _.first($scope.paymentCategories).name;

    // determine if registration payment status matches current payment category
    $scope.paymentStatus = function (registrant) {
      var registration = _.find(registrations, { 'id': registrant.registrationId });
      var paymentCategory = _.find($scope.paymentCategories, { 'name': $scope.currentPaymentCategory });
      return paymentCategory.matches(registration.totalPaid, registration.calculatedTotalDue);
    };

    $scope.completeStatus = function (registrant) {
      var registration = _.find(registrations, { 'id': registrant.registrationId });
      if ($scope.showRegistrationsCompleted) {
          return registration.completed;
      } else {
        return true;
      }
    };

    $scope.paidInFull = function (registrantId) {
      var registration = _.find(registrations, { 'id': registrantId });
      return registration.totalPaid >= registration.calculatedTotalDue;
    };

    var expandedRegistrations = [];
    $scope.expandRegistration = function (r) {
      if (_.contains(expandedRegistrations, r)) {
        _.remove(expandedRegistrations, function (i) { return i === r; });
      } else {
        expandedRegistrations.push(r);
      }
    };
    $scope.isExpanded = function (r) {
      return _.contains(expandedRegistrations, r);
    };

    $scope.editRegistrant = function (r) {
      var editRegistrationDialogOptions = {
        templateUrl: 'views/modals/editRegistration.html',
        controller: 'editRegistrationModalCtrl',
        resolve: {
          registrant: function () {
            return r;
          },
          conference: function () {
            return conference;
          }
        }
      };

      $modal.open(editRegistrationDialogOptions).result.then(function (result) {
        if (angular.isDefined(result)) {
          var index = _.findIndex($scope.registrants, { 'id': result.id });
          $scope.registrants[index] = result;
        }
      });
    };

    // Export conference registrations information to csv
    // showRegistrationsCompleted is now passed to this function. If checked only completed registrations will be exported.
    // If unchecked all registrations will be exported
    $scope.exportRegistrations = function () {
      var table = RegistrationsViewService.getTable(conference, registrations, $scope.showRegistrationsCompleted, _.find($scope.registrationViewsDropdown, { 'id': $scope.activeRegViewId }).visibleBlockIds);
      var csvContent = U.stringifyArray(table, ',');
      var url = apiUrl + 'services/download/registrations/' + conference.name + '-registrations.csv';
      U.submitForm(url, { name: csvContent });
    };

    // Export conference registration payments information to csv
    $scope.exportPayments = function () {
      var table = PaymentsViewService.getTable(conference, registrations);
      var csvContent = U.stringifyArray(table, ',');
      var url = apiUrl + 'services/download/payments/' + conference.name + '-payments.csv';
      U.submitForm(url, { name: csvContent });
    };

    $scope.eventHasCost = function () {
      return _.max(_.flatten(conference.registrantTypes, 'cost')) > 0;
    };

    $scope.registerUser = function () {
      var registrationModalOptions = {
        templateUrl: 'views/modals/manualRegistration.html',
        controller: 'registrationModal',
        resolve: {
          conference: function () {
            return conference;
          }
        }
      };
      $modal.open(registrationModalOptions);
    };

    $scope.allowDeleteRegistration = function () {
      return permissions.permissionInt > 1;
    };

    $scope.getRegistration = function(id){
      return _.find(registrations, { 'id': id });
    };

    $scope.getRegistrantType = function(id){
      return _.find(conference.registrantTypes, { 'id': id });
    };

    $scope.deleteRegistrant = function (registrant) {
      var modalInstance = $modal.open({
        templateUrl: 'views/modals/deleteRegistration.html',
        controller: 'deleteRegistrationCtrl'
      });

      modalInstance.result.then(function (doDelete) {
        if (doDelete) {
          var registration = _.find(registrations, { 'id': registrant.registrationId });
          var url = 'registrations/' + registration.id;

          if(registration.registrants.length > 1){
            //Delete Registrant
            url = 'registrants/' + registrant.id;
          }

          $http({
            method: 'DELETE',
            url: url
          }).success(function () {
            _.remove($scope.registrants, function (r) {
              return r.id === registrant.id;
            });
          });
        }
      });
    };
  });
<commit_msg>Export now defaults to the boxes checked  ( a workaround for when a pre-defined view is selected, but not saved)<commit_after>'use strict';

angular.module('confRegistrationWebApp')
  .controller('eventRegistrationsCtrl', function ($rootScope, $scope, $modal, $http, apiUrl, uuid, registrations, conference, RegViewCache, RegistrationsViewService, U, PaymentsViewService, permissions) {
    $rootScope.globalPage = {
      type: 'admin',
      mainClass: 'registrations',
      bodyClass: '',
      title: conference.name,
      confId: conference.id,
      footer: true
    };

    $scope.conference = conference;
    $scope.blocks = [];
    $scope.reversesort = false;
    $scope.showAllViewId = 'all';
    $scope.defaultViewId = 'default';
    $scope.activeRegViewId = $scope.defaultViewId;
    $scope.savedState = '';
    $scope.showRegistrationsCompleted = true;
    $scope.columnsDropdownToggle = false;
    $scope.registrations = registrations;
    $scope.registrants = _.flatten(registrations, 'registrants');

    // collect all 'Content' blocks from the conferences' pages
    angular.forEach(conference.registrationPages, function (page) {
      angular.forEach(page.blocks, function (block) {
        if (block.type.indexOf('Content') === -1) {
          $scope.blocks.push(angular.copy(block));
        }
      });
    });

    // toggle (show/hide) the column and auto save the registration view
    $scope.toggleColumn = function (block) {
      $scope.blocks[block].visible = !$scope.blocks[block].visible;
      $scope.updateRegView();
    };

    // set the registration view as per the active registration view id
    $scope.setRegView = function () {
      $scope.savedState = '';
      if ($scope.activeRegViewId === '') {

      } else {
        var visibleBlocks = _.find($scope.registrationViewsDropdown, { 'id': $scope.activeRegViewId }).visibleBlockIds;
        angular.forEach($scope.blocks, function (block) {
          if (visibleBlocks.indexOf(block.id) !== -1) {
            block.visible = true;
          } else if (block.type.indexOf('Content') === -1) {
            block.visible = false;
          }
        });
      }
    };

    // create a registration view
    $scope.createRegView = function () {
      $modal.open({
        templateUrl: 'views/modals/regViewCreate.html',
        controller: 'createEventCtrl',
        resolve: {
          defaultValue: function () {
            return '';
          }
        }
      }).result.then(function (viewName) {
        if (viewName !== '') {

          var regViewNames = _.pluck($scope.registrationViewsDropdown, 'name');
          if (regViewNames.indexOf(viewName) > -1) {
            var errorModalOptions = {
              templateUrl: 'views/modals/errorModal.html',
              controller: 'genericModal',
              resolve: {
                message: function () {
                  return 'View name "' + viewName + '" already exists. Please provide a different view name.';
                }
              }
            };
            $modal.open(errorModalOptions);

            return;
          }

          var newView = {
            id: uuid(),
            conferenceId: conference.id,
            name: viewName,
            visibleBlockIds: _.pluck(_.filter($scope.blocks, function (item) {
              return item.visible === true;
            }), 'id')
          };

          $http({method: 'POST',
            url: 'conferences/' + conference.id + '/registration-views',
            data: newView
          }).success(function () {
            $scope.registrationViews = $scope.registrationViews.concat(newView);
            $scope.registrationViewsDropdown = $scope.registrationViewsDropdown.concat(newView);
            $scope.activeRegViewId = newView.id;
          }).error(function () {
          });
        }
      });
    };

    // update a registration view
    $scope.updateRegView = function () {

      // don't update predefined view
      if ($scope.isPredefinedView($scope.activeRegViewId))
      {
        return;
      }

      var thisView = {
        id: $scope.activeRegViewId,
        conferenceId: conference.id,
        name: _.find($scope.registrationViewsDropdown, { 'id': $scope.activeRegViewId }).name,
        visibleBlockIds: _.pluck(_.filter($scope.blocks, function (item) {
          return item.visible === true;
        }), 'id')
      };

      $scope.savedState = 'Saving...';
      $http({method: 'PUT',
        url: 'registration-views/' + $scope.activeRegViewId,
        data: thisView
      }).success(function () {
        $scope.registrationViews = _.remove($scope.registrationViews, function (view) { return view.id !== $scope.activeRegViewId; });
        $scope.registrationViewsDropdown = _.remove($scope.registrationViewsDropdown, function (view) {
          return view.id !== $scope.activeRegViewId;
        });
        $scope.registrationViews = $scope.registrationViews.concat(thisView);
        $scope.registrationViewsDropdown = $scope.registrationViewsDropdown.concat(thisView);
        $scope.activeRegViewId = thisView.id;
        $scope.savedState = 'Saved';
        RegViewCache.update(conference.id, $scope.registrationViews);
      }).error(function () {
      });
    };

    // delete a registration view
    $scope.delRegView = function () {

      // don't delete predefined views
      if ($scope.isPredefinedView($scope.activeRegViewId))
      {
        return;
      }

      $http({method: 'DELETE',
        url: 'registration-views/' + $scope.activeRegViewId
      }).success(function () {
        $scope.registrationViews = _.remove($scope.registrationViews, function (view) { return view.id !== $scope.activeRegViewId; });
        $scope.registrationViewsDropdown = _.remove($scope.registrationViewsDropdown, function (view) {
          return view.id !== $scope.activeRegViewId;
        });

        $scope.activeRegViewId = $scope.defaultViewId;
        $scope.setRegView();
        RegViewCache.update(conference.id, $scope.registrationViews);
      }).error(function () {
      });
    };

    // get all the registration views for this conference
    RegViewCache.get(conference.id, function (data) {
      $scope.registrationViews = _.sortBy(data, 'name');

      var profileBlocks = function (blocks) {
        return _.filter(blocks, function (block) {
          var profileTypes = [ 'EMAIL', 'NAME' ];
          return profileTypes.indexOf(block.profileType) > -1;
        });
      };

      $scope.registrationViewsDropdown = [
        {
          id: $scope.defaultViewId,
          name: '-Name & Email-',
          visibleBlockIds: _.pluck(profileBlocks($scope.blocks), 'id')
        },
        {
          id: $scope.showAllViewId,
          name: '-Show All-',
          visibleBlockIds: _.pluck($scope.blocks, 'id')
        }
      ];

      $scope.registrationViewsDropdown = $scope.registrationViewsDropdown.concat($scope.registrationViews);
      $scope.setRegView();
    });

    $scope.findAnswer = function (registration, blockId) {
      return _.find(registration.answers, function (answer) {
        return angular.equals(answer.blockId, blockId);
      });
    };

    $scope.getSelectedCheckboxes = function (choices) {
      return _.keys(_.pick(choices, function (val) {
        return val === true;
      }));
    };

    $scope.answerSort = function (registration) {
      if (angular.isDefined($scope.order)) {
        if (angular.isDefined($scope.findAnswer(registration, $scope.order))) {
          if ($scope.findAnswer(registration, $scope.order).value) { //text field
            return $scope.findAnswer(registration, $scope.order).value;
          } else if ($scope.getSelectedCheckboxes($scope.findAnswer(registration, $scope.order).value).length > 0) {
            //mc
            return $scope.getSelectedCheckboxes($scope.findAnswer(registration, $scope.order).value).join(' ');
          } else if (typeof $scope.findAnswer(registration, $scope.order).value === 'object') { //name
            return _.values($scope.findAnswer(registration, $scope.order).value).join(' ');
          } else { //radio
            return $scope.findAnswer(registration, $scope.order).value;
          }
        }
      } else {
        return 0;
      }
    };

    $scope.setOrder = function (order) {
      if (order === $scope.order) {
        $scope.reversesort = !$scope.reversesort;
      } else {
        $scope.reversesort = false;
      }
      $scope.order = order;
    };

    $scope.viewPayments = function (registrationId) {
      var paymentModalOptions = {
        templateUrl: 'views/modals/paymentsModal.html',
        controller: 'paymentModal',
        backdrop: 'static',
        resolve: {
          registration: function () {
            return _.find(registrations, { 'id': registrationId });
          },
          conference: function () {
            return conference;
          }
        }
      };

      $modal.open(paymentModalOptions).result.then(function (updatedRegistration) {
        var localUpdatedRegistrationIndex = _.findIndex($scope.registrations, { 'id': updatedRegistration.id });
        $scope.registrations[localUpdatedRegistrationIndex] = updatedRegistration;
      });
    };

    $scope.isPredefinedView = function (regViewId) {
      var predefinedViews = [ $scope.showAllViewId, $scope.defaultViewId ];
      return predefinedViews.indexOf(regViewId) > -1;
    };

    // define payment categories
    $scope.paymentCategories = [
      {
        name: 'Show All',
        matches: function () {
          return true;
        }
      },
      {
        name: 'Full/Overpaid',
        matches: function (x, y) {
          return x >= y;
        }
      },
      {
        name: 'Partial',
        matches: function (x, y) {
          return x > 0 && x < y;
        }
      },
      {
        name: 'Full/Partial',
        matches: function (x) {
          return x > 0;
        }
      },
      {
        name: 'Not Paid',
        matches: function (x) {
          if (x === null) {
            return true;
          }

          return x <= 0;
        }
      },
      {
        name: 'Overpaid',
        matches: function (x, y) {
          return x > y;
        }
      }
    ];

    // set current to first in array
    $scope.currentPaymentCategory = _.first($scope.paymentCategories).name;

    // determine if registration payment status matches current payment category
    $scope.paymentStatus = function (registrant) {
      var registration = _.find(registrations, { 'id': registrant.registrationId });
      var paymentCategory = _.find($scope.paymentCategories, { 'name': $scope.currentPaymentCategory });
      return paymentCategory.matches(registration.totalPaid, registration.calculatedTotalDue);
    };

    $scope.completeStatus = function (registrant) {
      var registration = _.find(registrations, { 'id': registrant.registrationId });
      if ($scope.showRegistrationsCompleted) {
          return registration.completed;
      } else {
        return true;
      }
    };

    $scope.paidInFull = function (registrantId) {
      var registration = _.find(registrations, { 'id': registrantId });
      return registration.totalPaid >= registration.calculatedTotalDue;
    };

    var expandedRegistrations = [];
    $scope.expandRegistration = function (r) {
      if (_.contains(expandedRegistrations, r)) {
        _.remove(expandedRegistrations, function (i) { return i === r; });
      } else {
        expandedRegistrations.push(r);
      }
    };
    $scope.isExpanded = function (r) {
      return _.contains(expandedRegistrations, r);
    };

    $scope.editRegistrant = function (r) {
      var editRegistrationDialogOptions = {
        templateUrl: 'views/modals/editRegistration.html',
        controller: 'editRegistrationModalCtrl',
        resolve: {
          registrant: function () {
            return r;
          },
          conference: function () {
            return conference;
          }
        }
      };

      $modal.open(editRegistrationDialogOptions).result.then(function (result) {
        if (angular.isDefined(result)) {
          var index = _.findIndex($scope.registrants, { 'id': result.id });
          $scope.registrants[index] = result;
        }
      });
    };

    // Export conference registrations information to csv
    // showRegistrationsCompleted is now passed to this function. If checked only completed registrations will be exported.
    // If unchecked all registrations will be exported
    $scope.exportRegistrations = function () {
      var table = RegistrationsViewService.getTable(conference, registrations, $scope.showRegistrationsCompleted, $scope.getVisibleBlocksForExport());
      var csvContent = U.stringifyArray(table, ',');
      var url = apiUrl + 'services/download/registrations/' + conference.name + '-registrations.csv';
      U.submitForm(url, { name: csvContent });
    };

    /*
     *   for the export, we should always return the values of the boxes that are checked.  this gets around
     *   the fact that pre-defined views are not automatically updated.. therefore creating a scenario where
     *   a user has altered a pre-defined view, but not clicked 'save-as'.  in this case they would have
     *   exposed or hidden some blocks, but upon export receive just the blocks in the pre-defined view.
     */
    $scope.getVisibleBlocksForExport = function () {
      return _.pluck(_.filter($scope.blocks, function (item) {
        return item.visible === true;
      }), 'id');
    };

    // Export conference registration payments information to csv
    $scope.exportPayments = function () {
      var table = PaymentsViewService.getTable(conference, registrations);
      var csvContent = U.stringifyArray(table, ',');
      var url = apiUrl + 'services/download/payments/' + conference.name + '-payments.csv';
      U.submitForm(url, { name: csvContent });
    };

    $scope.eventHasCost = function () {
      return _.max(_.flatten(conference.registrantTypes, 'cost')) > 0;
    };

    $scope.registerUser = function () {
      var registrationModalOptions = {
        templateUrl: 'views/modals/manualRegistration.html',
        controller: 'registrationModal',
        resolve: {
          conference: function () {
            return conference;
          }
        }
      };
      $modal.open(registrationModalOptions);
    };

    $scope.allowDeleteRegistration = function () {
      return permissions.permissionInt > 1;
    };

    $scope.getRegistration = function(id){
      return _.find(registrations, { 'id': id });
    };

    $scope.getRegistrantType = function(id){
      return _.find(conference.registrantTypes, { 'id': id });
    };

    $scope.deleteRegistrant = function (registrant) {
      var modalInstance = $modal.open({
        templateUrl: 'views/modals/deleteRegistration.html',
        controller: 'deleteRegistrationCtrl'
      });

      modalInstance.result.then(function (doDelete) {
        if (doDelete) {
          var registration = _.find(registrations, { 'id': registrant.registrationId });
          var url = 'registrations/' + registration.id;

          if(registration.registrants.length > 1){
            //Delete Registrant
            url = 'registrants/' + registrant.id;
          }

          $http({
            method: 'DELETE',
            url: url
          }).success(function () {
            _.remove($scope.registrants, function (r) {
              return r.id === registrant.id;
            });
          });
        }
      });
    };
  });
<|endoftext|>