<commit_before>module.exports = {
  plugins: [
    "react",
  ],

  extends: [
    "./index.js",
  ],

  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
  },

  rules: {
    "jsx-quotes": [ 2, "prefer-double" ],

    "react/jsx-boolean-value": [ 2, "never" ],
    "react/jsx-closing-bracket-location": [ 2, "tag-aligned" ],
    "react/jsx-curly-spacing": [ 2, "always", {
      "allowMultiline": true,
      "spacing": {
        "objectLiterals": "never",
      },
    } ],
    "react/jsx-equals-spacing": [ 2, "never" ],
    "react/jsx-filename-extension": 0, // .jsx sucks :)
    "react/jsx-first-prop-new-line": [ 2, "multiline" ],
    "react/jsx-handler-names": 2,
    "react/jsx-indent-props": [ 2, 2 ],
    "react/jsx-indent": [ 2, 2 ],
    "react/jsx-key": 2,
    "react/jsx-max-props-per-line": 0, // eslint max-len is enough
    "react/jsx-no-bind": 2,
    "react/jsx-no-duplicate-props": 2,
    "react/jsx-no-literals": 2,
    "react/jsx-no-target-blank": 2,
    "react/jsx-no-undef": 2,
    "react/jsx-pascal-case": 2,
    "react/jsx-sort-props": 0, // alpha order is stupid
    "react/jsx-sort-prop-types": 0, // deprecated
    "react/jsx-space-before-closing": 2,
    "react/jsx-uses-react": 2,
    "react/jsx-uses-vars": 2,
    "react/jsx-wrap-multilines": 2,
    "react/jsx-no-comment-textnodes": 2,

    "react/forbid-component-props": 0, // I know what I am doing ok?
    "react/display-name": 0, // transpilers does a good job for now
    "react/forbid-prop-types": 0,
    "react/no-children-prop": 2,
    "react/no-danger-with-children": 2,
    "react/no-danger": 0, // dangerous properties names are clear enough
    "react/no-deprecated": 2,
    "react/no-did-mount-set-state": 2,
    "react/no-did-update-set-state": 2,
    "react/no-direct-mutation-state": 2,
    "react/no-find-dom-node": 2,
    "react/no-is-mounted": 2,
    "react/no-multi-comp": 2,
    "react/no-render-return-value": 2,
    "react/no-set-state": 0, // sometimes it's relevant
    "react/no-string-refs": 2,
    "react/no-unescaped-entities": 2,
    "react/no-unknown-property": 2,
    "react/no-unused-prop-types": 2,
    "react/prefer-es6-class": 2,
    "react/prefer-stateless-function": 2,
    "react/prop-types": 2,
    "react/react-in-jsx-scope": 2,
    "react/require-optimization": 0, // a bit brutal
    "react/require-render-return": 2,
    "react/self-closing-comp": 2,
    "react/sort-comp": [ 2, {
      "order": [
        // allow to add props/state on top of react classes
        "props",
        "state",

        // defaults
        // eslint-disable-next-line max-len
        // see https://github.com/yannickcr/eslint-plugin-react/blob/master/lib/rules/sort-comp.js
        "static-methods",
        "lifecycle",
        "everything-else",
        "render",
      ],
    } ],
    "react/sort-prop-types": 0, // alpha order is stupid
    "react/style-prop-object": 2,

    // deprecated with v6
    "react/require-extension": 0,
    "react/wrap-multilines" : 0,
    "react/no-comment-textnodes": 0,
  },
}
<commit_msg>no-unused-prop-types is really buggy<commit_after>module.exports = {
  plugins: [
    "react",
  ],

  extends: [
    "./index.js",
  ],

  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
  },

  rules: {
    "jsx-quotes": [ 2, "prefer-double" ],

    "react/jsx-boolean-value": [ 2, "never" ],
    "react/jsx-closing-bracket-location": [ 2, "tag-aligned" ],
    "react/jsx-curly-spacing": [ 2, "always", {
      "allowMultiline": true,
      "spacing": {
        "objectLiterals": "never",
      },
    } ],
    "react/jsx-equals-spacing": [ 2, "never" ],
    "react/jsx-filename-extension": 0, // .jsx sucks :)
    "react/jsx-first-prop-new-line": [ 2, "multiline" ],
    "react/jsx-handler-names": 2,
    "react/jsx-indent-props": [ 2, 2 ],
    "react/jsx-indent": [ 2, 2 ],
    "react/jsx-key": 2,
    "react/jsx-max-props-per-line": 0, // eslint max-len is enough
    "react/jsx-no-bind": 2,
    "react/jsx-no-duplicate-props": 2,
    "react/jsx-no-literals": 2,
    "react/jsx-no-target-blank": 2,
    "react/jsx-no-undef": 2,
    "react/jsx-pascal-case": 2,
    "react/jsx-sort-props": 0, // alpha order is stupid
    "react/jsx-sort-prop-types": 0, // deprecated
    "react/jsx-space-before-closing": 2,
    "react/jsx-uses-react": 2,
    "react/jsx-uses-vars": 2,
    "react/jsx-wrap-multilines": 2,
    "react/jsx-no-comment-textnodes": 2,

    "react/forbid-component-props": 0, // I know what I am doing ok?
    "react/display-name": 0, // transpilers does a good job for now
    "react/forbid-prop-types": 0,
    "react/no-children-prop": 2,
    "react/no-danger-with-children": 2,
    "react/no-danger": 0, // dangerous properties names are clear enough
    "react/no-deprecated": 2,
    "react/no-did-mount-set-state": 2,
    "react/no-did-update-set-state": 2,
    "react/no-direct-mutation-state": 2,
    "react/no-find-dom-node": 2,
    "react/no-is-mounted": 2,
    "react/no-multi-comp": 2,
    "react/no-render-return-value": 2,
    "react/no-set-state": 0, // sometimes it's relevant
    "react/no-string-refs": 2,
    "react/no-unescaped-entities": 2,
    "react/no-unknown-property": 2,
    // https://github.com/yannickcr/eslint-plugin-react/issues/861
    "react/no-unused-prop-types": 0,
    "react/prefer-es6-class": 2,
    "react/prefer-stateless-function": 2,
    "react/prop-types": 2,
    "react/react-in-jsx-scope": 2,
    "react/require-optimization": 0, // a bit brutal
    "react/require-render-return": 2,
    "react/self-closing-comp": 2,
    "react/sort-comp": [ 2, {
      "order": [
        // allow to add props/state on top of react classes
        "props",
        "state",

        // defaults
        // eslint-disable-next-line max-len
        // https://github.com/yannickcr/eslint-plugin-react/blob/master/lib/rules/sort-comp.js
        "static-methods",
        "lifecycle",
        "everything-else",
        "render",
      ],
    } ],
    "react/sort-prop-types": 0, // alpha order is stupid
    "react/style-prop-object": 2,

    // deprecated with v6
    "react/require-extension": 0,
    "react/wrap-multilines" : 0,
    "react/no-comment-textnodes": 0,
  },
}
<|endoftext|>


