<commit_before>// @author         Fly33
// @name           Fly Links
// @category       Draw
// @version        0.5.0
// @description    Calculate how to link the portals to create the largest tidy set of nested fields. Enable from the layer chooser.

// use own namespace for plugin
window.plugin.flyLinks = function() {};

// const values
window.plugin.flyLinks.MAX_PORTALS_TO_LINK = 100;

window.plugin.flyLinks.linksLayerGroup = null;
window.plugin.flyLinks.fieldsLayerGroup = null;

window.plugin.flyLinks.updateLayer = function() {
  if (!window.map.hasLayer(window.plugin.flyLinks.linksLayerGroup) &&
      !window.map.hasLayer(window.plugin.flyLinks.fieldsLayerGroup)) {
    return;
  }

  window.plugin.flyLinks.linksLayerGroup.clearLayers();
  window.plugin.flyLinks.fieldsLayerGroup.clearLayers();
  var ctrl = [$('.leaflet-control-layers-selector + span:contains("Fly links")').parent(),
              $('.leaflet-control-layers-selector + span:contains("Fly fields")').parent()];

  var distance = function(a, b) {
    return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
  };

  var drawLink = function(a, b, style) {
    var poly = L.polyline([a.latlng, b.latlng], style);
    poly.addTo(window.plugin.flyLinks.linksLayerGroup);
  };

  var drawField = function(a, b, c, style) {
    var poly = L.polygon([a.latlng, b.latlng, c.latlng], style);
    poly.addTo(window.plugin.flyLinks.fieldsLayerGroup);
  };

  var EPS = 1e-9;
  var det = function(a, b, c) {
    return a.x * b.y - a.y * b.x + b.x * c.y - b.y * c.x + c.x * a.y - c.y * a.x;
  };

  var convexHull = function(points) {
    if (points.length < 3) {
      return [];
    }
    var result = [];
    var func = function _func(ai, bi, index) {
      var maxd = 0;
      var maxdi = -1;
      var a = points[ai];
      var b = points[bi];
      var _index = [];
      for (var i = 0; i < index.length; ++i) {
        var c = points[index[i]];
        var d = -det(a, b, c);
        if (d > EPS) {
            _index.push(index[i]);
        }
        if (maxd < d - EPS) {
          maxd = d;
          maxdi = index[i];
        }
      }
      if (maxdi != -1) {
        _func(ai, maxdi, _index);
        _func(maxdi, bi, _index);
      } else {
        result.push(ai);
      }
    };
    var minxi = 0;
    var maxxi = 0;
    var index = [];
    for (var i = 0; i < points.length; ++i) {
      index.push(i);
      if (points[minxi].x > points[i].x) {
        minxi = i;
      }
      if (points[maxxi].x < points[i].x) {
        maxxi = i;
      }
    }
    func(minxi, maxxi, index);
    func(maxxi, minxi, index);
    return result;
  };

  var triangulate2 = function(index, line_indexes, line_edge_indexes, locations) {
    if (index.length == 0) {
      return {edges: [], triangles: []};
    }
    var data = [];
    var subtriangulate = function _subtriangulate(ai, bi, ci, index) {
      var _i = [ai, bi, ci].sort(function(a,b){return a-b;});
      if (data[_i[0]] === undefined) {
        data[_i[0]] = [];
      }
      if (data[_i[0]][_i[1]-_i[0]] === undefined) {
        data[_i[0]][_i[1]-_i[0]] = [];
      }
      if (data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]] === undefined) {
        var _index = [];
        for (var i = 0; i < index.length; ++i) {
          var detc = det(locations[ai], locations[bi], locations[index[i]]);
          var deta = det(locations[bi], locations[ci], locations[index[i]]);
          var detb = det(locations[ci], locations[ai], locations[index[i]]);
          if (deta > EPS && detb > EPS && detc > EPS) {
            _index.push(index[i]);
          }
        }
        var besth = 0;
        var besthi = -1;
        var i;
        for (i = 0; i < line_indexes.length; ++i) {
          var f0 = _index.indexOf(line_indexes[i][0]) != -1;
          var f1 = _index.indexOf(line_indexes[i][1]) != -1;
          if (f0 && !f1 && _i.indexOf(line_indexes[i][1]) == -1) {
            break;
          }
          if (f1 && !f0 && _i.indexOf(line_indexes[i][0]) == -1) {
            break;
          }
        }
        if (i < line_indexes.length) {
          besth = 0;
          besthi = -1;
        } else if (_index.length == 0) {
          var a = locations[ai];
          var b = locations[bi];
          var c = locations[ci];
          var s = Math.abs(det(a, b, c));
          var ch = s / distance(a, b);
          var ah = s / distance(b, c);
          var bh = s / distance(c, a);
          besth = Math.min(ah, bh, ch);
          besthi = -1;
        } else {
          var besths = 0;
          for (var i = 0; i < _index.length; ++i) {
            var ch = _subtriangulate(ai, bi, _index[i], _index);
            var ah = _subtriangulate(bi, ci, _index[i], _index);
            var bh = _subtriangulate(ci, ai, _index[i], _index);
            var _besth = Math.min(ah, bh, ch);
            var _besths = ah + bh + ch;
            if (besth < _besth || Math.abs(besth - _besth) <= EPS && besths < _besths) {
              besth = _besth;
              besths = _besths;
              besthi = _index[i];
            }
          }
        }
        data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]] = {height: besth, index: besthi};
      }
      return data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].height;
    };
    var subindex = [];
    for (var i = 0; i < locations.length; ++i) {
      subindex.push(i);
    }
    var best = [];
    best[1] = [];
    for (var k = 0; k < index.length - 1; ++k) {
      best[1][k] = {height: Infinity, length: -1};
    }
    for (var len = 2; len <= index.length - 1; ++len) {
      best[len] = [];
      for (var k = 0; k < index.length - len; ++k) {
        var t = 0;
        var tlen = -1;
        var i;
        for (i = 0; i < line_edge_indexes.length; ++i) {
          var [i0, i1] = line_edge_indexes[i];
          if (k < i0 && i0 < k+len && (i1 < k || k+len < i1) ||
              k < i1 && i1 < k+len && (i0 < k || k+len < i0)) {
            break;
          }
        }
        if (i >= line_edge_indexes.length) {
          for (var _len = 1; _len <= len - 1; ++_len) {
            var _t = Math.min(best[_len][k].height, best[len-_len][k+_len].height, subtriangulate(index[k], index[k+_len], index[k+len], subindex));
            if (t < _t) {
              t = _t;
              tlen = _len;
            }
          }
        }
        best[len][k] = {height: t, length: tlen};
      }
    }

    var edges = [];
    var triangles = [];
    var makesubtriangulation = function _makesubtriangulation(ai, bi, ci, depth) {
      var _i = [ai, bi, ci].sort(function(a,b){return a-b;});
      if (data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index == -1) {
        triangles.push(new window.plugin.flyLinks.Triangle(locations[ai], locations[bi], locations[ci], depth));
      } else {
        _makesubtriangulation(ai, bi, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
        _makesubtriangulation(bi, ci, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
        _makesubtriangulation(ci, ai, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
        edges.push(new window.plugin.flyLinks.Edge(locations[ai], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
        edges.push(new window.plugin.flyLinks.Edge(locations[bi], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
        edges.push(new window.plugin.flyLinks.Edge(locations[ci], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
      }
    };
    var maketriangulation = function _maketriangulation(len, a) {
      edges.push(new window.plugin.flyLinks.Edge(locations[index[a]], locations[index[a+len]], 0));
      if (best[len][a].length == -1) {
        return;
      }
      makesubtriangulation(index[a], index[a+best[len][a].length], index[a+len], 1);
      _maketriangulation(best[len][a].length, a);
      _maketriangulation(len - best[len][a].length, a + best[len][a].length);
    };
    if (best[index.length - 1][0].height > 0) {
      maketriangulation(index.length - 1, 0);
    } else {
      console.log("Fly links: no triangulation");
    }
    return {edges: edges, triangles: triangles};
  };

  var triangulate = function(locations, lines) {
    var index = convexHull(locations);
    var line_indexes = filterLines(locations, lines);
    var line_edge_indexes = [];
    for (var i = 0; i < line_indexes.length; ++i) {
      var i0 = index.indexOf(line_indexes[i][0]);
      var i1 = index.indexOf(line_indexes[i][1]);
      if (i0 == -1 || i1 == -1) {
        continue;
      }
      line_edge_indexes.push([i0, i1]);
    }
    return triangulate2(index, line_indexes, line_edge_indexes, locations);
  };

  var edges = [];
  var triangles = [];

  var lines = [];
  if (plugin.drawTools) {
    var polylines = [];
    plugin.drawTools.drawnItems.eachLayer(function(layer) {
      if (layer instanceof L.GeodesicPolyline) {
        polylines.push(layer);
      }
    });
    polylines.forEach(function (polyline) {
      if (!polyline._rings) { return; }
      var p = polyline._rings[0];
      for (var j = 1; j < p.length; ++j) {
        lines.push([p[j-1], p[j]]);
      }
    });
  }

  var filterLines = function(points, lines) {
    var result = [];
    var findPoint = function(points, point) {
      for (var i = 0; i < points.length; ++i) {
        if (points[i].x === point.x && points[i].y === point.y) {
          return i;
        }
      }
      return -1;
    };
    for (var i = 0; i < lines.length; ++i) {
      var a = findPoint(points, lines[i][0]);
      if (a == -1) {
        continue;
      }
      var b = findPoint(points, lines[i][1]);
      if (b == -1) {
        continue;
      }
      result.push([a, b]);
    }
    return result;
  };

  var filters = plugin.drawTools && plugin.drawTools.getLocationFilters && plugin.drawTools.getLocationFilters();
  // fallback to map bounds if no drawn polygon (or no drawtools)
  if (!filters || !filters.length) {
    var bounds = map.getBounds();
    filters = [function (p) {
      return bounds.contains(p.getLatLng());
    }];
  }

  filters.forEach(function (filter) {
    var points = [];
    for (var guid in window.portals) {
      var location = window.portals[guid];
      if (filter(location)) {
        var point = location._point.clone();
        point.latlng = location._latlng;
        points.push(point);
      }
    }
    if (points.length >= window.plugin.flyLinks.MAX_PORTALS_TO_LINK) {
      //alert("Some polygon contains more than 100 portals.");
      return;
    }
    if (!points.length) return;
    var triangulation = triangulate(points, lines);
    edges = edges.concat(triangulation.edges);
    triangles = triangles.concat(triangulation.triangles);
  });

  $.each(edges, function(idx, edge) {
    drawLink(edge.a, edge.b, {
      color: '#FF0000',
      opacity: 1,
      weight: 1.5,
      interactive: false,
      smoothFactor: 10,
      dashArray: [6, 4],
    });
  });

  $.each(triangles, function(idx, triangle) {
    drawField(triangle.a, triangle.b, triangle.c, {
      stroke: false,
      fill: true,
      fillColor: '#FF0000',
      fillOpacity: 1 - Math.pow(0.85, triangle.depth),
      interactive: false,
    });
  });
};

window.plugin.flyLinks.Edge = function(a, b, depth) {
  this.a = a;
  this.b = b;
  this.depth = depth;
};

window.plugin.flyLinks.Triangle = function(a, b, c, depth) {
  this.a = a;
  this.b = b;
  this.c = c;
  this.depth = depth;
};

window.plugin.flyLinks.setup = function() {
  window.plugin.flyLinks.linksLayerGroup = new L.LayerGroup();
  window.plugin.flyLinks.fieldsLayerGroup = new L.LayerGroup();

  function update () {
    if (!map.hasLayer(window.plugin.flyLinks.linksLayerGroup) ||
        !map.hasLayer(window.plugin.flyLinks.fieldsLayerGroup)) {
      window.plugin.flyLinks.updateLayer();
    }
  }
  window.plugin.flyLinks.linksLayerGroup.on('add', update);
  window.plugin.flyLinks.fieldsLayerGroup.on('add', update);

  window.addHook('mapDataRefreshEnd', function(e) {
    window.plugin.flyLinks.updateLayer();
  });

  window.map.on('moveend', function() {
    window.plugin.flyLinks.updateLayer();
  });

  if (window.plugin.drawTools && window.plugin.drawTools.filterEvents) {
    window.plugin.drawTools.filterEvents.on('changed', window.plugin.flyLinks.updateLayer);
  }

  window.addLayerGroup('Fly links', window.plugin.flyLinks.linksLayerGroup, false);
  window.addLayerGroup('Fly fields', window.plugin.flyLinks.fieldsLayerGroup, false);
};
var setup = window.plugin.flyLinks.setup;
<commit_msg>fly-links: lint (eqeqeq)<commit_after>// @author         Fly33
// @name           Fly Links
// @category       Draw
// @version        0.5.0
// @description    Calculate how to link the portals to create the largest tidy set of nested fields. Enable from the layer chooser.

// use own namespace for plugin
window.plugin.flyLinks = function() {};

// const values
window.plugin.flyLinks.MAX_PORTALS_TO_LINK = 100;

window.plugin.flyLinks.linksLayerGroup = null;
window.plugin.flyLinks.fieldsLayerGroup = null;

window.plugin.flyLinks.updateLayer = function() {
  if (!window.map.hasLayer(window.plugin.flyLinks.linksLayerGroup) &&
      !window.map.hasLayer(window.plugin.flyLinks.fieldsLayerGroup)) {
    return;
  }

  window.plugin.flyLinks.linksLayerGroup.clearLayers();
  window.plugin.flyLinks.fieldsLayerGroup.clearLayers();
  var ctrl = [$('.leaflet-control-layers-selector + span:contains("Fly links")').parent(),
              $('.leaflet-control-layers-selector + span:contains("Fly fields")').parent()];

  var distance = function(a, b) {
    return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
  };

  var drawLink = function(a, b, style) {
    var poly = L.polyline([a.latlng, b.latlng], style);
    poly.addTo(window.plugin.flyLinks.linksLayerGroup);
  };

  var drawField = function(a, b, c, style) {
    var poly = L.polygon([a.latlng, b.latlng, c.latlng], style);
    poly.addTo(window.plugin.flyLinks.fieldsLayerGroup);
  };

  var EPS = 1e-9;
  var det = function(a, b, c) {
    return a.x * b.y - a.y * b.x + b.x * c.y - b.y * c.x + c.x * a.y - c.y * a.x;
  };

  var convexHull = function(points) {
    if (points.length < 3) {
      return [];
    }
    var result = [];
    var func = function _func(ai, bi, index) {
      var maxd = 0;
      var maxdi = -1;
      var a = points[ai];
      var b = points[bi];
      var _index = [];
      for (var i = 0; i < index.length; ++i) {
        var c = points[index[i]];
        var d = -det(a, b, c);
        if (d > EPS) {
            _index.push(index[i]);
        }
        if (maxd < d - EPS) {
          maxd = d;
          maxdi = index[i];
        }
      }
      if (maxdi !== -1) {
        _func(ai, maxdi, _index);
        _func(maxdi, bi, _index);
      } else {
        result.push(ai);
      }
    };
    var minxi = 0;
    var maxxi = 0;
    var index = [];
    for (var i = 0; i < points.length; ++i) {
      index.push(i);
      if (points[minxi].x > points[i].x) {
        minxi = i;
      }
      if (points[maxxi].x < points[i].x) {
        maxxi = i;
      }
    }
    func(minxi, maxxi, index);
    func(maxxi, minxi, index);
    return result;
  };

  var triangulate2 = function(index, line_indexes, line_edge_indexes, locations) {
    if (index.length === 0) {
      return {edges: [], triangles: []};
    }
    var data = [];
    var subtriangulate = function _subtriangulate(ai, bi, ci, index) {
      var _i = [ai, bi, ci].sort(function(a,b){return a-b;});
      if (data[_i[0]] === undefined) {
        data[_i[0]] = [];
      }
      if (data[_i[0]][_i[1]-_i[0]] === undefined) {
        data[_i[0]][_i[1]-_i[0]] = [];
      }
      if (data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]] === undefined) {
        var _index = [];
        for (var i = 0; i < index.length; ++i) {
          var detc = det(locations[ai], locations[bi], locations[index[i]]);
          var deta = det(locations[bi], locations[ci], locations[index[i]]);
          var detb = det(locations[ci], locations[ai], locations[index[i]]);
          if (deta > EPS && detb > EPS && detc > EPS) {
            _index.push(index[i]);
          }
        }
        var besth = 0;
        var besthi = -1;
        var i;
        for (i = 0; i < line_indexes.length; ++i) {
          var f0 = _index.indexOf(line_indexes[i][0]) !== -1;
          var f1 = _index.indexOf(line_indexes[i][1]) !== -1;
          if (f0 && !f1 && _i.indexOf(line_indexes[i][1]) === -1) {
            break;
          }
          if (f1 && !f0 && _i.indexOf(line_indexes[i][0]) === -1) {
            break;
          }
        }
        if (i < line_indexes.length) {
          besth = 0;
          besthi = -1;
        } else if (_index.length === 0) {
          var a = locations[ai];
          var b = locations[bi];
          var c = locations[ci];
          var s = Math.abs(det(a, b, c));
          var ch = s / distance(a, b);
          var ah = s / distance(b, c);
          var bh = s / distance(c, a);
          besth = Math.min(ah, bh, ch);
          besthi = -1;
        } else {
          var besths = 0;
          for (var i = 0; i < _index.length; ++i) {
            var ch = _subtriangulate(ai, bi, _index[i], _index);
            var ah = _subtriangulate(bi, ci, _index[i], _index);
            var bh = _subtriangulate(ci, ai, _index[i], _index);
            var _besth = Math.min(ah, bh, ch);
            var _besths = ah + bh + ch;
            if (besth < _besth || Math.abs(besth - _besth) <= EPS && besths < _besths) {
              besth = _besth;
              besths = _besths;
              besthi = _index[i];
            }
          }
        }
        data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]] = {height: besth, index: besthi};
      }
      return data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].height;
    };
    var subindex = [];
    for (var i = 0; i < locations.length; ++i) {
      subindex.push(i);
    }
    var best = [];
    best[1] = [];
    for (var k = 0; k < index.length - 1; ++k) {
      best[1][k] = {height: Infinity, length: -1};
    }
    for (var len = 2; len <= index.length - 1; ++len) {
      best[len] = [];
      for (var k = 0; k < index.length - len; ++k) {
        var t = 0;
        var tlen = -1;
        var i;
        for (i = 0; i < line_edge_indexes.length; ++i) {
          var [i0, i1] = line_edge_indexes[i];
          if (k < i0 && i0 < k+len && (i1 < k || k+len < i1) ||
              k < i1 && i1 < k+len && (i0 < k || k+len < i0)) {
            break;
          }
        }
        if (i >= line_edge_indexes.length) {
          for (var _len = 1; _len <= len - 1; ++_len) {
            var _t = Math.min(best[_len][k].height, best[len-_len][k+_len].height, subtriangulate(index[k], index[k+_len], index[k+len], subindex));
            if (t < _t) {
              t = _t;
              tlen = _len;
            }
          }
        }
        best[len][k] = {height: t, length: tlen};
      }
    }

    var edges = [];
    var triangles = [];
    var makesubtriangulation = function _makesubtriangulation(ai, bi, ci, depth) {
      var _i = [ai, bi, ci].sort(function(a,b){return a-b;});
      if (data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index === -1) {
        triangles.push(new window.plugin.flyLinks.Triangle(locations[ai], locations[bi], locations[ci], depth));
      } else {
        _makesubtriangulation(ai, bi, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
        _makesubtriangulation(bi, ci, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
        _makesubtriangulation(ci, ai, data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index, depth+1);
        edges.push(new window.plugin.flyLinks.Edge(locations[ai], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
        edges.push(new window.plugin.flyLinks.Edge(locations[bi], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
        edges.push(new window.plugin.flyLinks.Edge(locations[ci], locations[data[_i[0]][_i[1]-_i[0]][_i[2]-_i[1]].index], depth));
      }
    };
    var maketriangulation = function _maketriangulation(len, a) {
      edges.push(new window.plugin.flyLinks.Edge(locations[index[a]], locations[index[a+len]], 0));
      if (best[len][a].length === -1) {
        return;
      }
      makesubtriangulation(index[a], index[a+best[len][a].length], index[a+len], 1);
      _maketriangulation(best[len][a].length, a);
      _maketriangulation(len - best[len][a].length, a + best[len][a].length);
    };
    if (best[index.length - 1][0].height > 0) {
      maketriangulation(index.length - 1, 0);
    } else {
      console.log("Fly links: no triangulation");
    }
    return {edges: edges, triangles: triangles};
  };

  var triangulate = function(locations, lines) {
    var index = convexHull(locations);
    var line_indexes = filterLines(locations, lines);
    var line_edge_indexes = [];
    for (var i = 0; i < line_indexes.length; ++i) {
      var i0 = index.indexOf(line_indexes[i][0]);
      var i1 = index.indexOf(line_indexes[i][1]);
      if (i0 === -1 || i1 === -1) {
        continue;
      }
      line_edge_indexes.push([i0, i1]);
    }
    return triangulate2(index, line_indexes, line_edge_indexes, locations);
  };

  var edges = [];
  var triangles = [];

  var lines = [];
  if (plugin.drawTools) {
    var polylines = [];
    plugin.drawTools.drawnItems.eachLayer(function(layer) {
      if (layer instanceof L.GeodesicPolyline) {
        polylines.push(layer);
      }
    });
    polylines.forEach(function (polyline) {
      if (!polyline._rings) { return; }
      var p = polyline._rings[0];
      for (var j = 1; j < p.length; ++j) {
        lines.push([p[j-1], p[j]]);
      }
    });
  }

  var filterLines = function(points, lines) {
    var result = [];
    var findPoint = function(points, point) {
      for (var i = 0; i < points.length; ++i) {
        if (points[i].x === point.x && points[i].y === point.y) {
          return i;
        }
      }
      return -1;
    };
    for (var i = 0; i < lines.length; ++i) {
      var a = findPoint(points, lines[i][0]);
      if (a === -1) {
        continue;
      }
      var b = findPoint(points, lines[i][1]);
      if (b === -1) {
        continue;
      }
      result.push([a, b]);
    }
    return result;
  };

  var filters = plugin.drawTools && plugin.drawTools.getLocationFilters && plugin.drawTools.getLocationFilters();
  // fallback to map bounds if no drawn polygon (or no drawtools)
  if (!filters || !filters.length) {
    var bounds = map.getBounds();
    filters = [function (p) {
      return bounds.contains(p.getLatLng());
    }];
  }

  filters.forEach(function (filter) {
    var points = [];
    for (var guid in window.portals) {
      var location = window.portals[guid];
      if (filter(location)) {
        var point = location._point.clone();
        point.latlng = location._latlng;
        points.push(point);
      }
    }
    if (points.length >= window.plugin.flyLinks.MAX_PORTALS_TO_LINK) {
      //alert("Some polygon contains more than 100 portals.");
      return;
    }
    if (!points.length) return;
    var triangulation = triangulate(points, lines);
    edges = edges.concat(triangulation.edges);
    triangles = triangles.concat(triangulation.triangles);
  });

  $.each(edges, function(idx, edge) {
    drawLink(edge.a, edge.b, {
      color: '#FF0000',
      opacity: 1,
      weight: 1.5,
      interactive: false,
      smoothFactor: 10,
      dashArray: [6, 4],
    });
  });

  $.each(triangles, function(idx, triangle) {
    drawField(triangle.a, triangle.b, triangle.c, {
      stroke: false,
      fill: true,
      fillColor: '#FF0000',
      fillOpacity: 1 - Math.pow(0.85, triangle.depth),
      interactive: false,
    });
  });
};

window.plugin.flyLinks.Edge = function(a, b, depth) {
  this.a = a;
  this.b = b;
  this.depth = depth;
};

window.plugin.flyLinks.Triangle = function(a, b, c, depth) {
  this.a = a;
  this.b = b;
  this.c = c;
  this.depth = depth;
};

window.plugin.flyLinks.setup = function() {
  window.plugin.flyLinks.linksLayerGroup = new L.LayerGroup();
  window.plugin.flyLinks.fieldsLayerGroup = new L.LayerGroup();

  function update () {
    if (!map.hasLayer(window.plugin.flyLinks.linksLayerGroup) ||
        !map.hasLayer(window.plugin.flyLinks.fieldsLayerGroup)) {
      window.plugin.flyLinks.updateLayer();
    }
  }
  window.plugin.flyLinks.linksLayerGroup.on('add', update);
  window.plugin.flyLinks.fieldsLayerGroup.on('add', update);

  window.addHook('mapDataRefreshEnd', function(e) {
    window.plugin.flyLinks.updateLayer();
  });

  window.map.on('moveend', function() {
    window.plugin.flyLinks.updateLayer();
  });

  if (window.plugin.drawTools && window.plugin.drawTools.filterEvents) {
    window.plugin.drawTools.filterEvents.on('changed', window.plugin.flyLinks.updateLayer);
  }

  window.addLayerGroup('Fly links', window.plugin.flyLinks.linksLayerGroup, false);
  window.addLayerGroup('Fly fields', window.plugin.flyLinks.fieldsLayerGroup, false);
};
var setup = window.plugin.flyLinks.setup;
<|endoftext|>


<commit_before>"use strict";

/**
 * Great Circle calculation
 * @author  Kevin Ulrich
 * @class  greatCircle
 */
class greatCircle {

	/**
	 * Constructor
	 * @param  {object} opts Option-Object. Accepts properties 'sort' ('asc' || 'desc' || false) and limit (positive integer || false)
	 */
	constructor(opts) {
		if(typeof opts != 'object') opts = {};

		this.sort = greatCircle._getValidSort(opts.sort);
		this.limit = greatCircle._getValidLimit(opts.limit);
	}

	/**
	 * Calculate a list of points. Can override default 'sort' and 'limit' by property.
	 * @param  {object} opts Option-Object. Accepts properties from (valid point), to (array of or single valid point), sort (see constructor), limit (see constructor)
	 * @return {array}      The resulting distance list with given points and distances
	 */
	calc(opts) {
		var tempSort = typeof opts.sort != 'undefined' ? greatCircle._getValidSort(opts.sort) : this.sort;
		var tempLimit = typeof opts.limit != 'undefined' ? greatCircle._getValidLimit(opts.limit) : this.limit;

		var from = greatCircle._getValidPoint(opts.from);
		var to = greatCircle._getValidPointList(opts.to);

		var distances = greatCircle._getDistanceList(from, to);

		if(tempSort !== false) distances = greatCircle._sortDistanceList(distances, tempSort);
		if(tempLimit !== false) distances = greatCircle._limitDistanceList(distances, tempLimit);

		return distances;
	}

	/**
	 * The raw calculation according to the haversine formula.
	 * Big thanks to http://www.movable-type.co.uk/scripts/latlong.html for the brilliant explanation of
	 * different methods of determining great circle distance!
	 * @param  {object} from A valid point
	 * @param  {object} to   A valid point
	 * @return {number}      Distance in meters
	 * @static
	 */
	static getDistanceBetween(from, to) {
		from = greatCircle._getValidPoint(from);
		to = greatCircle._getValidPoint(to);

		var radLatFrom = greatCircle.floatToRadians(from.lat);
		var radLatTo = greatCircle.floatToRadians(to.lat);

		var radDeltaLat = greatCircle.floatToRadians(to.lat - from.lat);
		var radDeltaLon = greatCircle.floatToRadians(to.lon - from.lon);

		var a = Math.sin(radDeltaLat / 2) * Math.sin(radDeltaLat / 2) +
		        Math.cos(radLatFrom) * Math.cos(radLatTo) *
		        Math.sin(radDeltaLon / 2) * Math.sin(radDeltaLon / 2);

		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

		// Precision less than a meter is not really accurate here

		return parseInt(greatCircle.earthRadius * c);
	}

	/**
	 * Limit a list to a number of items
	 * @param  {object} list  The Array/Object to limit
	 * @param  {number} limit Number of allowed items as integer
	 * @return {object}       Truncated Array/Object
	 * @static
	 */
	static _limitDistanceList(list, limit) {
		limit = greatCircle._getValidLimit(limit);

		if(limit === false) return [];

		var limitedArray = [];

		for(var item in list) {
			if(item == limit) break;
			limitedArray.push(list[item]);
		}

		return limitedArray;
	}

	/**
	 * Sort a list of distances (as determined by the distance property of items)
	 * @param  {object} list      List of distances, as returned by _getDistanceList()
	 * @param  {mixed} direction A valid direction ('asc' || 'desc' || false)
	 * @return {object}           The sorted List
	 * @static
	 */
	static _sortDistanceList(list, direction) {
		direction = greatCircle._getValidSort(direction);

		// Use selection sort for now

		var sortedList = [];

		while(list.length > 0) {
			sortedList.push(greatCircle._spliceLargestListItem(list));
		}

		if(direction == 'asc') sortedList.reverse();

		return sortedList;
	}

	/**
	 * Get the list item with the largest distance, remove it from the list
	 * and return it
	 * @param {object} list       List of distances, as returned by _getDistanceList()
	 * @return {object}           The sorted List
	 * @static
	 */
	static _spliceLargestListItem(list) {
		var maxIdx = 0;

		for(var item in list) {
			let thisItem = list[item];

			if(typeof thisItem.distance == 'undefined') continue;

			if(thisItem.distance >= list[maxIdx].distance) {
				maxIdx = item;
			}
		}

		return list.splice(maxIdx, 1)[0];
	}

	/**
	 * Get a list of distances for from to each to point
	 * @param  {object} from A valid point
	 * @param  {object} to   An array of or a single valid point
	 * @return {object}      An Array containing the distances for from to each to point.
	 * @static
	 */
	static _getDistanceList(from, to) {
		from = greatCircle._getValidPoint(from);
		to = greatCircle._getValidPointList(to);

		var distanceList = [];

		for(var item in to) {
			try {
				let distance = greatCircle.getDistanceBetween(from, to[item]);
				distanceList.push({from: from, to: to[item], distance: distance});
			} catch(e) {
				continue;
			}
		}

		return distanceList;
	}

	/**
	 * Check/Clean/Format a given list for validity.
	 * @param  {object} list The list to check
	 * @return {object}      An Array containing valid points
	 * @static
	 */
	static _getValidPointList(list) {
		try {
			if(!(list instanceof Array)) return [greatCircle._getValidPoint(list)];
		} catch(e) {
			return [];
		}

		var cleanList = [];

		for(var item in list) {
			try {
				cleanList.push(greatCircle._getValidPoint(list[item]));
			} catch(e) {
				continue;
			}
		}

		return cleanList;
	}

	/**
	 * Check/Clean/Format a given point for validity
	 * @param  {object} point The point to check
	 * @return {object}       A valid point
	 * @static
	 */
	static _getValidPoint(point) {
		if(typeof point == 'undefined') throw "greatCircle: No coordinates given";
		if(isNaN(point.lat) || isNaN(point.lon)) throw "greatCircle: Invalid coordinates";
		if(parseFloat(point.lat) < -180 || parseFloat(point.lat) > 180) throw "greatCircle: Invalid coordinates";
		if(parseFloat(point.lon) < -180 || parseFloat(point.lon) > 180) throw "greatCircle: Invalid coordinates";

		var cleanPoint = {lat: parseFloat(point.lat), lon: parseFloat(point.lon)};

		if(typeof point.payload != 'undefined') cleanPoint.payload = point.payload;

		return cleanPoint;
	}

	/**
	 * Get a valid sorting parameter. Either 'asc', 'desc', or false for no sorting.
	 * @param  {mixed} input Input to check
	 * @return {mixed}       Valid sorting parameter for use with further functions
	 * @static
	 */
	static _getValidSort(input) {
		return (input && input == 'asc') ? 'asc' : (input && input == 'desc') ? 'desc' : false;
	}

	/**
	 * Get a valid limit parameter. Can either be false or a positive integer
	 * @param  {mixed} input Input to check
	 * @return {mixed}       Valid limit parameter
	 * @static
	 */
	static _getValidLimit(input) {
		if(isNaN(parseInt(input))) {
			return false;
		}

		if(parseInt(input) < 0)  {
			return false;
		}

		return parseInt(input);
	}

	/**
	 * Convert a degree as floating point number to degree radians
	 * @param  {number} number The input degree
	 * @return {number}        Degree Radians
	 * @static
	 */
	static floatToRadians(number) {
		return number * Math.PI / 180;
	}
}

/**
 * The earths radius as required by the haversine formula
 * @type {Number}
 * @static
 */
greatCircle.earthRadius = 6371000;

module.exports = greatCircle;<commit_msg>Refactor point checking for readability<commit_after>"use strict";

/**
 * Great Circle calculation
 * @author  Kevin Ulrich
 * @class  greatCircle
 */
class greatCircle {

	/**
	 * Constructor
	 * @param  {object} opts Option-Object. Accepts properties 'sort' ('asc' || 'desc' || false) and limit (positive integer || false)
	 */
	constructor(opts) {
		if(typeof opts != 'object') opts = {};

		this.sort = greatCircle._getValidSort(opts.sort);
		this.limit = greatCircle._getValidLimit(opts.limit);
	}

	/**
	 * Calculate a list of points. Can override default 'sort' and 'limit' by property.
	 * @param  {object} opts Option-Object. Accepts properties from (valid point), to (array of or single valid point), sort (see constructor), limit (see constructor)
	 * @return {array}      The resulting distance list with given points and distances
	 */
	calc(opts) {
		var tempSort = typeof opts.sort != 'undefined' ? greatCircle._getValidSort(opts.sort) : this.sort;
		var tempLimit = typeof opts.limit != 'undefined' ? greatCircle._getValidLimit(opts.limit) : this.limit;

		var from = greatCircle._getValidPoint(opts.from);
		var to = greatCircle._getValidPointList(opts.to);

		var distances = greatCircle._getDistanceList(from, to);

		if(tempSort !== false) distances = greatCircle._sortDistanceList(distances, tempSort);
		if(tempLimit !== false) distances = greatCircle._limitDistanceList(distances, tempLimit);

		return distances;
	}

	/**
	 * The raw calculation according to the haversine formula.
	 * Big thanks to http://www.movable-type.co.uk/scripts/latlong.html for the brilliant explanation of
	 * different methods of determining great circle distance!
	 * @param  {object} from A valid point
	 * @param  {object} to   A valid point
	 * @return {number}      Distance in meters
	 * @static
	 */
	static getDistanceBetween(from, to) {
		from = greatCircle._getValidPoint(from);
		to = greatCircle._getValidPoint(to);

		var radLatFrom = greatCircle.floatToRadians(from.lat);
		var radLatTo = greatCircle.floatToRadians(to.lat);

		var radDeltaLat = greatCircle.floatToRadians(to.lat - from.lat);
		var radDeltaLon = greatCircle.floatToRadians(to.lon - from.lon);

		var a = Math.sin(radDeltaLat / 2) * Math.sin(radDeltaLat / 2) +
		        Math.cos(radLatFrom) * Math.cos(radLatTo) *
		        Math.sin(radDeltaLon / 2) * Math.sin(radDeltaLon / 2);

		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

		// Precision less than a meter is not really accurate here

		return parseInt(greatCircle.earthRadius * c);
	}

	/**
	 * Limit a list to a number of items
	 * @param  {object} list  The Array/Object to limit
	 * @param  {number} limit Number of allowed items as integer
	 * @return {object}       Truncated Array/Object
	 * @static
	 */
	static _limitDistanceList(list, limit) {
		limit = greatCircle._getValidLimit(limit);

		if(limit === false) return [];

		var limitedArray = [];

		for(var item in list) {
			if(item == limit) break;
			limitedArray.push(list[item]);
		}

		return limitedArray;
	}

	/**
	 * Sort a list of distances (as determined by the distance property of items)
	 * @param  {object} list      List of distances, as returned by _getDistanceList()
	 * @param  {mixed} direction A valid direction ('asc' || 'desc' || false)
	 * @return {object}           The sorted List
	 * @static
	 */
	static _sortDistanceList(list, direction) {
		direction = greatCircle._getValidSort(direction);

		// Use selection sort for now

		var sortedList = [];

		while(list.length > 0) {
			sortedList.push(greatCircle._spliceLargestListItem(list));
		}

		if(direction == 'asc') sortedList.reverse();

		return sortedList;
	}

	/**
	 * Get the list item with the largest distance, remove it from the list
	 * and return it
	 * @param {object} list       List of distances, as returned by _getDistanceList()
	 * @return {object}           The sorted List
	 * @static
	 */
	static _spliceLargestListItem(list) {
		var maxIdx = 0;

		for(var item in list) {
			let thisItem = list[item];

			if(typeof thisItem.distance == 'undefined') continue;

			if(thisItem.distance >= list[maxIdx].distance) {
				maxIdx = item;
			}
		}

		return list.splice(maxIdx, 1)[0];
	}

	/**
	 * Get a list of distances for from to each to point
	 * @param  {object} from A valid point
	 * @param  {object} to   An array of or a single valid point
	 * @return {object}      An Array containing the distances for from to each to point.
	 * @static
	 */
	static _getDistanceList(from, to) {
		from = greatCircle._getValidPoint(from);
		to = greatCircle._getValidPointList(to);

		var distanceList = [];

		for(var item in to) {
			try {
				let distance = greatCircle.getDistanceBetween(from, to[item]);
				distanceList.push({from: from, to: to[item], distance: distance});
			} catch(e) {
				continue;
			}
		}

		return distanceList;
	}

	/**
	 * Check/Clean/Format a given list for validity.
	 * @param  {object} list The list to check
	 * @return {object}      An Array containing valid points
	 * @static
	 */
	static _getValidPointList(list) {
		try {
			if(!(list instanceof Array)) return [greatCircle._getValidPoint(list)];
		} catch(e) {
			return [];
		}

		var cleanList = [];

		for(var item in list) {
			try {
				cleanList.push(greatCircle._getValidPoint(list[item]));
			} catch(e) {
				continue;
			}
		}

		return cleanList;
	}

	/**
	 * Check/Clean/Format a given point for validity
	 * @param  {object} point The point to check
	 * @return {object}       A valid point
	 * @static
	 */
	static _getValidPoint(point) {
		if(typeof point === 'undefined') throw "greatCircle: No coordinates given";

		if(!this._isValidDegree(point.lat) || !this._isValidDegree(point.lon)) {
			throw "greatCircle: Invalid coordinates";
		}

		var cleanPoint = {lat: parseFloat(point.lat), lon: parseFloat(point.lon)};

		if(typeof point.payload != 'undefined') cleanPoint.payload = point.payload;

		return cleanPoint;
	}

	/**
	 * Check whether a given value can plausibly be a degree
	 * @param {number} The number to check
	 * @return {boolean}
	 * @static
	 */
	static _isValidDegree(degree) {
		if(isNaN(degree)) {
			return false;
		}

		if(Math.abs(parseFloat(degree) > 180)) {
			return false;
		}

		return true;
	}

	/**
	 * Get a valid sorting parameter. Either 'asc', 'desc', or false for no sorting.
	 * @param  {mixed} input Input to check
	 * @return {mixed}       Valid sorting parameter for use with further functions
	 * @static
	 */
	static _getValidSort(input) {
		return (input && input == 'asc') ? 'asc' : (input && input == 'desc') ? 'desc' : false;
	}

	/**
	 * Get a valid limit parameter. Can either be false or a positive integer
	 * @param  {mixed} input Input to check
	 * @return {mixed}       Valid limit parameter
	 * @static
	 */
	static _getValidLimit(input) {
		if(isNaN(parseInt(input))) {
			return false;
		}

		if(parseInt(input) < 0)  {
			return false;
		}

		return parseInt(input);
	}

	/**
	 * Convert a degree as floating point number to degree radians
	 * @param  {number} number The input degree
	 * @return {number}        Degree Radians
	 * @static
	 */
	static floatToRadians(number) {
		return number * Math.PI / 180;
	}
}

/**
 * The earths radius as required by the haversine formula
 * @type {Number}
 * @static
 */
greatCircle.earthRadius = 6371000;

module.exports = greatCircle;<|endoftext|>


<commit_before>var bot = require('..')

bot.addListener('message', function (nick, to, text, message) {
  bot.fireEvents('message', nick, to, text, message)
})

bot.addListener('pm', function (nick, text, message) {
  bot.fireEvents('message', nick, text, message)
})

bot.addListener('ctcp-version', function (from, to, message) {
  bot.fireEvents('version', from, to, message)
})

bot.addListener('action', function (from, to, text, message) {
  bot.fireEvents('action', from, to, text, message)
})
<commit_msg>add listener for join event<commit_after>var bot = require('..')

bot.addListener('message', function (nick, to, text, message) {
  bot.fireEvents('message', nick, to, text, message)
})

bot.addListener('pm', function (nick, text, message) {
  bot.fireEvents('message', nick, text, message)
})

bot.addListener('ctcp-version', function (from, to, message) {
  bot.fireEvents('version', from, to, message)
})

bot.addListener('action', function (from, to, text, message) {
  bot.fireEvents('action', from, to, text, message)
})

bot.addListener('join', function (channel, nick, message) {
  bot.fireEvents('join', channel, nick, message)
  if (nick == bot.nick) {
    bot.fireEvents('selfjoin', channel, message)
  }
})
<|endoftext|>


<commit_before>/*
 This lazySizes extension helps to use responsive images, but to opt-out from high retina support in case the w descriptor is used (for x descriptor this is not needed!),
 - data-sizes="auto" has to be used in conjunction
 - respimage.js needs to be loaded even if the browser supports responsive images

 */

(function(window, document, undefined){
	'use strict';
	var regPicture = /^picture$/i;
	if(!window.addEventListener){
		return;
	}

	function ascendingSort( a, b ) {
		return a.res - b.res;
	}

	function parseSets(elem){
		var lazyData = {srcset: elem.getAttribute(lazySizes.cfg.srcsetAttr)} || '';
		var cands = respimage._.parseSet(lazyData);
		elem._lazySrcset = lazyData;

		cands.sort( ascendingSort );
		lazyData.index = 0;
		lazyData.dirty = false;
		if(cands[0]){
			lazyData.cSrcset = [cands[0].url +' '+ cands[0].desc.val + cands[0].desc.type];
		} else {
			lazyData.cSrcset = [];
		}

		return lazyData;
	}

	function parseImg(elem){
		var sources, i, len;
		var parent = elem.parentNode || {};
		var lazyData = parseSets(elem);
		lazyData.isImg = true;
		if(regPicture.test(parent.nodeName) || ''){
			lazyData.picture = true;
			sources = parent.getElementsByTagName('source');
			for(i = 0, len = sources.length; i < len; i++){
				parseSets(sources[i]).isImg = false;
			}
		}

		return lazyData;
	}

	function getConstrainedSrcSet(data, width){
		var i, can;

		for(i = data.index + 1; i < data.cands.length; i++){
			can = data.cands[i];
			if(can.desc.type != 'w' || can.desc.val <= width){
				data.cSrcset.push(can.url +' '+ can.desc.val + can.desc.type);
				data.index = i;
			} else {
				break;
			}
		}
	}

	function constrainSrces(elem, width, attr){
		var imageData;
		var lazyData = elem._lazySrcset;
		var curIndex = lazyData.index;

		getConstrainedSrcSet(lazyData, width);

		if(!lazyData.dirty || curIndex != lazyData.index){
			elem.setAttribute(attr, lazyData.cSrcset.join(', '));
			lazyData.dirty = true;

			if(lazyData.isImg && attr == 'srcset' && !window.HTMLPictureElement && !respimage._.observer){
				imageData = elem[respimage._.ns];
				if(imageData){
					imageData.srcset = undefined;
				}
				respimage({reparse: true, elements: [elem]});
			}
		}
	}

	document.addEventListener('lazybeforesizes', function(e){
		var maxdpr, lazyData, width, data, attr, parent, sources, i, len;
		if(!window.respimage ||
			e.defaultPrevented ||
			!(maxdpr = e.target.getAttribute('data-maxdpr')) ||
			maxdpr > (window.devicePixelRatio || 1) ||
			(e.target._lazysizesWidth && e.target._lazysizesWidth > e.details.width)){return;}

		lazyData = e.target._lazySrcset || parseImg(e.target);
		width = e.details.width * maxdpr;

		if(width){
			attr = e.details.polyfill ? 'srcset' : lazySizes.cfg.srcsetAttr;

			if(lazyData.picture && (parent = e.target.parentNode)){
				sources = parent.getElementsByTagName('source');
				for(i = 0, len = sources.length; i < len; i++){
					constrainSrces(sources[i], width, attr);
				}
			}
			constrainSrces(e.target, width, attr);
		}
	}, false);

})(window, document);
<commit_msg>improve maxdpr plugin<commit_after>/*
 This lazySizes extension helps to use responsive images, but to opt-out from high retina support in case the w descriptor is used (for x descriptor this is not needed!),
 - data-sizes="auto" has to be used in conjunction
 - respimage.js needs to be loaded even if the browser supports responsive images

 */

(function(window, document, undefined){
	'use strict';
	var regPicture = /^picture$/i;
	if(!window.addEventListener){
		return;
	}

	function ascendingSort( a, b ) {
		return a.res - b.res;
	}

	function parseSets(elem){
		var lazyData = {srcset: elem.getAttribute(lazySizes.cfg.srcsetAttr)} || '';
		var cands = respimage._.parseSet(lazyData);
		elem._lazySrcset = lazyData;

		cands.sort( ascendingSort );
		lazyData.index = 0;
		lazyData.dirty = false;
		if(cands[0]){
			lazyData.cSrcset = [cands[0].url +' '+ cands[0].desc.val + cands[0].desc.type];
		} else {
			lazyData.cSrcset = [];
		}

		return lazyData;
	}

	function parseImg(elem){
		var sources, i, len;
		var parent = elem.parentNode || {};
		var lazyData = parseSets(elem);
		lazyData.isImg = true;
		if(regPicture.test(parent.nodeName) || ''){
			lazyData.picture = true;
			sources = parent.getElementsByTagName('source');
			for(i = 0, len = sources.length; i < len; i++){
				parseSets(sources[i]).isImg = false;
			}
		}

		return lazyData;
	}

	function takeHighRes(beforeCan, curCanWidth, width){
		var low, high;
		if(!beforeCan || !beforeCan.desc){
			return true;
		}
		low = (1 - (beforeCan.desc.val / width)) * 0.6;
		high = (curCanWidth / width) - 1;
		return high - low < 0;

	}

	function getConstrainedSrcSet(data, width){
		var i, can;

		for(i = data.index + 1; i < data.cands.length; i++){
			can = data.cands[i];
			if(can.desc.type != 'w' || can.desc.val <= width || takeHighRes(data.cands[i - 1], can.desc.val, width)){
				data.cSrcset.push(can.url +' '+ can.desc.val + can.desc.type);
				data.index = i;
			} else {
				break;
			}
		}
	}

	function constrainSrces(elem, width, attr){
		var imageData, curIndex;
		var lazyData = elem._lazySrcset;

		if(!lazyData){return;}
		curIndex = lazyData.index;

		getConstrainedSrcSet(lazyData, width);

		if(!lazyData.dirty || curIndex != lazyData.index){
			elem.setAttribute(attr, lazyData.cSrcset.join(', '));
			lazyData.dirty = true;

			if(lazyData.isImg && attr == 'srcset' && !window.HTMLPictureElement && !respimage._.observer){
				imageData = elem[respimage._.ns];
				if(imageData){
					imageData.srcset = undefined;
				}
				respimage({reparse: true, elements: [elem]});
			}
		}
	}

	document.addEventListener('lazybeforesizes', function(e){
		var maxdpr, lazyData, width, data, attr, parent, sources, i, len;
		if(!window.respimage ||
			e.defaultPrevented ||
			!(maxdpr = e.target.getAttribute('data-maxdpr')) ||
			maxdpr > (window.devicePixelRatio || 1) ||
			(e.target._lazysizesWidth && e.target._lazysizesWidth > e.details.width)){return;}

		lazyData = e.target._lazySrcset || parseImg(e.target);
		width = e.details.width * maxdpr;

		if(width){
			attr = e.details.polyfill ? 'srcset' : lazySizes.cfg.srcsetAttr;

			if(lazyData.picture && (parent = e.target.parentNode)){
				sources = parent.getElementsByTagName('source');
				for(i = 0, len = sources.length; i < len; i++){
					constrainSrces(sources[i], width, attr);
				}
			}
			constrainSrces(e.target, width, attr);
		}
	}, false);

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


<commit_before>angular
  .module('app', ['ui.router'])
  .config(function($stateProvider) {
    $stateProvider
      .state('artworks', {
        url: '/artworks',
        templateUrl: 'app/views/artworks/index.html'
      })
      .state('artworks.search', {
        url: '/search',
        templateUrl: 'app/views/artworks/search.html'
      })
  })
<commit_msg>add home state to module<commit_after>angular
  .module('app', ['ui.router'])
  .config(function($stateProvider) {
    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'app/views/index.html'
      })
      .state('artworks', {
        url: '/artworks',
        templateUrl: 'app/views/artworks/index.html'
      })
      .state('artworks.search', {
        url: '/search',
        templateUrl: 'app/views/artworks/search.html'
      })
  })
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require_self
//= require_tree ./backbone

window.Cfp        = {};
window.Cfp.Models = {};
window.Cfp.Views  = {};
<commit_msg>Fix the delete links<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
//
//= require jquery_ujs
//= require_self
//= require_tree ./backbone

window.Cfp        = {};
window.Cfp.Models = {};
window.Cfp.Views  = {};
<|endoftext|>


<commit_before>/**
 * @class Renderer
 */

EZ3.Renderer = function(canvas, options) {
  this._programs = {};

  this.canvas = canvas;
  this.options = options;
  this.context = null;
};

EZ3.Renderer.prototype._processContextLost = function(event) {
  event.preventDefault();
};

EZ3.Renderer.prototype._renderMesh = function(mesh, camera, lights) {
  var gl = this.context;
  var program = mesh.material.program;
  var modelView = new EZ3.Matrix4();
  var i;

  program.bind(gl);
  mesh.material.updateStates(gl);
  mesh.material.updateUniforms(gl);

  modelView.mul(mesh.world, camera.view);

  program.loadUniformf(gl, 'uEyePosition', 3, camera.position);
  program.loadUniformMatrix(gl, 'uModel', 4, mesh.world);
  program.loadUniformMatrix(gl, 'uModelView', 4, modelView);
  program.loadUniformMatrix(gl, 'uProjection', 4, camera.projection);

  if (!lights.empty)
    program.loadUniformMatrix(gl, 'uNormal', 3, mesh.normal);

  for (i = 0; i < lights.point.length; i++)
    lights.point[i].updateUniforms(gl, program, i);

  for (i = 0; i < lights.directional.length; i++)
    lights.directional[i].updateUniforms(gl, program, i);

  for (i = 0; i < lights.spot.length; i++)
    lights.spot[i].updateUniforms(gl, program, i);

  mesh.render(gl, program.attributes);
};

EZ3.Renderer.prototype.initContext = function() {
  var that = this;
  var names = [
    'webgl',
    'experimental-webgl',
    'webkit-3d',
    'moz-webgl'
  ];
  var i;

  for (i = 0; i < names.length; i++) {
    try {
      this.context = this.canvas.getContext(names[i], this.options);
    } catch (e) {}

    if (this.context)
      break;
  }

  if (!this.context)
    throw new Error('Unable to initialize WebGL with selected options.');

  this._onContextLost = function(event) {
    that._processContextLost(event);
  };

  this.context.getExtension('OES_standard_derivatives');

  this.canvas.addEventListener('webglcontextlost', this._onContextLost, false);
};

EZ3.Renderer.prototype.clear = function() {
  var gl = this.context;

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(this.context.COLOR_BUFFER_BIT | this.context.DEPTH_BUFFER_BIT);
};

EZ3.Renderer.prototype.viewport = function(position, size) {
  var gl = this.context;

  gl.viewport(position.x, position.y, size.x, size.y);
};

EZ3.Renderer.prototype.render = function(scene, camera) {
  var gl = this.context;
  var entities = [];
  var meshes = {
    common: [],
    opaque: [],
    transparent: []
  };
  var lights = {
    empty: true,
    point: [],
    directional: [],
    spot: []
  };
  var entity;
  var mesh;
  var i;

  entities.push(scene);

  while (entities.length) {
    entity = entities.pop();

    if (entity instanceof EZ3.PointLight)
      lights.point.push(entity);
    else if (entity instanceof EZ3.DirectionalLight)
      lights.directional.push(entity);
    else if (entity instanceof EZ3.SpotLight)
      lights.spot.push(entity);
    else if (entity instanceof EZ3.Mesh)
      meshes.common.push(entity);

    for (i = entity.children.length - 1; i >= 0; i--)
      entities.push(entity.children[i]);

    entity.updateWorld();
  }

  if (lights.point.length || lights.directional.length || lights.spot.length)
    lights.empty = false;

  for (i = 0; i < meshes.common.length; i++) {
    mesh = meshes.common[i];

    mesh.updateEssentialBuffers();

    if (!lights.empty) {
      mesh.updateIlluminationBuffers();
      mesh.updateNormal();
    }

    mesh.material.updateProgram(gl, this._programs, lights);

    if (mesh.material.transparent)
      meshes.transparent.push(mesh);
    else
      meshes.opaque.push(mesh);
  }

  for (i = 0; i < meshes.opaque.length; i++)
    this._renderMesh(meshes.opaque[i], camera, lights);

  for (i = 0; i < meshes.transparent.length; i++)
    this._renderMesh(meshes.transparent[i], camera, lights);
};
<commit_msg>Updated Renderer.js<commit_after>/**
 * @class Renderer
 */

EZ3.Renderer = function(canvas, options) {
  this._programs = {};

  this.context = null;
  this.canvas = canvas;
  this.options = options;
  this.state = new EZ3.RendererState();
};

EZ3.Renderer.prototype._processContextLost = function(event) {
  event.preventDefault();
};

EZ3.Renderer.prototype._renderMesh = function(mesh, camera, lights) {
  var gl = this.context;
  var program = mesh.material.program;
  var modelView = new EZ3.Matrix4();
  var i;

  program.bind(gl);
  mesh.material.updateStates(gl, this.state);
  mesh.material.updateUniforms(gl);

  modelView.mul(mesh.world, camera.view);

  program.loadUniformf(gl, 'uEyePosition', 3, camera.position);
  program.loadUniformMatrix(gl, 'uModel', 4, mesh.world);
  program.loadUniformMatrix(gl, 'uModelView', 4, modelView);
  program.loadUniformMatrix(gl, 'uProjection', 4, camera.projection);

  if (!lights.empty)
    program.loadUniformMatrix(gl, 'uNormal', 3, mesh.normal);

  for (i = 0; i < lights.point.length; i++)
    lights.point[i].updateUniforms(gl, program, i);

  for (i = 0; i < lights.directional.length; i++)
    lights.directional[i].updateUniforms(gl, program, i);

  for (i = 0; i < lights.spot.length; i++)
    lights.spot[i].updateUniforms(gl, program, i);

  mesh.render(gl, program.attributes);
};

EZ3.Renderer.prototype.initContext = function() {
  var that = this;
  var names = [
    'webgl',
    'experimental-webgl',
    'webkit-3d',
    'moz-webgl'
  ];
  var i;

  for (i = 0; i < names.length; i++) {
    try {
      this.context = this.canvas.getContext(names[i], this.options);
    } catch (e) {}

    if (this.context)
      break;
  }

  if (!this.context)
    throw new Error('Unable to initialize WebGL with selected options.');

  this._onContextLost = function(event) {
    that._processContextLost(event);
  };

  this.context.getExtension('OES_standard_derivatives');

  this.canvas.addEventListener('webglcontextlost', this._onContextLost, false);
};

EZ3.Renderer.prototype.clear = function() {
  var gl = this.context;

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(this.context.COLOR_BUFFER_BIT | this.context.DEPTH_BUFFER_BIT);
};

EZ3.Renderer.prototype.viewport = function(position, size) {
  var gl = this.context;

  gl.viewport(position.x, position.y, size.x, size.y);
};

EZ3.Renderer.prototype.render = function(scene, camera) {
  var gl = this.context;
  var entities = [];
  var meshes = {
    common: [],
    opaque: [],
    transparent: []
  };
  var lights = {
    empty: true,
    point: [],
    directional: [],
    spot: []
  };
  var entity;
  var mesh;
  var i;

  entities.push(scene);

  while (entities.length) {
    entity = entities.pop();

    if (entity instanceof EZ3.PointLight)
      lights.point.push(entity);
    else if (entity instanceof EZ3.DirectionalLight)
      lights.directional.push(entity);
    else if (entity instanceof EZ3.SpotLight)
      lights.spot.push(entity);
    else if (entity instanceof EZ3.Mesh)
      meshes.common.push(entity);

    for (i = entity.children.length - 1; i >= 0; i--)
      entities.push(entity.children[i]);

    entity.updateWorld();
  }

  if (lights.point.length || lights.directional.length || lights.spot.length)
    lights.empty = false;

  for (i = 0; i < meshes.common.length; i++) {
    mesh = meshes.common[i];

    mesh.updateEssentialBuffers();

    if (!lights.empty) {
      mesh.updateIlluminationBuffers();
      mesh.updateNormal();
    }

    mesh.material.updateProgram(gl, this._programs, lights);

    if (mesh.material.transparent)
      meshes.transparent.push(mesh);
    else
      meshes.opaque.push(mesh);
  }

  for (i = 0; i < meshes.opaque.length; i++)
    this._renderMesh(meshes.opaque[i], camera, lights);

  for (i = 0; i < meshes.transparent.length; i++)
    this._renderMesh(meshes.transparent[i], camera, lights);
};
<|endoftext|>


<commit_before>function chatController(postId) {

  var chatDAO = new ChatDAO(postId);
  var lastChatId;
  var firstChatId;
  var isScrollingUp;
  var isWindowActive = true;
  chatDAO.getChatsInfo({limit: 30}, function(chatsInfo) {
    var members = [];
    var membersMap = {};
    chatsInfo.members.forEach(function(m) {
      var user = new User(m);
      members.push(user);
      membersMap[user.id] = user;
    });

    var chat = new Chat(new User(current_user), members, {}, function(message, callback) {
      myMessageSent = true;
      chatDAO.sendMessage(message, function(error, data) {
        if (callback) {
          callback(error, data);
        }
      });
    });

    if (chatsInfo.chats.length > 0) {
      lastChatId = chatsInfo.chats[chatsInfo.chats.length - 1].id;
      firstChatId = chatsInfo.chats[0].id; // handle case when no messages.
    }

    chat.setScrollTopEventCallback(function() {
      if (!isScrollingUp) {
        isScrollingUp = true;
        chatDAO.getChatsInfo({before_id: firstChatId, limit: 10}, function(ci) {
          if (ci.chats.length > 0) {
            firstChatId = ci.chats[0].id; // move this to a function
          }
          var messages = getMessagesFromChatInfo(ci);
          chat.addMessagesToTop(messages);
          isScrollingUp = false;
        });
      }
    });
    chat.addMessages(getMessagesFromChatInfo(chatsInfo));
    $('.js_upload_form').on("ajax:remotipartComplete", function(e, data) {
      var c = JSON.parse(data.responseText).chat;
      var msg = new Message(c, membersMap[c.user.id]);
      chat.addMessage(msg);
    });

    setInterval(function() {
      fetchLattestMessages(lastChatId);
    }, 5000);

    function getMessagesFromChatInfo(chatsInfo, excludeMyMessages) {
      var messages = [];
      chatsInfo.chats.forEach(function(chat) {
        if (!(excludeMyMessages && chat.user.id == current_user.id)) {
          var message = new Message(chat, membersMap[chat.user.id]);
          messages.push(message);
        } // TODO this will not work if multiple windows are open FIX
      });
      return messages;
    }

    function getUsersFromChatInfo(chatsInfo) {
      var members = [];
      chatsInfo.members.forEach(function(m) {
        var user = new User(m);
        members.push(user);
      });
      return members;
    }

    function fetchLattestMessages() {
      chatDAO.getChatsInfo({after_id: lastChatId}, function(ci) {
        if (ci.chats.length > 0) {
          lastChatId = ci.chats[ci.chats.length - 1].id; // move this to a function
        }
        var messages = getMessagesFromChatInfo(ci, true);
        chat.addMessages(messages);
        chat.addUsersToUserArea(getUsersFromChatInfo(ci));

      });
    }

  });

  $(window).blur(function(){
    isWindowActive = false;
  });
  $(window).focus(function(){
    isWindowActive = true;
  });

}
<commit_msg>basic desktop notification<commit_after>function chatController(postId) {

  var chatDAO = new ChatDAO(postId);
  var lastChatId;
  var firstChatId;
  var isScrollingUp;
  var isWindowActive = true;
  chatDAO.getChatsInfo({limit: 30}, function(chatsInfo) {
    var members = [];
    var membersMap = {};
    chatsInfo.members.forEach(function(m) {
      var user = new User(m);
      members.push(user);
      membersMap[user.id] = user;
    });

    var chat = new Chat(new User(current_user), members, {}, function(message, callback) {
      myMessageSent = true;
      chatDAO.sendMessage(message, function(error, data) {
        if (callback) {
          callback(error, data);
        }
      });
    });

    if (chatsInfo.chats.length > 0) {
      lastChatId = chatsInfo.chats[chatsInfo.chats.length - 1].id;
      firstChatId = chatsInfo.chats[0].id; // handle case when no messages.
    }

    chat.setScrollTopEventCallback(function() {
      if (!isScrollingUp) {
        isScrollingUp = true;
        chatDAO.getChatsInfo({before_id: firstChatId, limit: 10}, function(ci) {
          if (ci.chats.length > 0) {
            firstChatId = ci.chats[0].id; // move this to a function
          }
          var messages = getMessagesFromChatInfo(ci);
          chat.addMessagesToTop(messages);
          isScrollingUp = false;
        });
      }
    });
    chat.addMessages(getMessagesFromChatInfo(chatsInfo));
    $('.js_upload_form').on("ajax:remotipartComplete", function(e, data) {
      var c = JSON.parse(data.responseText).chat;
      var msg = new Message(c, membersMap[c.user.id]);
      chat.addMessage(msg);
    });

    setInterval(function() {
      fetchLattestMessages(lastChatId);
    }, 5000);

    function getMessagesFromChatInfo(chatsInfo, excludeMyMessages) {
      var messages = [];
      chatsInfo.chats.forEach(function(chat) {
        if (!(excludeMyMessages && chat.user.id == current_user.id)) {
          var message = new Message(chat, membersMap[chat.user.id]);
          messages.push(message);
        } // TODO this will not work if multiple windows are open FIX
      });
      return messages;
    }

    function getUsersFromChatInfo(chatsInfo) {
      var members = [];
      chatsInfo.members.forEach(function(m) {
        var user = new User(m);
        members.push(user);
      });
      return members;
    }

    function fetchLattestMessages() {
      chatDAO.getChatsInfo({after_id: lastChatId}, function(ci) {
        if (ci.chats.length > 0) {
          lastChatId = ci.chats[ci.chats.length - 1].id; // move this to a function
        }
        var messages = getMessagesFromChatInfo(ci, true);
        chat.addMessages(messages);
        chat.addUsersToUserArea(getUsersFromChatInfo(ci));
        notifyMe(messages);
      });
    }

  });

  $(window).blur(function(){
    isWindowActive = false;
  });
  $(window).focus(function(){
    isWindowActive = true;
  });

  function notifyMe(messages) {
    var m = messages[0];
    if (Notification.permission !== "granted")
      Notification.requestPermission();
    else {
      var notification = new Notification('New chat message', {
        icon: m.user.picture,
        body: m.text,
      });

      notification.onclick = function () {
        window.focus(); this.cancel();
        //window.open("http://stackoverflow.com/a/13328397/1269037");
      };

    }

  }
}
document.addEventListener('DOMContentLoaded', function () {
  if (!Notification) {
    alert('Desktop notifications not available in your browser. Try Chromium.');
    return;
  }

  if (Notification.permission !== "granted")
    Notification.requestPermission();
});

<|endoftext|>


<commit_before>CKEDITOR.editorConfig = function(config) {

  // Filebrowser routes
  // The location of an external file browser, that should be launched when "Browse Server" button is pressed.
  config.filebrowserBrowseUrl = "/cms9/ckeditor/attachment_files";
  // The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Flash dialog.
  config.filebrowserFlashBrowseUrl = "/cms9/ckeditor/attachment_files";
  // The location of a script that handles file uploads in the Flash dialog.
  config.filebrowserFlashUploadUrl = "/cms9/ckeditor/attachment_files";
  // The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Link tab of Image dialog.
  config.filebrowserImageBrowseLinkUrl = "/cms9/ckeditor/pictures";
  // The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Image dialog.
  config.filebrowserImageBrowseUrl = "/cms9/ckeditor/pictures";
  // The location of a script that handles file uploads in the Image dialog.
  config.filebrowserImageUploadUrl = "/cms9/ckeditor/pictures";
  // The location of a script that handles file uploads.
  config.filebrowserUploadUrl = "/cms9/ckeditor/attachment_files";

  return true;
};
<commit_msg>- quick update<commit_after>CKEDITOR.editorConfig = function(config) {

  config.filebrowserBrowseUrl = "/cms9/ckeditor/attachment_files";
  config.filebrowserFlashBrowseUrl = "/cms9/ckeditor/attachment_files";
  config.filebrowserFlashUploadUrl = "/cms9/ckeditor/attachment_files";
  config.filebrowserImageBrowseLinkUrl = "/cms9/ckeditor/pictures";
  config.filebrowserImageBrowseUrl = "/cms9/ckeditor/pictures";
  config.filebrowserImageUploadUrl = "/cms9/ckeditor/pictures";
  config.filebrowserUploadUrl = "/cms9/ckeditor/attachment_files";

  return true;
};
<|endoftext|>


<commit_before>$(function() {
    // http://localhost:3333/exercises/38/statistics good for testing
    // originally at--> localhost:3333/exercises/69/statistics

    if ($.isController('exercises') && $('.graph-functions-2').isPresent()) {
        // GET THE DATA
        var submissions = $('#data').data('submissions');
        var submissions_length = submissions.length;

        submissionsScoreAndTimeAssess = [[0,0]];
        submissionsScoreAndTimeSubmits = [[0,0]];
        submissionsScoreAndTimeRuns = [];
        var maximumValue = 0;

        var wtimes = $('#wtimes').data('working_times'); //.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(working_times_until)

        // console.log(submissions);
        // console.log(wtimes);

        for (var i = 0;i<submissions_length;i++){
            var submission = submissions[i];
            var workingTime;
            var submissionArray;

            workingTime = get_minutes(wtimes[i]);
            submissionArray = [submission.score, 0];

            if (workingTime > 0) {
                submissionArray[1] = workingTime;
            }
            if(submission.score>maximumValue){
                maximumValue = submission.score;
            }

            if(submission.cause == "assess"){
                submissionsScoreAndTimeAssess.push(submissionArray);
            } else if(submission.cause == "submit"){
                submissionsScoreAndTimeSubmits.push(submissionArray);
            } else if(submission.cause == "run"){
                submissionsScoreAndTimeRuns.push(submissionArray[1]);
            }
        }
        // console.log(submissionsScoreAndTimeAssess.length);
        // console.log(submissionsScoreAndTimeSubmits);
        // console.log(submissionsScoreAndTimeRuns);

        function get_minutes (time_stamp) {
            try {
                hours = time_stamp.split(":")[0];
                minutes = time_stamp.split(":")[1];
                seconds = time_stamp.split(":")[2];
                seconds /= 60;
                minutes = parseFloat(hours * 60) + parseInt(minutes) + seconds;
                if (minutes > 0){
                    return minutes;
                } else{
                    return parseFloat(seconds/60);
                }
            } catch (err) {
                return 0;
            }
        }

        function getWidth() {
            if (self.innerHeight) {
                return self.innerWidth;
            }

            if (document.documentElement && document.documentElement.clientWidth) {
                return document.documentElement.clientWidth;
            }

            if (document.body) {
                return document.body.clientWidth;
            }
        }

        function graph_assesses() {
            // MAKE THE GRAPH
            var width_ratio = .8;
            if (getWidth()*width_ratio > 1000){
                width_ratio = 1000/getWidth();
            }
            var height_ratio = .7; // percent of height

            var margin = {top: 100, right: 20, bottom: 70, left: 70},//30,50
                width = (getWidth() * width_ratio) - margin.left - margin.right,
                height = (width * height_ratio) - margin.top - margin.bottom;

            // Set the ranges
            var x = d3.scale.linear().range([0, width]);
            var y = d3.scale.linear().range([height,0]);

            //var x = d3.scale.linear()
            //    .range([0, width]);
            //var y = d3.scale.linear()
            //    .range([0,height]); // - (height/20

            var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom")
                .ticks(20);

            var yAxis = d3.svg.axis()
                .scale(d3.scale.linear().domain([0,maximumValue]).range([height,0]))//y
                // .scale(y)
                .orient("left")
                .ticks(maximumValue)
                .innerTickSize(-width)
                .outerTickSize(0);

            //var line = d3.svg.line()
            //    .x(function(d) { return x(d.date); })
            //    .y(function(d) { return y(d.close); });

            var line = d3.svg.line()
                .x(function (d) {
                    // console.log(d[1]);
                    return x(d[1]);
                })
                .y(function (d) {
                    // console.log(d[0]);
                    return y(d[0]);
                });

            var svg = d3.select("#progress_chart").append("svg")  //PLACEMENT GOES HERE  <---------------
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            var largestSubmittedTimeStamp = submissions[submissions_length-1];
            var indexForTime;
            var largestArrayForRange;
            if(largestSubmittedTimeStamp.cause == "assess"){
                largestArrayForRange = submissionsScoreAndTimeAssess;
                indexForTime = 1;
            } else if(largestSubmittedTimeStamp.cause == "submit"){
                largestArrayForRange = submissionsScoreAndTimeSubmits;
                indexForTime = 1;
            } else if(largestSubmittedTimeStamp.cause == "run"){
                largestArrayForRange = submissionsScoreAndTimeRuns;
                indexForTime = 0;

            }

            x.domain(d3.extent(largestArrayForRange, function (d) {
                // console.log(d[1]);
                return (d[indexForTime]);
            }));
            y.domain(d3.extent(submissionsScoreAndTimeAssess, function (d) {
                // console.log(d[0]);
                return (d[0]);
            }));

            svg.append("g") //x axis
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis);

            svg.append("text")// x axis label
                .attr("class", "x axis")
                .attr("text-anchor", "middle")
                .attr("x", width / 2)
                .attr("y", height)
                .attr("dy", ((height / 20) + 20) + 'px')
                .text("Time Spent on Assignment (Minutes)")
                .style('font-size', 14);

            svg.append("g") // y axis
                .attr("class", "y axis")
                .call(yAxis);

            svg.append("text") // y axis label
                .attr("transform", "rotate(-90)")
                .attr("x", -height / 2)
                .attr("dy", "-3em")
                .style("text-anchor", "middle")
                .text("Score")
                .style('font-size', 14);

            svg.append("text")// Title
                .attr("class", "x axis")
                .attr("text-anchor", "middle")
                .attr("x", (width / 2))//+300)
                .attr("y", 0)
                .attr("dy", '-1.5em')
                .text("Assesses Timeline")
                .style('font-size', 20)
                .style('text-decoration', 'underline');

            //
            // svg.append("path")
            //    //.datum()
            //    .attr("class", "line")
            //    .attr('id', 'myPath')// new
            //    .attr("stroke", "black")
            //    .attr("stroke-width", 5)
            //    .attr("fill", "none")// end new
            //    .attr("d", line(submissionsScoreAndTimeAssess));//---

            svg.append("path")
                .datum(submissionsScoreAndTimeAssess)
                .attr("class", "line")
                .attr('id', 'myPath')// new
                .attr("stroke", "orange")
                .attr("stroke-width", 5)
                .attr("fill", "none")// end new
                .attr("d", line);//---

            svg.selectAll("dot") // Add dots to assesses
                .data(submissionsScoreAndTimeAssess)
                .enter().append("circle")
                .attr("r", 3.5)
                .attr("cx", function(d) { return x(d[1]); })
                .attr("cy", function(d) { return y(d[0]); });

            svg.append("path")
                .datum(submissionsScoreAndTimeSubmits)
                .attr("class", "line2")
                .attr('id', 'myPath')// new
                .attr("stroke", "blue")
                .attr("stroke-width", 5)
                .attr("fill", "none")// end new
                .attr("d", line);//---

            svg.selectAll("dot") // Add dots to submits
                .data(submissionsScoreAndTimeSubmits)
                .enter().append("circle")
                .attr("r", 3.5)
                .attr("cx", function(d) { return x(d[1]); })
                .attr("cy", function(d) { return y(d[0]); });

            for (var i = 0; i < submissionsScoreAndTimeRuns.length; i++) {
                svg.append("line")
                    .attr("stroke", "red")
                    .attr("stroke-width", 1)
                    .attr("fill", "none")// end new
                    .attr("y1", y(0))
                    .attr("y2", 0)
                    .attr("x1", x(submissionsScoreAndTimeRuns[i]))
                    .attr("x2", x(submissionsScoreAndTimeRuns[i]));
            }

            var color_hash = {  0 : ["Submissions", "blue"],
                1 : ["Assesses", "orange"],
                2 : ["Runs", "red"]
            };

            // add legend
            var legend = svg.append("g")
                .attr("class", "legend")
                .attr("x", 65)
                .attr("y", 25)
                .attr("height", 100)
                .attr("width", 100);

            var dataset = [submissionsScoreAndTimeSubmits,submissionsScoreAndTimeAssess, submissionsScoreAndTimeRuns];
            var yOffset = -70;

            legend.selectAll('g').data(dataset)
                .enter()
                .append('g')
                .each(function(d, i) {
                    var g = d3.select(this);
                    g.append("rect")
                        .attr("x", 20)
                        .attr("y", i*25 + yOffset)// + 8
                        .attr("width", 10)
                        .attr("height", 10)
                        .style("fill", color_hash[String(i)][1]);

                    g.append("text")
                        .attr("x", 40)
                        .attr("y", i * 25 + yOffset + 10)// + 18
                        .attr("height",30)
                        .attr("width",100)
                        .style("fill", color_hash[String(i)][1])
                        .text(color_hash[String(i)][0]);

                });

            // function type(d) {
            //     d.frequency = +d.frequency;
            //     return d;
            // }

            //.on("mousemove", mMove)//new again
            //.append("title");

        }

        try{
            graph_assesses();
        } catch(err){
            // not enough data
        }

  }

});
<commit_msg>fixed graph sizing<commit_after>$(function() {
    // http://localhost:3333/exercises/38/statistics good for testing
    // originally at--> localhost:3333/exercises/69/statistics

    if ($.isController('exercises') && $('.graph-functions-2').isPresent()) {
        // GET THE DATA
        var submissions = $('#data').data('submissions');
        var submissions_length = submissions.length;

        submissionsScoreAndTimeAssess = [[0,0]];
        submissionsScoreAndTimeSubmits = [[0,0]];
        submissionsScoreAndTimeRuns = [];
        submissionsSaves = [];
        submissionsAutosaves = [];
        var maximumValue = 0;

        var wtimes = $('#wtimes').data('working_times'); //.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(working_times_until)

        // console.log(submissions);
        // console.log(wtimes);

        for (var i = 0;i<submissions_length;i++){
            var submission = submissions[i];
            var workingTime;
            var submissionArray;

            workingTime = get_minutes(wtimes[i]);
            submissionArray = [submission.score, 0];

            if (workingTime > 0) {
                submissionArray[1] = workingTime;
            }
            if(submission.score>maximumValue){
                maximumValue = submission.score;
            }

            if(submission.cause == "assess"){
                submissionsScoreAndTimeAssess.push(submissionArray);
            } else if(submission.cause == "submit"){
                submissionsScoreAndTimeSubmits.push(submissionArray);
            } else if(submission.cause == "run"){
                submissionsScoreAndTimeRuns.push(submissionArray[1]);
            } else if(submission.cause == "autosave"){
                submissionsAutosaves.push(submissionArray[1]);
            }  else if(submission.cause == "save"){
                submissionsSaves.push(submissionArray[1]);
            }
        }
        // console.log(submissionsScoreAndTimeAssess.length);
        // console.log(submissionsScoreAndTimeSubmits);
        // console.log(submissionsScoreAndTimeRuns);

        function get_minutes (time_stamp) {
            try {
                hours = time_stamp.split(":")[0];
                minutes = time_stamp.split(":")[1];
                seconds = time_stamp.split(":")[2];
                seconds /= 60;
                minutes = parseFloat(hours * 60) + parseInt(minutes) + seconds;
                if (minutes > 0){
                    return minutes;
                } else{
                    return parseFloat(seconds/60);
                }
            } catch (err) {
                return 0;
            }
        }

        function getWidth() {
            if (self.innerHeight) {
                return self.innerWidth;
            }

            if (document.documentElement && document.documentElement.clientWidth) {
                return document.documentElement.clientWidth;
            }

            if (document.body) {
                return document.body.clientWidth;
            }
        }

        function graph_assesses() {
            // MAKE THE GRAPH
            var width_ratio = .8;
            if (getWidth()*width_ratio > 1000){
                width_ratio = 1000/getWidth();
            }
            var height_ratio = .7; // percent of height

            var margin = {top: 100, right: 20, bottom: 70, left: 70},//30,50
                width = (getWidth() * width_ratio) - margin.left - margin.right,
                height = (width * height_ratio) - margin.top - margin.bottom;

            // Set the ranges
            var x = d3.scale.linear().range([0, width]);
            var y = d3.scale.linear().range([height,0]);

            //var x = d3.scale.linear()
            //    .range([0, width]);
            //var y = d3.scale.linear()
            //    .range([0,height]); // - (height/20

            var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom")
                .ticks(20);

            var yAxis = d3.svg.axis()
                .scale(d3.scale.linear().domain([0,maximumValue]).range([height,0]))//y
                // .scale(y)
                .orient("left")
                .ticks(maximumValue)
                .innerTickSize(-width)
                .outerTickSize(0);

            //var line = d3.svg.line()
            //    .x(function(d) { return x(d.date); })
            //    .y(function(d) { return y(d.close); });

            var line = d3.svg.line()
                .x(function (d) {
                    // console.log(d[1]);
                    return x(d[1]);
                })
                .y(function (d) {
                    // console.log(d[0]);
                    return y(d[0]);
                });

            var svg = d3.select("#progress_chart").append("svg")  //PLACEMENT GOES HERE  <---------------
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            var largestSubmittedTimeStamp = submissions[submissions_length-1];
            var largestArrayForRange;

            if(largestSubmittedTimeStamp.cause == "assess"){
                largestArrayForRange = submissionsScoreAndTimeAssess;
                indexForTime = 1;
            } else if(largestSubmittedTimeStamp.cause == "submit"){
                largestArrayForRange = submissionsScoreAndTimeSubmits;
                indexForTime = 1;
            } else if(largestSubmittedTimeStamp.cause == "run"){
                largestArrayForRange = submissionsScoreAndTimeRuns;
                indexForTime = 0;
                x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true);
            } else if(largestSubmittedTimeStamp.cause == "submit"){
                largestArrayForRange = submissionsScoreAndTimeSubmits;
                x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true);
            } else if(largestSubmittedTimeStamp.cause == "run"){
                largestArrayForRange = submissionsScoreAndTimeRuns;
                x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true);
            } else if(largestSubmittedTimeStamp.cause == "autosave"){
                largestArrayForRange = submissionsAutosaves;
                x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true);
            } else if(largestSubmittedTimeStamp.cause == "save"){
                largestArrayForRange = submissionsSaves;
                x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true);
            }

            // x.domain(d3.extent(largestArrayForRange, function (d) {
            //     // console.log(d[1]);
            //     return (d[indexForTime]);
            // }));

            // take maximum value between assesses and submits
            var yDomain = submissionsScoreAndTimeAssess.concat(submissionsScoreAndTimeSubmits);
            y.domain(d3.extent(yDomain, function (d) {
                // console.log(d[0]);
                return (d[0]);
            }));
            // y.domain([0,2]).clamp(true);

            svg.append("g") //x axis
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis);

            svg.append("text")// x axis label
                .attr("class", "x axis")
                .attr("text-anchor", "middle")
                .attr("x", width / 2)
                .attr("y", height)
                .attr("dy", ((height / 20) + 20) + 'px')
                .text("Time Spent on Assignment (Minutes)")
                .style('font-size', 14);

            svg.append("g") // y axis
                .attr("class", "y axis")
                .call(yAxis);

            svg.append("text") // y axis label
                .attr("transform", "rotate(-90)")
                .attr("x", -height / 2)
                .attr("dy", "-3em")
                .style("text-anchor", "middle")
                .text("Score")
                .style('font-size', 14);

            svg.append("text")// Title
                .attr("class", "x axis")
                .attr("text-anchor", "middle")
                .attr("x", (width / 2))//+300)
                .attr("y", 0)
                .attr("dy", '-1.5em')
                .text("Assesses Timeline")
                .style('font-size', 20)
                .style('text-decoration', 'underline');

            //
            // svg.append("path")
            //    //.datum()
            //    .attr("class", "line")
            //    .attr('id', 'myPath')// new
            //    .attr("stroke", "black")
            //    .attr("stroke-width", 5)
            //    .attr("fill", "none")// end new
            //    .attr("d", line(submissionsScoreAndTimeAssess));//---

            svg.append("path")
                .datum(submissionsScoreAndTimeAssess)
                .attr("class", "line")
                .attr('id', 'myPath')// new
                .attr("stroke", "orange")
                .attr("stroke-width", 5)
                .attr("fill", "none")// end new
                .attr("d", line);//---

            svg.selectAll("dot") // Add dots to assesses
                .data(submissionsScoreAndTimeAssess)
                .enter().append("circle")
                .attr("r", 3.5)
                .attr("cx", function(d) { return x(d[1]); })
                .attr("cy", function(d) { return y(d[0]); });

            svg.append("path")
                .datum(submissionsScoreAndTimeSubmits)
                .attr("class", "line2")
                .attr('id', 'myPath')// new
                .attr("stroke", "blue")
                .attr("stroke-width", 5)
                .attr("fill", "none")// end new
                .attr("d", line);//---

            svg.selectAll("dot") // Add dots to submits
                .data(submissionsScoreAndTimeSubmits)
                .enter().append("circle")
                .attr("r", 3.5)
                .attr("cx", function(d) { return x(d[1]); })
                .attr("cy", function(d) { return y(d[0]); });

            for (var i = 0; i < submissionsScoreAndTimeRuns.length; i++) {
                svg.append("line")
                    .attr("stroke", "red")
                    .attr("stroke-width", 1)
                    .attr("fill", "none")// end new
                    .attr("y1", y(0))
                    .attr("y2", 0)
                    .attr("x1", x(submissionsScoreAndTimeRuns[i]))
                    .attr("x2", x(submissionsScoreAndTimeRuns[i]));
            }

            var color_hash = {  0 : ["Submissions", "blue"],
                1 : ["Assesses", "orange"],
                2 : ["Runs", "red"]
            };

            // add legend
            var legend = svg.append("g")
                .attr("class", "legend")
                .attr("x", 65)
                .attr("y", 25)
                .attr("height", 100)
                .attr("width", 100);

            var dataset = [submissionsScoreAndTimeSubmits,submissionsScoreAndTimeAssess, submissionsScoreAndTimeRuns];
            var yOffset = -70;

            legend.selectAll('g').data(dataset)
                .enter()
                .append('g')
                .each(function(d, i) {
                    var g = d3.select(this);
                    g.append("rect")
                        .attr("x", 20)
                        .attr("y", i*25 + yOffset)// + 8
                        .attr("width", 10)
                        .attr("height", 10)
                        .style("fill", color_hash[String(i)][1]);

                    g.append("text")
                        .attr("x", 40)
                        .attr("y", i * 25 + yOffset + 10)// + 18
                        .attr("height",30)
                        .attr("width",100)
                        .style("fill", color_hash[String(i)][1])
                        .text(color_hash[String(i)][0]);

                });

            // function type(d) {
            //     d.frequency = +d.frequency;
            //     return d;
            // }

            //.on("mousemove", mMove)//new again
            //.append("title");

        }

        try{
            graph_assesses();
        } catch(err){
            alert("could not draw the graph");
        }

  }

});
<|endoftext|>


<commit_before>/**
 * @module myModule
 * @summary: This module's purpose is to:
 *
 * @description:
 *
 * Author: Justin Mooser
 * Created On: 2015-06-01.
 * @license Apache-2.0
 */

"use strict";

var util = require('util');

/**
 *
 * @param config
 * @param storage .save(): promise rejects if it should not be retried.
 * @param longTermStorage - .save(): resolves url to saved resource.  Rejects if it should not be retried.
 * @returns {{}}
 */
module.exports = function construct(config, storage, longTermStorage) {
  var m = {};
  config = config ? config : {};
  config = _.defaults(config, {
    saveEventsToLongTermStorage: true
  });

  m.processEvent = function(eventPayload, eventType) {
    var eventRow;
    return p.resolve().then(function() {
      return extractEventRow(eventPayload,eventType);
    })
    .then(function(eventrow) {
      eventRow = eventrow;
      console.log('Saving to S3...', eventRow.local_ts);
      return saveToLongTermStorage(eventRow, eventPayload)
        .then(function(result) {
          console.log('Done saving to S3...', eventRow.local_ts);
          return result;
        })
        .catch(function(err) {
          console.error('Failed to save to S3.', err);
        });
    })
    .then(function(longTermStorageUrl) {
      console.log('Saved to', longTermStorageUrl);
        // the database only allows 100 characters.
      if (eventRow.details.length && eventRow.details.length > 100) {
        eventRow.details = eventRow.details.substr(0, 99);
      }
      if(eventRow.label.length > 24) {
        eventRow.label = eventRow.label.substr(0,24);
      }
      eventRow.url = longTermStorageUrl;
      eventRow.timestamp = new Date().getTime();

      return storage.save('eventlog', eventRow)
        .catch(function(err) {
          console.error('Error saving tracked event to storage.');
          console.error(err);
        });
    })
    .catch(function(err) {
      console.error('Unexpected error processing tracked event.');
      console.error(err);
    });
  };

  function extractEventRow(eventPayload, eventType) {
    eventType = eventType || 'bunyan-v1';
    // you have to extract the payload and the essential details about the logged event.
    if (eventType=='bunyan-v1') {
      var details = extractDetailsObject(eventPayload);

      var eventRow = {
        details: JSON.stringify(details) || '{}',
        local_ts: Math.floor(Date.parse(eventPayload.time)/1000),
        component: eventPayload.name || 'unknown',
        host: eventPayload.hostname || 'unknown',
        label: eventPayload.eventLabel || 'unknown',
        env: eventPayload.env || 'env',
        pid: eventPayload.pid || 'unknown',
        app: (eventPayload.app || 'app') + '-' + (eventPayload.env || 'env'),
        version: eventType || 'unknown',
        level: eventPayload.level || 'unspecified'
      };

      eventRow.key = util.format('%s-%s', eventRow.app, eventRow.label);

      return eventRow;
    }
  }

  function fixJSON(str) {
    var json = str.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2": ');
    json = json.replace(/\'/g, '"');
    return json;
  }

  function extractDetailsObject(eventPayload) {
    var msg = eventPayload.msg;

    var detailsObjectStart = msg.indexOf('{');
    var parse = false;
    if (detailsObjectStart>=0) {
      parse = true;
    }
    else {
      detailsObjectStart = msg.indexOf('[');
      if (detailsObjectStart >= 0) parse = true;
    }

    if (parse) {
      var json = fixJSON(msg.substr(detailsObjectStart));
      try {
        return JSON.parse(json);
      } catch (ex) {
        console.log('WARNING: details could not be parsed.', json, ex);
      }
    }

    if (eventPayload.eventLabel && eventPayload.eventLabel.length) {
      return { details: msg.substr(eventPayload.eventLabel.length+1) };
    } else {
      return { details: msg };
    }
  }

  /**
   * Container names are per application per environment.
   * @note: In S3 a container name would reference a bucket.
   */
  function chooseStorageContainerName(eventRow) {
    if (!eventRow.app) throw 'chooseStorageContainerName eventRow.app is empty';
    if (!eventRow.env) warn('chooseStorageContainerName eventRow.env is empty');
    return eventRow.app;
  }

  /**
   * Constructs an S3 friendly URL (uses random hash at start of key name to optimize S3 performance)
   * @param eventRow
   */
  function chooseStorageKey(eventRow) {
    return util.format("%s-%s.%s", new Date().toISOString(), eventRow.component, eventRow.version);
  }

  function saveToLongTermStorage(eventRow, eventPayload) {
    var container = chooseStorageContainerName(eventRow);
    var key = chooseStorageKey(eventRow);

    return longTermStorage.save(container, key, eventPayload);
  }

  return m;
};
<commit_msg>bugfix<commit_after>/**
 * @module myModule
 * @summary: This module's purpose is to:
 *
 * @description:
 *
 * Author: Justin Mooser
 * Created On: 2015-06-01.
 * @license Apache-2.0
 */

"use strict";

var util = require('util');

/**
 *
 * @param config
 * @param storage .save(): promise rejects if it should not be retried.
 * @param longTermStorage - .save(): resolves url to saved resource.  Rejects if it should not be retried.
 * @returns {{}}
 */
module.exports = function construct(config, storage, longTermStorage) {
  var m = {};
  config = config ? config : {};
  config = _.defaults(config, {
    saveEventsToLongTermStorage: true
  });

  m.processEvent = function(eventPayload, eventType) {
    var eventRow;
    return p.resolve().then(function() {
      return extractEventRow(eventPayload,eventType);
    })
    .then(function(eventrow) {
      eventRow = eventrow;
      console.log('Saving to S3...', eventRow.local_ts);
      return saveToLongTermStorage(eventRow, eventPayload)
        .then(function(result) {
          console.log('Done saving to S3...', eventRow.local_ts);
          return result;
        })
        .catch(function(err) {
          console.error('Failed to save to S3.', err);
        });
    })
    .then(function(longTermStorageUrl) {
      console.log('Saved to', longTermStorageUrl);
        // the database only allows 100 characters.
      if (eventRow.details.length && eventRow.details.length > 100) {
        eventRow.details = eventRow.details.substr(0, 99);
      }
      if(eventRow.label.length > 24) {
        eventRow.label = eventRow.label.substr(0,24);
      }
      eventRow.url = longTermStorageUrl;
      eventRow.timestamp = new Date().getTime();

      return storage.save('eventlog', eventRow)
        .catch(function(err) {
          console.error('Error saving tracked event to storage.');
          console.error(err);
        });
    })
    .catch(function(err) {
      console.error('Unexpected error processing tracked event.');
      console.error(err);
    });
  };

  function extractEventRow(eventPayload, eventType) {
    eventType = eventType || 'bunyan-v1';
    // you have to extract the payload and the essential details about the logged event.
    if (eventType=='bunyan-v1') {
      var details = extractDetailsObject(eventPayload);

      var eventRow = {
        details: JSON.stringify(details) || '{}',
        local_ts: Math.floor(Date.parse(eventPayload.time)/1000),
        component: eventPayload.name || 'unknown',
        host: eventPayload.hostname || 'unknown',
        label: eventPayload.eventLabel || 'unknown',
        env: eventPayload.env || 'env',
        pid: eventPayload.pid || 'unknown',
        app: (eventPayload.app || 'app') + '-' + (eventPayload.env || 'env'),
        version: eventType || 'unknown',
        level: eventPayload.level || 'unspecified'
      };

      eventRow.key = util.format('%s-%s', eventRow.app, eventRow.label);

      return eventRow;
    }
  }

  function fixJSON(str) {
    var json = str.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2": ');
    json = json.replace(/\'/g, '"');
    return json;
  }

  function extractDetailsObject(eventPayload) {
    var msg = eventPayload.msg;

    var detailsObjectStart = msg.indexOf('{');
    var parse = false;
    if (detailsObjectStart>=0) {
      parse = true;
    }
    else {
      detailsObjectStart = msg.indexOf('[');
      if (detailsObjectStart >= 0) parse = true;
    }

    if (parse) {
      var json = msg.substr(detailsObjectStart);
      try {
        return JSON.parse(json);
      } catch (ex) {
        json = fixJSON(json);
        try {
          return JSON.parse(json);
        }
        catch (ex) {
          console.log('WARNING: details could not be parsed.', json, ex);
          return { details: msg.substr(detailsObjectStart) };
        }
      }
    }

    if (eventPayload.eventLabel && eventPayload.eventLabel.length) {
      return { details: msg.substr(eventPayload.eventLabel.length+1) };
    } else {
      return { details: msg };
    }
  }

  /**
   * Container names are per application per environment.
   * @note: In S3 a container name would reference a bucket.
   */
  function chooseStorageContainerName(eventRow) {
    if (!eventRow.app) throw 'chooseStorageContainerName eventRow.app is empty';
    if (!eventRow.env) warn('chooseStorageContainerName eventRow.env is empty');
    return eventRow.app;
  }

  /**
   * Constructs an S3 friendly URL (uses random hash at start of key name to optimize S3 performance)
   * @param eventRow
   */
  function chooseStorageKey(eventRow) {
    return util.format("%s-%s.%s", new Date().toISOString(), eventRow.component, eventRow.version);
  }

  function saveToLongTermStorage(eventRow, eventPayload) {
    var container = chooseStorageContainerName(eventRow);
    var key = chooseStorageKey(eventRow);

    return longTermStorage.save(container, key, eventPayload);
  }

  return m;
};
<|endoftext|>