<commit_before>module.exports = [

  {
    location: 'Fremont',
    address: '45500 Fremont Blvd, Fremont, CA 94538 - Tesla Factory',
    state: 'CA',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Folsom',
    address: '13000 Folsom Blvd, Folsom, CA 95630 - Folsom Boulevard Exit 23 - Folsom Premium Outlets',
    state: 'CA',
    slots: 4,
    rate: 0,
    description: ''
  },

  {
    location: 'Gilroy',
    address: '681 Leavesley Rd, Gilroy, CA 95020 - 101 at Leavesley Road - Gilroy Premium Outlets - Near Sony',
    state: 'CA',
    slots: 10,
    rate: 120,
    description: ''
  },

  {
    location: 'Harris Ranch',
    address: 'I-5 Exit 334, Harris Ranch Inn and Restaurant 24505 W. Dorris Ave Coalinga, CA 93210',
    state: 'CA',
    slots: 7,
    rate: 120,
    description: ''
  },

  {
    location: 'Mt. Shasta',
    address: '111 Morgan Way, Mt Shasta, CA',
    state: 'CA',
    slots: 4,
    rate: 120,
    description: ''
  },

  {
    location: 'Roseville',
    address: '1151 Galleria Blvd, Roseville, CA 95678 - Westfield Galleria at Roseville',
    state: 'CA',
    slots: 7,
    rate: 135,
    description: ''
  },

  {
    location: 'Truckee',
    address: '11290 Donner Pass Road, Truckee, CA 96161',
    state: 'CA',
    slots: 6,
    rate: 135,
    description: ''
  },

  {
    location: 'Vacaville',
    address: '101 Nut Tree Rd, Vacaville, CA 95687 - Vacaville Premium Outlets',
    state: 'CA',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Atascadero',
    address: '6950 El Camino Real Atascadero, CA 93422 - Rabobank Parking Lot',
    state: 'CA',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Barstow',
    address: '1611 E Main St, Barstow, CA 92311 - I-15 Exit 178 - Country Inn and Suites',
    state: 'CA',
    slots: 8,
    rate: 135,
    description: ''
  },

  {
    location: 'Buellton',
    address: '555 McMurray Rd, Buellton, CA 93427 - US0-101 Exit 140 - Marriott Hotel',
    state: 'CA',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Cabazon',
    address: '48400 Seminole Drive, Cabazon, CA 92230 - Desert Hills Premium Outlets',
    state: 'CA',
    slots: 10,
    rate: 135,
    description: ''
  },

  {
    location: 'Culver City',
    address: '6000 Sepulveda Blvd., Culver City, CA 90230 - Westfield Culver City',
    state: 'CA',
    slots: 12,
    rate: 135,
    description: ''
  },

  {
    location: 'El Centro',
    address: '3551 South Dogwood Road, El Centro, CA - Imperial Valley Mall',
    state: 'CA',
    slots: 8,
    rate: 135,
    description: ''
  },

  {
    location: 'Indio',
    address: '42100 Jackson Street, Indio, CA 92203',
    state: 'CA',
    slots: 8,
    rate: 135,
    description: ''
  },

  {
    location: 'LA',
    address: '3203 Jack Northrop Ave, Hawthorne, CA 90250 - I-105 Exit 5 - SpaceX - Near Hawthorne Municipal Airport',
    state: 'CA',
    slots: 6,
    rate: 120,
    description: ''
  },

  {
    location: 'Needles',
    address: '2451 Needles Highway, Needles CA 92363',
    state: 'CA',
    slots: 4,
    rate: 135,
    description: ''
  },

  {
    location: 'Oxnard',
    address: '2751 Park View Court, Oxnard, CA 93036',
    state: 'CA',
    slots: 10,
    rate: 0,
    description: ''
  },

  {
    location: 'Rancho Cucamonga',
    address: '12642 Cultural Center Drive, Rancho Cucamonga, CA 91739 - Victoria Gardens Shopping Center',
    state: 'CA',
    slots: 12,
    rate: 135,
    description: ''
  },

  {
    location: 'San Juan Capistrano',
    address: '31971 Camino Capistrano, San Juan Capistrano, CA 92675 - Union Bank',
    state: 'CA',
    slots: 7,
    rate: 135,
    description: ''
  },

  {
    location: 'Tejon Ranch',
    address: '5602 Dennis McCarthy Dr, Lebec, CA 93243 - I-5 Exit 219B - Petro Shopping Center',
    state: 'CA',
    slots: 6,
    rate: 120,
    description: ''
  }

];<commit_msg>Added lat and long to CA supercharger locations<commit_after>module.exports = [

  {
    location: 'Fremont',
    address: '45500 Fremont Blvd, Fremont, CA 94538 - Tesla Factory',
    latitude: 37.49267,
    longitude: -121.94409,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Folsom',
    address: '13000 Folsom Blvd, Folsom, CA 95630 - Folsom Boulevard Exit 23 - Folsom Premium Outlets',
    latitude: 38.64392,
    longitude: -121.18621,
    state: 'CA',
    country: 'United States',
    slots: 4,
    rate: 0,
    description: ''
  },

  {
    location: 'Gilroy',
    address: '681 Leavesley Rd, Gilroy, CA 95020 - 101 at Leavesley Road - Gilroy Premium Outlets - Near Sony',
    latitude: 37.02615,
    longitude: -121.56487,
    state: 'CA',
    country: 'United States',
    slots: 10,
    rate: 120,
    description: ''
  },

  {
    location: 'Harris Ranch',
    address: 'I-5 Exit 334, Harris Ranch Inn and Restaurant 24505 W. Dorris Ave Coalinga, CA 93210',
    latitude: 36.25316,
    longitude: -120.23853,
    state: 'CA',
    country: 'United States',
    slots: 7,
    rate: 120,
    description: ''
  },

  {
    location: 'Mt. Shasta',
    address: '111 Morgan Way, Mt Shasta, CA',
    latitude: 41.30981,
    longitude: -122.31766,
    state: 'CA',
    country: 'United States',
    slots: 4,
    rate: 120,
    description: ''
  },

  {
    location: 'Roseville',
    address: '1151 Galleria Blvd, Roseville, CA 95678 - Westfield Galleria at Roseville',
    latitude: 38.77344,
    longitude: -121.26928,
    state: 'CA',
    country: 'United States',
    slots: 7,
    rate: 135,
    description: ''
  },

  {
    location: 'Truckee',
    address: '11290 Donner Pass Road, Truckee, CA 96161',
    latitude: 39.32720,
    longitude: -120.20643,
    state: 'CA',
    country: 'United States',
    slots: 6,
    rate: 135,
    description: ''
  },

  {
    location: 'Vacaville',
    address: '101 Nut Tree Rd, Vacaville, CA 95687 - Vacaville Premium Outlets',
    latitude: 38.36720,
    longitude: -121.95601,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Atascadero',
    address: '6950 El Camino Real Atascadero, CA 93422 - Rabobank Parking Lot',
    latitude: 35.48640,
    longitude: -120.66558,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Barstow',
    address: '1611 E Main St, Barstow, CA 92311 - I-15 Exit 178 - Country Inn and Suites',
    latitude: 34.89120,
    longitude: -116.99910,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 135,
    description: ''
  },

  {
    location: 'Buellton',
    address: '555 McMurray Rd, Buellton, CA 93427 - US0-101 Exit 140 - Marriott Hotel',
    latitude: 34.61545,
    longitude: -120.18856,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 120,
    description: ''
  },

  {
    location: 'Cabazon',
    address: '48400 Seminole Drive, Cabazon, CA 92230 - Desert Hills Premium Outlets',
    latitude: 33.92967,
    longitude: -116.81649,
    state: 'CA',
    country: 'United States',
    slots: 10,
    rate: 135,
    description: ''
  },

  {
    location: 'Culver City',
    address: '6000 Sepulveda Blvd., Culver City, CA 90230 - Westfield Culver City',
    latitude: 33.98465,
    longitude: -118.39505,
    state: 'CA',
    country: 'United States',
    slots: 12,
    rate: 135,
    description: ''
  },

  {
    location: 'El Centro',
    address: '3551 South Dogwood Road, El Centro, CA - Imperial Valley Mall',
    latitude: 32.76200,
    longitude: -115.53197,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 135,
    description: ''
  },

  {
    location: 'Indio',
    address: '42100 Jackson Street, Indio, CA 92203',
    latitude: 33.74272,
    longitude: -116.21316,
    state: 'CA',
    country: 'United States',
    slots: 8,
    rate: 135,
    description: ''
  },

  {
    location: 'LA',
    address: '3203 Jack Northrop Ave, Hawthorne, CA 90250 - I-105 Exit 5 - SpaceX - Near Hawthorne Municipal Airport',
    latitude: 33.92142,
    longitude: 118.32982,
    state: 'CA',
    country: 'United States',
    slots: 6,
    rate: 120,
    description: ''
  },

  {
    location: 'Needles',
    address: '2451 Needles Highway, Needles CA 92363',
    latitude: 34.85083,
    longitude: -114.62414,
    state: 'CA',
    country: 'United States',
    slots: 4,
    rate: 135,
    description: ''
  },

  {
    location: 'Oxnard',
    address: '2751 Park View Court, Oxnard, CA 93036',
    latitude: 34.24075,
    longitude: -119.17699,
    state: 'CA',
    country: 'United States',
    slots: 10,
    rate: 0,
    description: ''
  },

  {
    location: 'Rancho Cucamonga',
    address: '12642 Cultural Center Drive, Rancho Cucamonga, CA 91739 - Victoria Gardens Shopping Center',
    latitude: 34.11348,
    longitude: -117.53257,
    state: 'CA',
    country: 'United States',
    slots: 12,
    rate: 135,
    description: ''
  },

  {
    location: 'San Juan Capistrano',
    address: '31971 Camino Capistrano, San Juan Capistrano, CA 92675 - Union Bank',
    latitude: 33.49835,
    longitude: -117.66287,
    state: 'CA',
    country: 'United States',
    slots: 7,
    rate: 135,
    description: ''
  },

  {
    location: 'Tejon Ranch',
    address: '5602 Dennis McCarthy Dr, Lebec, CA 93243 - I-5 Exit 219B - Petro Shopping Center',
    latitude: 34.98661,
    longitude: -118.94630,
    state: 'CA',
    country: 'United States',
    slots: 6,
    rate: 120,
    description: ''
  }

];<|endoftext|>


<commit_before>define(['d3', '../../hierarchyelements', '../../datastore', '../../listeners', './settings'], function (d3, hierarchyElements, dataStore, listeners, s) {

  var BOX_WIDTH = 16;
  var DATASET_HEIGHT = 14;
  var DATA_GROUP_HEIGHT = 80;
  var DATA_GROUP_V_PADDING = 5;
  var DATA_GROUP_CONTENT_HEIGHT = DATA_GROUP_HEIGHT - 2 * DATA_GROUP_V_PADDING;

  function DatasetWrapper(name, minValue, maxValue) {
    hierarchyElements.HierarchyElement.call(this);
    this.collapsed = true;
    this.name = name;
    this.minValue = minValue;
    this.maxValue = maxValue;
  }

  DatasetWrapper.prototype = Object.create(hierarchyElements.HierarchyElement.prototype);

  DatasetWrapper.prototype.getBaseHeight = function () {
    return DATASET_HEIGHT;
  };

  function DataGroupWrapper(name, parent) {
    hierarchyElements.HierarchyElement.call(this, parent);
    this.name = name;
  }

  DataGroupWrapper.prototype = Object.create(hierarchyElements.HierarchyElement.prototype);

  DataGroupWrapper.prototype.getBaseHeight = function () {
    return DATA_GROUP_HEIGHT;
  };

  function DatasetRenderer(pathList) {
    this.pathList = pathList;
  }

  function appendWhisker(parent, iqr, quartile, classNamePrefix, scaleY, pathWrapper, nodeIndex, pathList) {
    if (!isNaN(iqr)) {

      parent.append("line")
        .classed(classNamePrefix + "Whisker", true)
        .attr({
          x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 4,
          y1: scaleY(iqr),
          x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true) + BOX_WIDTH / 4,
          y2: scaleY(iqr)
        })
        .style({
          stroke: "black"
        });

      parent.append("line")
        .classed(classNamePrefix + "WhiskerConnector", true)
        .attr({
          x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true),
          y1: scaleY(iqr),
          x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true),
          y2: scaleY(quartile)
        })
        .style({
          stroke: "black"
        });
    }
  }

  function updateWhisker(parent, classPrefix, pathWrapper, nodeIndex, pathList) {
    parent.select("line." + classPrefix + "Whisker")
      .transition()
      .attr({
        x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 4,
        x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true) + BOX_WIDTH / 4
      });
    parent.select("line." + classPrefix + "WhiskerConnector")
      .transition()
      .attr({
        x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true),
        x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true)
      });
  }

  DatasetRenderer.prototype = {

    render: function () {
      var that = this;
      var allDatasetGroups = that.pathList.parent.selectAll("g.pathContainer g.datasetGroup")
        .data(that.pathList.pathWrappers, function (d) {
          return d.path.id;
        });

      allDatasetGroups.attr({
        transform: function (d) {
          return "translate(0," + (s.PATH_HEIGHT + d.getSetHeight()) + ")";
        }
      });

      allDatasetGroups.each(function (pathWrapper) {

        var allDatasets = d3.select(this).selectAll("g.dataset")
          .data(pathWrapper.datasets);

        var dataset = allDatasets.enter()
          .append("g")
          .classed("dataset", true);

        dataset.append("text")
          .attr("class", "collapseIconSmall");

        allDatasets.selectAll("text.collapseIconSmall")
          .attr("x", 5)
          .attr("y", s.SET_TYPE_HEIGHT)
          .text(function (d) {
            return d.collapsed ? "\uf0da" : "\uf0dd";
          })
          .on("click", function (d) {
            var collapsed = !d.collapsed;
            if (d3.event.ctrlKey) {
              listeners.notify(s.pathListUpdateTypes.COLLAPSE_ELEMENT_TYPE, {
                type: d.name,
                collapsed: collapsed
              });
            } else {
              d.collapsed = collapsed;
              d3.select(this).text(d.collapsed ? "\uf0da" : "\uf0dd");

              that.pathList.updatePathList();

            }
            //updateAggregateList(parent);
          });

        var datasetLabel = dataset.append("text")
          .attr({
            x: s.SET_TYPE_INDENT,
            y: DATASET_HEIGHT,
            "clip-path": "url(#SetLabelClipPath)"
          })
          .text(function (d) {
            return d.name;
          });

        datasetLabel.append("title")
          .text(function (d) {
            return d.name;
          });

        allDatasets.attr({
          transform: function (d, i) {
            var posY = 0;
            var datasetWrappers = pathWrapper.datasets;

            for (var j = 0; j < i; j++) {
              var datasetWrapper = datasetWrappers[j];
              if (datasetWrapper.canBeShown()) {
                posY += datasetWrapper.getHeight();
              }
            }
            return ("translate(0, " + posY + ")");
          }
        });

        allDatasets.each(function (dataset) {

          if (dataset.collapsed) {
            d3.select(this).selectAll("g.dataGroup").remove();
            return;
          }

          var scaleY = d3.scale.linear().domain([dataset.minValue, dataset.maxValue]).range([DATA_GROUP_CONTENT_HEIGHT, 0]);

          var allGroups = d3.select(this).selectAll("g.dataGroup")
            .data(dataset.children);
          var group = allGroups.enter()
            .append("g")
            .classed("dataGroup", true);

          group.append("rect")
            .classed("background", true)
            .attr({
              x: s.SET_TYPE_INDENT,
              y: DATA_GROUP_V_PADDING,
              width: that.pathList.getNodePositionX(pathWrapper, pathWrapper.path.nodes.length - 1, false) + s.NODE_WIDTH,
              height: DATA_GROUP_CONTENT_HEIGHT
            })
            .style({
              //stroke: "black",
              fill: "rgba(240,240,240,0.5)"
            });

          var yAxis = d3.svg.axis()
            .scale(scaleY)
            .orient("left")
            .ticks(3);

          group.append("g")
            .classed("boxPlotAxis", true)
            .attr("transform", "translate(" + s.NODE_START + "," + DATA_GROUP_V_PADDING + ")")
            .call(yAxis);

          var groupLabel = group.append("text")
            .attr({
              x: s.SET_TYPE_INDENT,
              y: 14,
              "clip-path": "url(#SetLabelClipPath)"
            })
            .text(function (d) {
              return d.name;
            });

          groupLabel.append("title")
            .text(function (d) {
              return d.name;
            });

          allGroups.attr({
            transform: function (d, i) {
              var posY = DATASET_HEIGHT;
              var groups = dataset.children;

              for (var j = 0; j < i; j++) {
                var g = groups[j];
                if (g.canBeShown()) {
                  posY += g.getHeight();
                }
              }
              return ("translate(0, " + posY + ")");
            }
          });

          allGroups.each(function (group) {

            d3.select(this).select("rect.background")
              .transition()
              .attr({
                width: that.pathList.getNodePositionX(pathWrapper, pathWrapper.path.nodes.length - 1, false) + s.NODE_WIDTH
              });

            var allNodeData = d3.select(this).selectAll("g.nodeData")
              .data(pathWrapper.path.nodes);

            var nodeData = allNodeData.enter()
              .append("g")
              .classed("nodeData", true)
              .attr({
                transform: "translate(0," + DATA_GROUP_V_PADDING + ")"
              });

            nodeData.append("title")
              .text(function (d) {
                var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                return "Elements: " + stats.data.length +
                  "\nNaNs: " + stats.nans +
                  "\nMedian: " + stats.median +
                  "\n1st Quartile: " + stats.quartile25 +
                  "\n3rd Quartile: " + stats.quartile75 +
                  "\nLowest value in 1.5xIQR range: " + stats.iqrMin +
                  "\nHighest value in 1.5xIQR range: " + stats.iqrMax +
                  "\nMin: " + stats.min +
                  "\nMax: " + stats.max;
              });

            nodeData.append("rect")
              .classed("box", true)
              .attr({
                x: function (d, i) {
                  return that.pathList.getNodePositionX(pathWrapper, i, true) - BOX_WIDTH / 2;
                },
                y: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.quartile75);
                },
                width: BOX_WIDTH,
                height: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.quartile25) - scaleY(stats.quartile75);
                }
              })
              .style({
                fill: "gray",
                stroke: "black"
              });

            d3.select(this).selectAll("g.nodeData rect.box")
              .transition()
              .attr({
                x: function (d, i) {
                  return that.pathList.getNodePositionX(pathWrapper, i, true) - BOX_WIDTH / 2;
                },
                y: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);

                  return scaleY(stats.quartile75);
                  //return scaleY(stats.quartile25);
                },
                width: BOX_WIDTH,
                height: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.quartile25) - scaleY(stats.quartile75);
                }
              });

            nodeData.append("line")
              .classed("median", true)
              .attr({
                x1: function (d, i) {
                  return that.pathList.getNodePositionX(pathWrapper, i, true) - BOX_WIDTH / 2;
                },
                y1: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.median);
                },
                x2: function (d, i) {
                  return that.pathList.getNodePositionX(pathWrapper, i, true) + BOX_WIDTH / 2;
                },
                y2: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.median);
                }
              })
              .style({
                stroke: "white"
              });

            d3.select(this).selectAll("g.nodeData line.median")
              .transition()
              .attr({
                x1: function (d, i) {
                  return that.pathList.getNodePositionX(pathWrapper, i, true) - BOX_WIDTH / 2;
                },
                y1: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.median);
                },
                x2: function (d, i) {
                  return that.pathList.getNodePositionX(pathWrapper, i, true) + BOX_WIDTH / 2;
                },
                y2: function (d) {
                  var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
                  return scaleY(stats.median);
                }
              });

            nodeData.each(function (node, nodeIndex) {

              var stats = dataStore.getStatsForNode(node, dataset.name, group.name);

              appendWhisker(d3.select(this), stats.iqrMin, stats.quartile25, "lower", scaleY, pathWrapper, nodeIndex, that.pathList);
              appendWhisker(d3.select(this), stats.iqrMax, stats.quartile75, "upper", scaleY, pathWrapper, nodeIndex, that.pathList);

            });

            allNodeData.each(function (node, nodeIndex) {

              updateWhisker(d3.select(this), "lower", pathWrapper, nodeIndex, that.pathList);
              updateWhisker(d3.select(this), "upper", pathWrapper, nodeIndex, that.pathList);

              var allPoints = d3.select(this).selectAll("g.dataPoint")
                .data(dataStore.getDataForNode(node, dataset.name, group.name));

              var point = allPoints.enter()
                .append("g")
                .classed("dataPoint", true);

              point.append("circle")
                .attr({

                  cx: that.pathList.getNodePositionX(pathWrapper, nodeIndex, true),

                  cy: function (d) {
                    return scaleY(d);
                  },
                  r: 1

                })
                .style({
                  opacity: 2,
                  fill: "red"
                });

              allPoints.selectAll("circle")
                .transition()
                .attr({
                  cx: that.pathList.getNodePositionX(pathWrapper, nodeIndex, true),

                  cy: function (d) {
                    return scaleY(d);
                  }
                });

              allPoints.exit().remove();

            });

            allNodeData.exit().remove();

          });

        });

        allDatasets.exit().remove();

      });

    }
  };

  return {
    DatasetWrapper: DatasetWrapper,
    DataGroupWrapper: DataGroupWrapper,
    DatasetRenderer: DatasetRenderer
  };

});
<commit_msg>refactoring<commit_after>define(['d3', '../../hierarchyelements', '../../datastore', '../../listeners', './settings'], function (d3, hierarchyElements, dataStore, listeners, s) {

  var BOX_WIDTH = 16;
  var DATASET_HEIGHT = 14;
  var DATA_GROUP_HEIGHT = 80;
  var DATA_GROUP_V_PADDING = 5;
  var DATA_GROUP_CONTENT_HEIGHT = DATA_GROUP_HEIGHT - 2 * DATA_GROUP_V_PADDING;

  var isVertical = true;

  function DatasetWrapper(name, minValue, maxValue) {
    hierarchyElements.HierarchyElement.call(this);
    this.collapsed = true;
    this.name = name;
    this.minValue = minValue;
    this.maxValue = maxValue;
  }

  DatasetWrapper.prototype = Object.create(hierarchyElements.HierarchyElement.prototype);

  DatasetWrapper.prototype.getBaseHeight = function () {
    return DATASET_HEIGHT;
  };

  function DataGroupWrapper(name, parent) {
    hierarchyElements.HierarchyElement.call(this, parent);
    this.name = name;
  }

  DataGroupWrapper.prototype = Object.create(hierarchyElements.HierarchyElement.prototype);

  DataGroupWrapper.prototype.getBaseHeight = function () {
    return DATA_GROUP_HEIGHT;
  };

  function DataRenderer(pathList) {
    this.pathList = pathList;
  }

  DataRenderer.prototype = {

    render: function () {
      var that = this;
      var allDatasetGroups = that.pathList.parent.selectAll("g.pathContainer g.datasetGroup")
        .data(that.pathList.pathWrappers, function (d) {
          return d.path.id;
        });

      allDatasetGroups.attr({
        transform: function (d) {
          return "translate(0," + (s.PATH_HEIGHT + d.getSetHeight()) + ")";
        }
      });

      allDatasetGroups.each(function (pathWrapper) {

        var allDatasets = d3.select(this).selectAll("g.dataset")
          .data(pathWrapper.datasets);

        var dataset = allDatasets.enter()
          .append("g")
          .classed("dataset", true);

        dataset.each(function (dataset, datasetIndex) {
          var $dataset = d3.select(this);
          $dataset.append("text")
            .attr("class", "collapseIconSmall");
          var datasetLabel = $dataset.append("text")
            .attr({
              x: s.SET_TYPE_INDENT,
              y: DATASET_HEIGHT,
              "clip-path": "url(#SetLabelClipPath)"
            })
            .text(function (d) {
              return d.name;
            });

          datasetLabel.append("title")
            .text(function (d) {
              return d.name;
            });

          that.onDatasetEnter($dataset, pathWrapper, dataset, datasetIndex);
        });

        allDatasets.each(function (dataset, datasetIndex) {

          var $dataset = d3.select(this);

          $dataset.select("text.collapseIconSmall")
            .attr("x", 5)
            .attr("y", s.SET_TYPE_HEIGHT)
            .text(function (d) {
              return d.collapsed ? "\uf0da" : "\uf0dd";
            })
            .on("click", function (d) {
              var collapsed = !d.collapsed;
              if (d3.event.ctrlKey) {
                listeners.notify(s.pathListUpdateTypes.COLLAPSE_ELEMENT_TYPE, {
                  type: d.name,
                  collapsed: collapsed
                });
              } else {
                d.collapsed = collapsed;
                d3.select(this).text(d.collapsed ? "\uf0da" : "\uf0dd");

                that.pathList.updatePathList();

              }
            });

          $dataset.attr({
            transform: function (d) {
              var posY = 0;
              var datasetWrappers = pathWrapper.datasets;

              for (var j = 0; j < datasetIndex; j++) {
                var datasetWrapper = datasetWrappers[j];
                if (datasetWrapper.canBeShown()) {
                  posY += datasetWrapper.getHeight();
                }
              }
              return ("translate(0, " + posY + ")");
            }
          });

          that.onDatasetUpdate($dataset, pathWrapper, dataset, datasetIndex);

          if (dataset.collapsed) {
            $dataset.selectAll("g.dataGroup").remove();
            return;
          }

          var allGroups = $dataset.selectAll("g.dataGroup")
            .data(dataset.children);

          var group = allGroups.enter()
            .append("g")
            .classed("dataGroup", true);

          group.each(function (group, groupIndex) {

            var $group = d3.select(this);

            $group.append("rect")
              .classed("background", true)
              .attr({
                x: s.SET_TYPE_INDENT,
                y: DATA_GROUP_V_PADDING,
                width: that.pathList.getNodePositionX(pathWrapper, pathWrapper.path.nodes.length - 1, false) + s.NODE_WIDTH,
                height: DATA_GROUP_CONTENT_HEIGHT
              })
              .style({
                //stroke: "black",
                fill: "rgba(240,240,240,0.5)"
              });

            var groupLabel = $group.append("text")
              .attr({
                x: s.SET_TYPE_INDENT,
                y: 14,
                "clip-path": "url(#SetLabelClipPath)"
              })
              .text(function (d) {
                return d.name;
              });

            groupLabel.append("title")
              .text(function (d) {
                return d.name;
              });

            that.onDataGroupEnter($group, pathWrapper, dataset, group, groupIndex);
          });

          allGroups.each(function (group, groupIndex) {

            var $group = d3.select(this);

            $group.attr({
              transform: function (d) {
                var posY = DATASET_HEIGHT;
                var groups = dataset.children;

                for (var j = 0; j < groupIndex; j++) {
                  var g = groups[j];
                  if (g.canBeShown()) {
                    posY += g.getHeight();
                  }
                }
                return ("translate(0, " + posY + ")");
              }
            });

            d3.select(this).select("rect.background")
              .transition()
              .attr({
                width: that.pathList.getNodePositionX(pathWrapper, pathWrapper.path.nodes.length - 1, false) + s.NODE_WIDTH
              });

            that.onDataGroupUpdate($group, pathWrapper, dataset, group, groupIndex);

            var allNodeData = d3.select(this).selectAll("g.nodeData")
              .data(pathWrapper.path.nodes);

            var nodeData = allNodeData.enter()
              .append("g")
              .classed("nodeData", true);

            nodeData.each(function (node, nodeIndex) {
              that.onNodeDataEnter(d3.select(this), pathWrapper, dataset, group, node, nodeIndex);
            });

            allNodeData.each(function (node, nodeIndex) {
              that.onNodeDataUpdate(d3.select(this), pathWrapper, dataset, group, node, nodeIndex);
            });

            allNodeData.exit().remove();

          });

          allGroups.exit().remove();

        });

        allDatasets.exit().remove();

      });
    },

    onDatasetEnter: function ($dataset, pathWrapper, dataset, datasetIndex) {

    },

    onDatasetUpdate: function ($dataset, pathWrapper, dataset, datasetIndex) {

    },

    onDataGroupEnter: function ($group, pathWrapper, dataset, group, groupIndex) {

    },

    onDataGroupUpdate: function ($group, pathWrapper, dataset, group, groupIndex) {

    },

    onNodeDataEnter: function ($nodeData, pathWrapper, dataset, group, node, nodeIndex) {

    },

    onNodeDataUpdate: function ($nodeData, pathWrapper, dataset, group, node, nodeIndex) {

    }

  };

  function VDataRenderer(pathList) {
    DataRenderer.call(this, pathList);
  }

  VDataRenderer.prototype = Object.create(DataRenderer.prototype);

  VDataRenderer.prototype.onDataGroupEnter = function ($group, pathWrapper, dataset, group, groupIndex) {
    var that = this;
    var scaleY = d3.scale.linear().domain([dataset.minValue, dataset.maxValue]).range([DATA_GROUP_CONTENT_HEIGHT, 0]);
    var yAxis = d3.svg.axis()
      .scale(scaleY)
      .orient("left")
      .ticks(3);

    $group.append("g")
      .classed("boxPlotAxis", true)
      .attr("transform", "translate(" + s.NODE_START + "," + DATA_GROUP_V_PADDING + ")")
      .call(yAxis);
  };

  VDataRenderer.prototype.onNodeDataEnter = function ($nodeData, pathWrapper, dataset, group, node, nodeIndex) {
    var that = this;

    var scaleY = d3.scale.linear().domain([dataset.minValue, dataset.maxValue]).range([DATA_GROUP_CONTENT_HEIGHT, 0]);

    $nodeData.attr({
      transform: "translate(0," + DATA_GROUP_V_PADDING + ")"
    });

    $nodeData.append("title")
      .text(function (d) {
        var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
        return "Elements: " + stats.data.length +
          "\nNaNs: " + stats.nans +
          "\nMedian: " + stats.median +
          "\n1st Quartile: " + stats.quartile25 +
          "\n3rd Quartile: " + stats.quartile75 +
          "\nLowest value in 1.5xIQR range: " + stats.iqrMin +
          "\nHighest value in 1.5xIQR range: " + stats.iqrMax +
          "\nMin: " + stats.min +
          "\nMax: " + stats.max;
      });

    $nodeData.append("rect")
      .classed("box", true)
      .attr({
        x: function (d) {
          return that.pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 2;
        },
        y: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.quartile75);
        },
        width: BOX_WIDTH,
        height: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.quartile25) - scaleY(stats.quartile75);
        }
      })
      .style({
        fill: "gray",
        stroke: "black"
      });

    $nodeData.append("line")
      .classed("median", true)
      .attr({
        x1: function (d) {
          return that.pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 2;
        },
        y1: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.median);
        },
        x2: function (d) {
          return that.pathList.getNodePositionX(pathWrapper, nodeIndex, true) + BOX_WIDTH / 2;
        },
        y2: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.median);
        }
      })
      .style({
        stroke: "white"
      });

    var stats = dataStore.getStatsForNode(node, dataset.name, group.name);

    appendWhisker($nodeData, stats.iqrMin, stats.quartile25, "lower", scaleY, pathWrapper, nodeIndex, that.pathList);
    appendWhisker($nodeData, stats.iqrMax, stats.quartile75, "upper", scaleY, pathWrapper, nodeIndex, that.pathList);
  };

  VDataRenderer.prototype.onNodeDataUpdate = function ($nodeData, pathWrapper, dataset, group, node, nodeIndex) {
    var that = this;

    var scaleY = d3.scale.linear().domain([dataset.minValue, dataset.maxValue]).range([DATA_GROUP_CONTENT_HEIGHT, 0]);

    $nodeData.select("rect.box")
      .transition()
      .attr({
        x: function (d) {
          return that.pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 2;
        },
        y: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);

          return scaleY(stats.quartile75);
          //return scaleY(stats.quartile25);
        },
        width: BOX_WIDTH,
        height: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.quartile25) - scaleY(stats.quartile75);
        }
      });

    $nodeData.select("line.median")
      .transition()
      .attr({
        x1: function (d) {
          return that.pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 2;
        },
        y1: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.median);
        },
        x2: function (d) {
          return that.pathList.getNodePositionX(pathWrapper, nodeIndex, true) + BOX_WIDTH / 2;
        },
        y2: function (d) {
          var stats = dataStore.getStatsForNode(d, dataset.name, group.name);
          return scaleY(stats.median);
        }
      });

    updateWhisker($nodeData, "lower", pathWrapper, nodeIndex, that.pathList);
    updateWhisker($nodeData, "upper", pathWrapper, nodeIndex, that.pathList);

    var allPoints = $nodeData.selectAll("g.dataPoint")
      .data(dataStore.getDataForNode(node, dataset.name, group.name));

    var point = allPoints.enter()
      .append("g")
      .classed("dataPoint", true);

    point.append("circle")
      .attr({

        cx: that.pathList.getNodePositionX(pathWrapper, nodeIndex, true),

        cy: function (d) {
          return scaleY(d);
        },
        r: 1

      })
      .style({
        opacity: 2,
        fill: "red"
      });

    allPoints.selectAll("circle")
      .transition()
      .attr({
        cx: that.pathList.getNodePositionX(pathWrapper, nodeIndex, true),

        cy: function (d) {
          return scaleY(d);
        }
      });

    allPoints.exit().remove();

  };

  function HDataRenderer(pathList) {
    this.pathList = pathList;
  }

  HDataRenderer.prototype = {
    render: function () {

    }
  };

  function appendWhisker(parent, iqr, quartile, classNamePrefix, scaleY, pathWrapper, nodeIndex, pathList) {
    if (!isNaN(iqr)) {

      parent.append("line")
        .classed(classNamePrefix + "Whisker", true)
        .attr({
          x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 4,
          y1: scaleY(iqr),
          x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true) + BOX_WIDTH / 4,
          y2: scaleY(iqr)
        })
        .style({
          stroke: "black"
        });

      parent.append("line")
        .classed(classNamePrefix + "WhiskerConnector", true)
        .attr({
          x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true),
          y1: scaleY(iqr),
          x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true),
          y2: scaleY(quartile)
        })
        .style({
          stroke: "black"
        });
    }
  }

  function updateWhisker(parent, classPrefix, pathWrapper, nodeIndex, pathList) {
    parent.select("line." + classPrefix + "Whisker")
      .transition()
      .attr({
        x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true) - BOX_WIDTH / 4,
        x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true) + BOX_WIDTH / 4
      });
    parent.select("line." + classPrefix + "WhiskerConnector")
      .transition()
      .attr({
        x1: pathList.getNodePositionX(pathWrapper, nodeIndex, true),
        x2: pathList.getNodePositionX(pathWrapper, nodeIndex, true)
      });
  }

  function DatasetRenderer(pathList) {
    this.vRenderer = new VDataRenderer(pathList);
    this.hRenderer = new HDataRenderer(pathList);
  }

  DatasetRenderer.prototype = {

    render: function () {
      if (isVertical) {
        this.vRenderer.render();
      } else {
        this.hRenderer.render();
      }

    }
  };

  return {
    DatasetWrapper: DatasetWrapper,
    DataGroupWrapper: DataGroupWrapper,
    DatasetRenderer: DatasetRenderer
  };

});
<|endoftext|>


<commit_before>import Connector from './connector';
import invariant from 'invariant';
import {createStore, applyMiddleware, compose, combineReducers} from 'redux';
import digestMiddleware from './digestMiddleware';

import assign from 'lodash.assign';
import isArray from 'lodash.isarray';
import isFunction from 'lodash.isfunction';
import isObejct from 'lodash.isobject';

export default function ngReduxProvider() {
  let _reducer = undefined;
  let _middlewares = undefined;
  let _storeEnhancers = undefined;
  let _initialState = undefined;
  let _reducerIsObejct = undefined;

  this.createStoreWith = (reducer, middlewares, storeEnhancers, initialState) => {
    invariant(
      isFunction(reducer) || isObejct(reducer),
      'The reducer parameter passed to createStoreWith must be a Function or an Object. Instead received %s.',
      typeof reducer
    );

    invariant(
      !storeEnhancers || isArray(storeEnhancers),
      'The storeEnhancers parameter passed to createStoreWith must be an Array. Instead received %s.',
      typeof storeEnhancers
    );

    _reducer = reducer;
    _reducerIsObejct = isObejct(reducer);
    _storeEnhancers = storeEnhancers
    _middlewares = middlewares || [];
    _initialState = initialState;
  };

  this.$get = ($injector) => {
    let store, resolvedMiddleware = [];

    for(let middleware of _middlewares) {
      if(typeof middleware === 'string') {
        resolvedMiddleware.push($injector.get(middleware));
      } else {
        resolvedMiddleware.push(middleware);
      }
    }

    if(_reducerIsObejct) {
      let reducersObj = {};
      let reducKeys = Object.keys(_reducer);

      reducKeys.forEach((key) => {
        if(typeof _reducer[key] === 'string') {
          reducersObj[key] = $injector.get(_reducer[key]);
        } else {
          reducersObj[key] = _reducer[key];
        }
      });

      _reducer = combineReducers(reducersObj);
    }

    let finalCreateStore = _storeEnhancers ? compose(..._storeEnhancers)(createStore) : createStore;

    //digestMiddleware needs to be the last one.
    resolvedMiddleware.push(digestMiddleware($injector.get('$rootScope')));

    store = _initialState
      ? applyMiddleware(...resolvedMiddleware)(finalCreateStore)(_reducer, _initialState)
      : applyMiddleware(...resolvedMiddleware)(finalCreateStore)(_reducer);

    return assign({}, store, { connect: Connector(store) });
  };

  this.$get.$inject = ['$injector'];
}
<commit_msg>fixes object typo<commit_after>import Connector from './connector';
import invariant from 'invariant';
import {createStore, applyMiddleware, compose, combineReducers} from 'redux';
import digestMiddleware from './digestMiddleware';

import assign from 'lodash.assign';
import isArray from 'lodash.isarray';
import isFunction from 'lodash.isfunction';
import isObject from 'lodash.isobject';

export default function ngReduxProvider() {
  let _reducer = undefined;
  let _middlewares = undefined;
  let _storeEnhancers = undefined;
  let _initialState = undefined;
  let _reducerIsObject = undefined;

  this.createStoreWith = (reducer, middlewares, storeEnhancers, initialState) => {
    invariant(
      isFunction(reducer) || isObject(reducer),
      'The reducer parameter passed to createStoreWith must be a Function or an Object. Instead received %s.',
      typeof reducer
    );

    invariant(
      !storeEnhancers || isArray(storeEnhancers),
      'The storeEnhancers parameter passed to createStoreWith must be an Array. Instead received %s.',
      typeof storeEnhancers
    );

    _reducer = reducer;
    _reducerIsObject = isObject(reducer);
    _storeEnhancers = storeEnhancers
    _middlewares = middlewares || [];
    _initialState = initialState;
  };

  this.$get = ($injector) => {
    let store, resolvedMiddleware = [];

    for(let middleware of _middlewares) {
      if(typeof middleware === 'string') {
        resolvedMiddleware.push($injector.get(middleware));
      } else {
        resolvedMiddleware.push(middleware);
      }
    }

    if(_reducerIsObject) {
      let reducersObj = {};
      let reducKeys = Object.keys(_reducer);

      reducKeys.forEach((key) => {
        if(typeof _reducer[key] === 'string') {
          reducersObj[key] = $injector.get(_reducer[key]);
        } else {
          reducersObj[key] = _reducer[key];
        }
      });

      _reducer = combineReducers(reducersObj);
    }

    let finalCreateStore = _storeEnhancers ? compose(..._storeEnhancers)(createStore) : createStore;

    //digestMiddleware needs to be the last one.
    resolvedMiddleware.push(digestMiddleware($injector.get('$rootScope')));

    store = _initialState
      ? applyMiddleware(...resolvedMiddleware)(finalCreateStore)(_reducer, _initialState)
      : applyMiddleware(...resolvedMiddleware)(finalCreateStore)(_reducer);

    return assign({}, store, { connect: Connector(store) });
  };

  this.$get.$inject = ['$injector'];
}
<|endoftext|>


<commit_before>/**
 * @license Copyright (c) 2010-2011 Brian Cavalier
 * LICENSE: see the LICENSE.txt file. If file is missing, this file is subject
 * to the MIT License at: http://www.opensource.org/licenses/mit-license.php.
 */

/*
	Package: pubsub.js
	wire plugin that sets up subscriptions and topics to be published after
	functions are invoked, and disconnect them when an object is destroyed.
	This implementation uses dojo.publish, dojo.subscribe and dojo.unsubscribe
	to do the work of connecting and disconnecting event handlers.
*/
define(['dojo', 'dojo/_base/connect'], function(pubsub) {

	return {
		wire$plugin: function pubsubPlugin(ready, destroyed, options) {

			var destroyHandlers = [];

			/*
				Function: proxyPublish
				Proxies methods on target so that they publish topics after being
				invoked.  The payload of a topic will be the return value of the method
				that triggered it.

				Parameters:
					target - object whose methods should be proxied
					publish - hash of method names to topics each should publish
			*/
			function proxyPublish(target, publish) {
				var originals = {};

				for(var f in publish) {
					if(typeof target[f] == 'function') {
						var orig = originals[f] = target[f],
							topic = publish[f];

						target[f] = function publishProxy() {
							var result = orig.apply(target, arguments);
							pubsub.publish(topic, [result]);
						};
					}
				}

				destroyHandlers.push(function() {
					unproxyPublish(target, originals);
				});
			}

			/*
				Function: unproxyPublish
				Restores the original functions on object after they've been proxied
				by <proxyPublish> to publish topics.

				Parameters:
					object - object whose methods should be restored
					originals - hash of method names to original methods to restore
			*/
			function unproxyPublish(object, originals) {
				for(var p in originals) {
					object[p] = originals[p];
				}
			}

			function subscribeTarget(target, subscriptions) {
				var subscribeHandles = [];
				for(var topic in subscriptions) {
					var f = subscriptions[topic];
					if(typeof target[f] == 'function') {
						// TODO: How to unsubscribe?
						subscribeHandles.push(pubsub.subscribe(topic, target, f));
					}
				}

				if(subscribeHandles.length > 0) {
					destroyHandlers.push(function() {
						unsubscribeTarget(subscribeHandles);
					});
				}
			}

		    function unsubscribeTarget(handles) {
		        for (var i = handles.length - 1; i >= 0; i--){
					pubsub.unsubscribe(handles[i]);
				}
			}

			destroyed.then(function onContextDestroy() {
				for (var i = destroyHandlers.length - 1; i >= 0; i--){
					destroyHandlers[i]();
				}
			});

			return {
				facets: {
					publish: {
						ready: function(promise, facet, wire) {
							proxyPublish(facet.target, facet.options);
							promise.resolve();
						}
					},
					subscribe: {
						ready: function(promise, facet, wire) {
							subscribeTarget(facet.target, facet.options);
							promise.resolve();
						}
					}
				}
			}
		}
	};
});<commit_msg>Use aop.js to add auto dojo.publish<commit_after>/**
 * @license Copyright (c) 2010-2011 Brian Cavalier
 * LICENSE: see the LICENSE.txt file. If file is missing, this file is subject
 * to the MIT License at: http://www.opensource.org/licenses/mit-license.php.
 */

/*
	Package: pubsub.js
	wire plugin that sets up subscriptions and topics to be published after
	functions are invoked, and disconnect them when an object is destroyed.
	This implementation uses dojo.publish, dojo.subscribe and dojo.unsubscribe
	to do the work of connecting and disconnecting event handlers.
*/
define(['dojo', 'aop', 'dojo/_base/connect'], function(pubsub, aop) {

	return {
		wire$plugin: function pubsubPlugin(ready, destroyed, options) {

			var destroyHandlers = [];

			/*
				Function: proxyPublish
				Proxies methods on target so that they publish topics after being
				invoked.  The payload of a topic will be the return value of the method
				that triggered it.

				Parameters:
					target - object whose methods should be proxied
					publish - hash of method names to topics each should publish
			*/
			function proxyPublish(target, publish) {
				var remove;

				for(var f in publish) {
					if(typeof target[f] == 'function') {

                        // Add after advice and save remove function to remove
                        // advice when this context is destroyed
                        remove = aop.after(target, f, function (result) {
                            pubsub.publish(publish[f], [result]);
                        });

                        destroyHandlers.push(remove);
                    }
				}
			}

			/*
				Function: unproxyPublish
				Restores the original functions on object after they've been proxied
				by <proxyPublish> to publish topics.

				Parameters:
					object - object whose methods should be restored
					originals - hash of method names to original methods to restore
			*/
			function unproxyPublish(object, originals) {
				for(var p in originals) {
					object[p] = originals[p];
				}
			}

			function subscribeTarget(target, subscriptions) {
				var subscribeHandles = [];
				for(var topic in subscriptions) {
					var f = subscriptions[topic];
					if(typeof target[f] == 'function') {
						// TODO: How to unsubscribe?
						subscribeHandles.push(pubsub.subscribe(topic, target, f));
					}
				}

				if(subscribeHandles.length > 0) {
					destroyHandlers.push(function() {
						unsubscribeTarget(subscribeHandles);
					});
				}
			}

		    function unsubscribeTarget(handles) {
		        for (var i = handles.length - 1; i >= 0; i--){
					pubsub.unsubscribe(handles[i]);
				}
			}

			destroyed.then(function onContextDestroy() {
				for (var i = destroyHandlers.length - 1; i >= 0; i--){
					destroyHandlers[i]();
				}
			});

			return {
				facets: {
					publish: {
						ready: function(promise, facet, wire) {
							proxyPublish(facet.target, facet.options);
							promise.resolve();
						}
					},
					subscribe: {
						ready: function(promise, facet, wire) {
							subscribeTarget(facet.target, facet.options);
							promise.resolve();
						}
					}
				}
			}
		}
	};
});<|endoftext|>


<commit_before>// LINK LIMITS
var GOOD_LINK_RSSI      = -70;
var MEDIUM_LINK_RSSI    = -80;
var GOOD_LINK_PDR       = 80;
var MEDIUM_LINK_PDR     = 50;

// LINK COLOR
var GOOD_LINK_COLOR     = "#00ff00"     // green
var MEDIUM_LINK_COLOR   = "#ffff00"     // orange
var BAD_LINK_COLOR      = "#ffff00"     // red
var UNKNOWN_LINK_COLOR  = "#ffffff"     // white

// Global variables
var map;
var motes   = [];         // a list of motes, indexed by id
var links   = [];         // a list of links
var timeout;
var infoWindow;
var SITE_TIME_OFFSET    = -3
var DEFAULT_PDR         = 101 // init to impossible value
var board_colors        = {"#c33c1c":null,"#dbd60d":null,"#acd2cd":null,"#ff9966":null}

//------------------ Init functions ------------------------------------------//

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: new google.maps.LatLng(0, 0),
        zoom: 18,
        scaleControl: true,
    });
    map.setMapTypeId(google.maps.MapTypeId.SATELLITE);

    // set date
    $( "#datepicker" ).datepicker();
    $( "#datepicker" ).datepicker('setDate', 'today');

    // set time
    defaultDate = new Date();
    defaultDate.setTime(defaultDate.getTime());
    $('#timepicker').timepicker({
        showNowButton: true,
        showDeselectButton: true,
        showCloseButton: true,
    });
    $('#timepicker').timepicker('setTime', defaultDate.toLocaleTimeString(
                                'es-AR', { hour: "numeric",
                                minute: "numeric"}));
    load_data(1);
}

function load_data(loop){
    clearLinks();

    // set default time to UTC time
    var date        = $("#datepicker").datepicker('getDate');
    var time        = $("#timepicker").timepicker('getTime').split(':');
    var localOffset = new Date().getTimezoneOffset()/60;
    date.setHours(parseInt(time[0]), time[1]);

    // get host and site name
    var host        = window.location.origin
    var path        = window.location.pathname.split("/map")[0]
    var sitename    = $("#sitename").val()

    // display motes and links
    get_motes(host, path, sitename, date);
    var delay = function() { get_links(host, path, sitename, date) };
    window.setTimeout(delay,500);

    if (loop == 1)
        timeout = setTimeout(load_data, 30000);
    else
        clearTimeout(timeout)
}

//------------------ Main functions ------------------------------------------//

// request the server for mote information
function get_motes(host, path, sitename, date){
    var solType     = "SOL_TYPE_DUST_SNAPSHOT";
    var encType     = encodeURIComponent(solType);
    var isoTime     = date.toISOString();
    var encTime     = encodeURIComponent(isoTime);
    $.getJSON(host+ path +
        "/api/v1/jsonp/"+sitename+"/" + encType +
        "/time/" + encTime, create_motes);
}

// request the server for links information
function get_links(host, path, sitename, date){
    var solType     = "SOL_TYPE_DUST_NOTIF_HRNEIGHBORS";
    var encType     = encodeURIComponent(solType);
    var isoTime     = date.toISOString();
    var encTime     = encodeURIComponent(isoTime);
    $.getJSON(host+ path +
        "/api/v1/jsonp/"+sitename+"/" + encType +
        "/time/" + encTime, create_links);
}

// populate the motes list
function create_motes(data){
    if (Object.keys(data).length > 0) {

        // manager
        motes[1] = {
            "mac"       : data[0].mac,
            "marker"    : null,
        }

        // motes
        mote_list = data[0].value.mote
        for (var i=0; i < Object.keys(mote_list).length; i++) {
            // populate table
            motes[mote_list[i].moteId] = {
                "mac"       : mote_list[i].macAddress,
                "marker"    : null
            }
        }
    }
}

function create_links(data){
    var bounds = new google.maps.LatLngBounds();
    var loc;

    // update motes location and board type

    //-- manager
    var color   = getMoteColor(data[0].board);
    motes[1].marker = createMarker(
        data[0].latitude,
        data[0].longitude,
        "Manager
"+data[0].mac+"
"+data[0].board,
        color
        );

    //-- motes
    for (var i=0; i < data.length; i++) {
        loc = new google.maps.LatLng(
            data[i].value.latitude,
            data[i].value.longitude
        );
        bounds.extend(loc);
        updateMote(
            data[i].mac,
            data[i].value.latitude,
            data[i].value.longitude,
            data[i].value.board);
    }
    map.fitBounds(bounds);
    map.panToBounds(bounds);

    // create links
    for (var i=0; i < data.length; i++) {
        for (var n_id in data[i].value.neighbors){
            var neighbor = data[i].value.neighbors[n_id]
            if (neighbor.neighborId in motes){
                var crd1 = getLocationFromMac(data[i].mac);
                var crd2 = getLocationFromId(neighbor.neighborId)
                if (crd1 != null && crd2 != null){
                    var lineCoords = [crd1, crd2];
                    var link    = getLink(crd1, crd2);

                    // get metric
                    var pdr = DEFAULT_PDR //init to unpossible value
                    if (neighbor.numTxPackets > 0){
                        pdr     = ((neighbor.numTxPackets - neighbor.numTxFailures)
                                    /neighbor.numTxPackets
                                ) * 100;
                    }
                    var rssi    = neighbor.rssi;

                    // update link if already exists
                    if (link != null){
                        link.metric.rssi    = Math.min(rssi, link.metric.rssi)
                        link.metric.pdr     = Math.min(pdr, link.metric.pdr)

                        link.pline.setMap(null);
                        var color   = getLinkColor(link.metric.rssi, link.metric.pdr);
                        var dist    = calcDistance(lineCoords[0],lineCoords[1])
                        // set line parameters
                        if (link.metric.pdr == DEFAULT_PDR)
                          s_pdr = "No Tx"
                        else
                          s_pdr = link.metric.pdr + "%"
                        var content =   "RSSI: " + rssi + "dBm
" +
                                        "PDR: " + s_pdr + "
" +
                                        "Distance: " + dist + "m"
                        link.pline  = createPolyline(lineCoords, content, color);
                    } // create link if it does not already exists
                    else {
                        var color   = getLinkColor(rssi, pdr);
                        var dist    = calcDistance(lineCoords[0],lineCoords[1])
                        // set line parameters
                        // set line parameters
                        if (pdr == DEFAULT_PDR)
                          s_pdr = "No Tx"
                        else
                          s_pdr = pdr + "%"
                        var content =   "RSSI: " + rssi + "dBm
" +
                                        "PDR: " + s_pdr + "
" +
                                        "Distance: " + dist + "m"
                        var l = createPolyline(lineCoords, content, color);
                        var newLink = {
                            "pline" :   l,
                            "metric":   {
                                    "rssi"  : rssi,
                                    "pdr"   : pdr,
                                }
                        }
                        links.push(newLink);
                    }
                }
            }
            else {console.log("ID not found:" + neighbor.neighborId);}
        }
    }
}

//----------------Interface Listeners ---------------------------------------//

$(document).ready(function() {
    $("#next_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // add 5 minutes
        var newDate = new Date(selectedTime.getTime() + 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0);
    });
});

$(document).ready(function() {
    $("#prev_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // remove 5 minutes
        var newDate = new Date(selectedTime.getTime() - 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0)
    });
});

//------------------ Helpers ------------------------------------------------//

function clearLinks(){
    // remove links
    for (var i=0; i<links.length; i++) {
        if (i in links){
            links[i].pline.setMap(null);
        }
    }
    links = [];
    links.length = 0;
}

function getLinkColor(rssi, pdr){
    var metric = $("#metric").val()

    if (metric == "rssi"){
        if (rssi > GOOD_LINK_RSSI)
            return GOOD_LINK_COLOR;
        else if (rssi >= MEDIUM_LINK_RSSI && rssi <= GOOD_LINK_RSSI)
            return MEDIUM_LINK_COLOR;
        else
            return BAD_LINK_COLOR;
    }
    else if (metric == "pdr"){
        if (pdr == DEFAULT_PDR)
            return UNKNOWN_LINK_COLOR;
        if (pdr >= GOOD_LINK_PDR)
            return GOOD_LINK_COLOR;
        else if (pdr > MEDIUM_LINK_PDR && pdr < GOOD_LINK_PDR)
            return MEDIUM_LINK_COLOR;
    }
}

function getMoteColor(board){
    if (board == null)
        board = "unknown"
    for (var color in board_colors){
        if (board_colors[color] === null){
            board_colors[color] = board;
            return color;
        }
        else if (board_colors[color] === board){
            return color
        }
    }
    // return default color if board_color is too small
    return "#FFF"
}

function updateMote(mac, lat, lng, board){
    var moteId  = null;
    var color   = getMoteColor(board)
    for (var i=0; i<motes.length; i++) {
        if (i in motes){
            if (motes[i].mac == mac){
                var content = mac + "
"
                if (board != null)
                  content += board + "
"
                if (motes[i].marker == null){
                    motes[i].marker = createMarker(lat, lng, content, color);
                } else if (motes[i].marker.position.lat() != lat ||
                            motes[i].marker.position.lng() != lng) {
                    motes[i].marker.setMap(null);
                    motes[i].marker = createMarker(lat, lng, content, color);
                }
            }
        }
    }
}

function createMarker(lat, lng, content, color){
    // create Markers
    var myLatLng = new google.maps.LatLng(lat, lng)
    var marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
        icon: pinSymbol(color),
    });
    // add popup listener
    infoBox(map, marker, content);
    return marker;
}

function createPolyline(lineCoordinates, content, color){
    var line = new google.maps.Polyline({
          path: lineCoordinates,
          geodesic: true,
          strokeColor: color,
          strokeWeight: 2
    });
    if (color != UNKNOWN_LINK_COLOR){
        line.setMap(map);
        infoBox(map, line, content);
    }
    return line;
}

function getLink(LatLng1, LatLng2){
    for (i=0; i<links.length; i++) {
        var link = links[i].pline;
        var coord1 = "("+link.getPath().getAt(0).lat()+", "+link.getPath().getAt(0).lng()+")"
        var coord2 = "("+link.getPath().getAt(1).lat()+", "+link.getPath().getAt(1).lng()+")"
        if ( ((LatLng1.toString() == coord1) && (LatLng2.toString() == coord2)) ||
             ((LatLng2.toString() == coord1) && (LatLng1.toString() == coord2))
           ){
            return links[i]
        }
    }
    return null
}

function getMoteFromId(moteId){
    if (motes[moteId].marker != null)
        return motes[moteId].marker.position;
    else
        return null;
}

function getLocationFromId(moteId){
    if (motes[moteId].marker != null)
        return motes[moteId].marker.position;
    else
        return null;
}

function getLocationFromMac(mac){
    for( i=0; i<motes.length; i++ ) {
        if (i in motes && motes[i].mac == mac){
            return motes[i].marker.position;
        }
    }
    console.log("MAC not found:" + mac)
    return null;
}

// calculates distance between two points in meters
function calcDistance(p1, p2) {
    return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2)).toFixed(2);
}

/*Function that shows a window with the content or info
 about the marker (sensor location)*/

function infoBox(map, marker, content) {
    infoWindow = new google.maps.InfoWindow();
    // Attaching a click event to the current marker
    google.maps.event.addListener(marker, "click", function(e) {
        infoWindow.setPosition(e.latLng);
        infoWindow.setContent(content);
        infoWindow.open(map, marker);
        var iw_container = $(".gm-style-iw").parent();
        iw_container.stop().hide();
        iw_container.fadeIn(300);
    });
}

// Function that create a vectorial image of a marker with the given color
function pinSymbol(color) {
    return {
        path: 'M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z M -2,-30 a 2,2 0 1,1 4,0 2,2 0 1,1 -4,0',
        fillColor: color,
        fillOpacity: 1,
        strokeColor: '#000',
        strokeWeight: 2,
        scale: 1,
   };
}
<commit_msg>#53 using snapshot to create mng marker<commit_after>// LINK LIMITS
var GOOD_LINK_RSSI      = -70;
var MEDIUM_LINK_RSSI    = -80;
var GOOD_LINK_PDR       = 80;
var MEDIUM_LINK_PDR     = 50;

// LINK COLOR
var GOOD_LINK_COLOR     = "#00ff00"     // green
var MEDIUM_LINK_COLOR   = "#ffff00"     // orange
var BAD_LINK_COLOR      = "#ffff00"     // red
var UNKNOWN_LINK_COLOR  = "#ffffff"     // white

// Global variables
var map;
var motes   = [];         // a list of motes, indexed by id
var links   = [];         // a list of links
var timeout;
var infoWindow;
var SITE_TIME_OFFSET    = -3
var DEFAULT_PDR         = 101 // init to impossible value
var board_colors        = {"#c33c1c":null,"#dbd60d":null,"#acd2cd":null,"#ff9966":null}

//------------------ Init functions ------------------------------------------//

function initMap() {
    map = new google.maps.Map(document.getElementById('map'), {
        center: new google.maps.LatLng(0, 0),
        zoom: 18,
        scaleControl: true,
    });
    map.setMapTypeId(google.maps.MapTypeId.SATELLITE);

    // set date
    $( "#datepicker" ).datepicker();
    $( "#datepicker" ).datepicker('setDate', 'today');

    // set time
    defaultDate = new Date();
    defaultDate.setTime(defaultDate.getTime());
    $('#timepicker').timepicker({
        showNowButton: true,
        showDeselectButton: true,
        showCloseButton: true,
    });
    $('#timepicker').timepicker('setTime', defaultDate.toLocaleTimeString(
                                'es-AR', { hour: "numeric",
                                minute: "numeric"}));
    load_data(1);
}

function load_data(loop){
    clearLinks();

    // set default time to UTC time
    var date        = $("#datepicker").datepicker('getDate');
    var time        = $("#timepicker").timepicker('getTime').split(':');
    var localOffset = new Date().getTimezoneOffset()/60;
    date.setHours(parseInt(time[0]), time[1]);

    // get host and site name
    var host        = window.location.origin
    var path        = window.location.pathname.split("/map")[0]
    var sitename    = $("#sitename").val()

    // display motes and links
    get_motes(host, path, sitename, date);
    var delay = function() { get_links(host, path, sitename, date) };
    window.setTimeout(delay,500);

    if (loop == 1)
        timeout = setTimeout(load_data, 30000);
    else
        clearTimeout(timeout)
}

//------------------ Main functions ------------------------------------------//

// request the server for mote information
function get_motes(host, path, sitename, date){
    var solType     = "SOL_TYPE_DUST_SNAPSHOT";
    var encType     = encodeURIComponent(solType);
    var isoTime     = date.toISOString();
    var encTime     = encodeURIComponent(isoTime);
    $.getJSON(host+ path +
        "/api/v1/jsonp/"+sitename+"/" + encType +
        "/time/" + encTime, create_motes);
}

// request the server for links information
function get_links(host, path, sitename, date){
    var solType     = "SOL_TYPE_DUST_NOTIF_HRNEIGHBORS";
    var encType     = encodeURIComponent(solType);
    var isoTime     = date.toISOString();
    var encTime     = encodeURIComponent(isoTime);
    $.getJSON(host+ path +
        "/api/v1/jsonp/"+sitename+"/" + encType +
        "/time/" + encTime, create_links);
}

// populate the motes list
function create_motes(data){
    if (Object.keys(data).length > 0) {

        // manager
        var color   = getMoteColor(data[0].board);
        var mark = createMarker(
            data[0].latitude,
            data[0].longitude,
            "Manager
"+data[0].mac+"
"+data[0].board,
            color
            );

        motes[1] = {
            "mac"       : data[0].mac,
            "marker"    : mark,
        }

        // motes
        mote_list = data[0].value.mote
        for (var i=0; i < Object.keys(mote_list).length; i++) {
            // populate table
            motes[mote_list[i].moteId] = {
                "mac"       : mote_list[i].macAddress,
                "marker"    : null
            }
        }
    }
}

function create_links(data){
    var bounds = new google.maps.LatLngBounds();
    var loc;

    // update motes location and board type
    for (var i=0; i < data.length; i++) {
        loc = new google.maps.LatLng(
            data[i].value.latitude,
            data[i].value.longitude
        );
        bounds.extend(loc);
        updateMote(
            data[i].mac,
            data[i].value.latitude,
            data[i].value.longitude,
            data[i].value.board);
    }
    map.fitBounds(bounds);
    map.panToBounds(bounds);

    // create links
    for (var i=0; i < data.length; i++) {
        for (var n_id in data[i].value.neighbors){
            var neighbor = data[i].value.neighbors[n_id]
            if (neighbor.neighborId in motes){
                var crd1 = getLocationFromMac(data[i].mac);
                var crd2 = getLocationFromId(neighbor.neighborId)
                if (crd1 != null && crd2 != null){
                    var lineCoords = [crd1, crd2];
                    var link    = getLink(crd1, crd2);

                    // get metric
                    var pdr = DEFAULT_PDR //init to unpossible value
                    if (neighbor.numTxPackets > 0){
                        pdr     = ((neighbor.numTxPackets - neighbor.numTxFailures)
                                    /neighbor.numTxPackets
                                ) * 100;
                    }
                    var rssi    = neighbor.rssi;

                    // update link if already exists
                    if (link != null){
                        link.metric.rssi    = Math.min(rssi, link.metric.rssi)
                        link.metric.pdr     = Math.min(pdr, link.metric.pdr)

                        link.pline.setMap(null);
                        var color   = getLinkColor(link.metric.rssi, link.metric.pdr);
                        var dist    = calcDistance(lineCoords[0],lineCoords[1])
                        // set line parameters
                        if (link.metric.pdr == DEFAULT_PDR)
                          s_pdr = "No Tx"
                        else
                          s_pdr = link.metric.pdr + "%"
                        var content =   "RSSI: " + rssi + "dBm
" +
                                        "PDR: " + s_pdr + "
" +
                                        "Distance: " + dist + "m"
                        link.pline  = createPolyline(lineCoords, content, color);
                    } // create link if it does not already exists
                    else {
                        var color   = getLinkColor(rssi, pdr);
                        var dist    = calcDistance(lineCoords[0],lineCoords[1])
                        // set line parameters
                        // set line parameters
                        if (pdr == DEFAULT_PDR)
                          s_pdr = "No Tx"
                        else
                          s_pdr = pdr + "%"
                        var content =   "RSSI: " + rssi + "dBm
" +
                                        "PDR: " + s_pdr + "
" +
                                        "Distance: " + dist + "m"
                        var l = createPolyline(lineCoords, content, color);
                        var newLink = {
                            "pline" :   l,
                            "metric":   {
                                    "rssi"  : rssi,
                                    "pdr"   : pdr,
                                }
                        }
                        links.push(newLink);
                    }
                }
            }
            else {console.log("ID not found:" + neighbor.neighborId);}
        }
    }
}

//----------------Interface Listeners ---------------------------------------//

$(document).ready(function() {
    $("#next_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // add 5 minutes
        var newDate = new Date(selectedTime.getTime() + 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0);
    });
});

$(document).ready(function() {
    $("#prev_time").click(function(event) {
        var selectedTime =  $('#timepicker').timepicker('getTimeAsDate');
        // remove 5 minutes
        var newDate = new Date(selectedTime.getTime() - 5*60000)
        str_date = newDate.getHours().toString() + ":" + newDate.getMinutes().toString();
        $('#timepicker').timepicker('setTime', str_date);
        load_data(0)
    });
});

//------------------ Helpers ------------------------------------------------//

function clearLinks(){
    // remove links
    for (var i=0; i<links.length; i++) {
        if (i in links){
            links[i].pline.setMap(null);
        }
    }
    links = [];
    links.length = 0;
}

function getLinkColor(rssi, pdr){
    var metric = $("#metric").val()

    if (metric == "rssi"){
        if (rssi > GOOD_LINK_RSSI)
            return GOOD_LINK_COLOR;
        else if (rssi >= MEDIUM_LINK_RSSI && rssi <= GOOD_LINK_RSSI)
            return MEDIUM_LINK_COLOR;
        else
            return BAD_LINK_COLOR;
    }
    else if (metric == "pdr"){
        if (pdr == DEFAULT_PDR)
            return UNKNOWN_LINK_COLOR;
        if (pdr >= GOOD_LINK_PDR)
            return GOOD_LINK_COLOR;
        else if (pdr > MEDIUM_LINK_PDR && pdr < GOOD_LINK_PDR)
            return MEDIUM_LINK_COLOR;
    }
}

function getMoteColor(board){
    if (board == null)
        board = "unknown"
    for (var color in board_colors){
        if (board_colors[color] === null){
            board_colors[color] = board;
            return color;
        }
        else if (board_colors[color] === board){
            return color
        }
    }
    // return default color if board_color is too small
    return "#FFF"
}

function updateMote(mac, lat, lng, board){
    var moteId  = null;
    var color   = getMoteColor(board)
    for (var i=0; i<motes.length; i++) {
        if (i in motes){
            if (motes[i].mac == mac){
                var content = mac + "
"
                if (board != null)
                  content += board + "
"
                if (motes[i].marker == null){
                    motes[i].marker = createMarker(lat, lng, content, color);
                } else if (motes[i].marker.position.lat() != lat ||
                            motes[i].marker.position.lng() != lng) {
                    motes[i].marker.setMap(null);
                    motes[i].marker = createMarker(lat, lng, content, color);
                }
            }
        }
    }
}

function createMarker(lat, lng, content, color){
    // create Markers
    var myLatLng = new google.maps.LatLng(lat, lng)
    var marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
        icon: pinSymbol(color),
    });
    // add popup listener
    infoBox(map, marker, content);
    return marker;
}

function createPolyline(lineCoordinates, content, color){
    var line = new google.maps.Polyline({
          path: lineCoordinates,
          geodesic: true,
          strokeColor: color,
          strokeWeight: 2
    });
    if (color != UNKNOWN_LINK_COLOR){
        line.setMap(map);
        infoBox(map, line, content);
    }
    return line;
}

function getLink(LatLng1, LatLng2){
    for (i=0; i<links.length; i++) {
        var link = links[i].pline;
        var coord1 = "("+link.getPath().getAt(0).lat()+", "+link.getPath().getAt(0).lng()+")"
        var coord2 = "("+link.getPath().getAt(1).lat()+", "+link.getPath().getAt(1).lng()+")"
        if ( ((LatLng1.toString() == coord1) && (LatLng2.toString() == coord2)) ||
             ((LatLng2.toString() == coord1) && (LatLng1.toString() == coord2))
           ){
            return links[i]
        }
    }
    return null
}

function getMoteFromId(moteId){
    if (motes[moteId].marker != null)
        return motes[moteId].marker.position;
    else
        return null;
}

function getLocationFromId(moteId){
    if (motes[moteId].marker != null)
        return motes[moteId].marker.position;
    else
        return null;
}

function getLocationFromMac(mac){
    for( i=0; i<motes.length; i++ ) {
        if (i in motes && motes[i].mac == mac){
            return motes[i].marker.position;
        }
    }
    console.log("MAC not found:" + mac)
    return null;
}

// calculates distance between two points in meters
function calcDistance(p1, p2) {
    return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2)).toFixed(2);
}

/*Function that shows a window with the content or info
 about the marker (sensor location)*/

function infoBox(map, marker, content) {
    infoWindow = new google.maps.InfoWindow();
    // Attaching a click event to the current marker
    google.maps.event.addListener(marker, "click", function(e) {
        infoWindow.setPosition(e.latLng);
        infoWindow.setContent(content);
        infoWindow.open(map, marker);
        var iw_container = $(".gm-style-iw").parent();
        iw_container.stop().hide();
        iw_container.fadeIn(300);
    });
}

// Function that create a vectorial image of a marker with the given color
function pinSymbol(color) {
    return {
        path: 'M 0,0 C -2,-20 -10,-22 -10,-30 A 10,10 0 1,1 10,-30 C 10,-22 2,-20 0,0 z M -2,-30 a 2,2 0 1,1 4,0 2,2 0 1,1 -4,0',
        fillColor: color,
        fillOpacity: 1,
        strokeColor: '#000',
        strokeWeight: 2,
        scale: 1,
   };
}
<|endoftext|>