<commit_before>require([
  'esri/renderers/ClassBreaksRenderer',
  'dojo/_base/Color',
  'esri/layers/FeatureLayer',
  'esri/layers/LayerDrawingOptions',
  'esri/dijit/Legend',
  'esri/map',
  'esri/symbols/SimpleLineSymbol',
  'esri/symbols/SimpleMarkerSymbol',
  'dojo/_base/array',
  'dojo/parser',
  'esri/request',
  'dojo/domReady!'
],
function(
  ClassBreaksRenderer,
  Color,
  FeatureLayer,
  LayerDrawingOptions,
  Legend,
  Map,
  SimpleLineSymbol,
  SimpleMarkerSymbol,
  array,
  parser,
  request) {
  parser.parse();
  var AGG_URL = 'http://utility.arcgis.com/usrsvcs/servers/06fb34d68e7a4673ba885015b671387b/rest/services/CCRPC/SidewalkInventoryScoreAggregated/MapServer',
    AGG_DEFAULT_FEATURE = 'Sidewalk',
    AGG_DEFAULT_FIELD = 'ScoreCompliance',
    IND_URL = 'http://utility.arcgis.com/usrsvcs/servers/88a6a9f6dc45461f820659d2d0f13fff/rest/services/CCRPC/SidewalkInventoryScore/MapServer',
    BREAKS = [
      {
        maxValue: 60,
        label: '0 to 60',
        color: '#e61c1f'
      },
      {
        maxValue: 70,
        label: '> 60 to 70',
        color: '#ffb061'
      },
      {
        maxValue: 80,
        label: '> 70 to 80',
        color: '#fcfc5d'
      },
      {
        maxValue: 90,
        label: '> 80 to 90',
        color: '#80cee8'
      },
      {
        maxValue: 100,
        label: '> 90 to 100',
        color: '#2e80bf'
      }
    ],
    makeIndLayer = function(idx, markerType, markerSize) {
      var layer = new FeatureLayer(IND_URL + '/' + idx, {
        mode: FeatureLayer.MODE_ONDEMAND,
        outFields: ['*']
      });
      layer.setScaleRange(10001, 0);
      layer.setRenderer(makeIndRenderer(markerType, markerSize));
      return layer;
    },
    makeIndRenderer = function(markerType, markerSize) {
      var renderer = new ClassBreaksRenderer(
        makeMarkerSymbol(markerType, markerSize, '#bbbbbb'),
        'ScoreCompliance');
      array.forEach(BREAKS, function(b, i) {
        renderer.addBreak(
          (i > 0) ? BREAKS[i-1].maxValue + 0.000001 : 0,
          b.maxValue,
          makeMarkerSymbol(markerType, markerSize, b.color));
      });
      return renderer;
    },
    makeMarkerSymbol = function(markerType, markerSize, fillColor) {
      return new SimpleMarkerSymbol(
        markerType,
        markerSize,
        new SimpleLineSymbol(
          SimpleLineSymbol.STYLE_SOLID,
          new Color('#000000'),
          1
        ),
        new Color(fillColor)
      );
    },
    updateAggRenderer = function() {
      aggRenderer.attributeField =
        fieldName.options[fieldName.selectedIndex].value;
      aggLayer.setRenderer(aggRenderer);
      aggLayer.redraw();
    },
    initLegend = function() {
      var legend = new Legend({
        map: map,
        layerInfos: [{
          layer: aggLayer,
          title: 'Legend'
        }],
        autoUpdate: false
      }, document.getElementById('legend'));
      legend.startup();
    },
    map = new Map('map', {
      center: [-88.2, 40.1],
      zoom: 11,
      basemap: 'gray-vector'
    }),
    aggLayer = new FeatureLayer(AGG_URL + '/0', {
      mode: FeatureLayer.MODE_SNAPSHOT,
      outFields: ['*'],
      opacity: 0.5
    }),
    aggRenderer,
    crLayer = makeIndLayer(0, SimpleMarkerSymbol.STYLE_CIRCLE, 10),
    // cwLayer = makeIndLayer(1, SimpleMarkerSymbol.STYLE_CROSS, 10),
    // psLayer = makeIndLayer(2, SimpleMarkerSymbol.STYLE_DIAMOND, 10),
    // swLayer = makeIndLayer(3, SimpleMarkerSymbol.STYLE_CIRCLE, 10),
    fieldName = document.getElementById('fieldName'),
    featureType = document.getElementById('featureType'),
    updateButton = document.getElementById('updateMap'),
    featureFields = {},
    populateFieldList = function(featureType) {
      while (fieldName.firstChild) fieldName.removeChild(fieldName.firstChild);
      array.forEach(featureFields[featureType], function(field, i) {
        var option = document.createElement('option');
        option.value = field.value;
        option.appendChild(document.createTextNode(
          field.label.substr(0, field.label.length - 6)));
        fieldName.appendChild(option);
        if (field.label == 'Compliance Score') fieldName.selectedIndex = i;
      });
    };

    aggLayer.on('load', function(e) {
      initLegend();
      aggLayer.setScaleRange(0, 10000);
    });
    map.addLayer(aggLayer);
    map.addLayer(crLayer);

    request({
      url: AGG_URL + '/0',
      content: {
        f: 'json'
      },
      callbackParamName: 'callback'
    }).then(function(res) {
      // Set the minimum value for the renderer to 0.
      res.drawingInfo.renderer.minValue = 0;
      aggRenderer = new ClassBreaksRenderer(res.drawingInfo.renderer);

      // Populate the field choices object.
      array.forEach(
          featureType.children,
          function(ftOption) {
        featureFields[ftOption.value] = [];
        array.forEach(res.fields, function(field) {
          if (
              field.name.substr(0, ftOption.value.length) == ftOption.value &&
              field.alias.substr(field.alias.length - 5) == 'Score') {
            featureFields[ftOption.value].push({
              value: field.name,
              label: field.alias.substr(ftOption.text.length)
            });
          }
        });
      });

      // Set up the change handler for feature type.
      featureType.onchange = function() {
        populateFieldList(this.options[this.selectedIndex].value);
      };

      // Set the initial values of the feature type and field name selects.
      featureType.selectedIndex = 0;
      featureType.onchange();

      // Set up the click handler for the update map button.
      updateButton.onclick = function() {
        updateAggRenderer();
      };
      updateButton.removeAttribute('disabled');
    });

});
<commit_msg>Moved field choice generation to a function.<commit_after>require([
  'esri/renderers/ClassBreaksRenderer',
  'dojo/_base/Color',
  'esri/layers/FeatureLayer',
  'esri/layers/LayerDrawingOptions',
  'esri/dijit/Legend',
  'esri/map',
  'esri/symbols/SimpleLineSymbol',
  'esri/symbols/SimpleMarkerSymbol',
  'dojo/_base/array',
  'dojo/parser',
  'esri/request',
  'dojo/domReady!'
],
function(
  ClassBreaksRenderer,
  Color,
  FeatureLayer,
  LayerDrawingOptions,
  Legend,
  Map,
  SimpleLineSymbol,
  SimpleMarkerSymbol,
  array,
  parser,
  request) {
  parser.parse();
  var AGG_URL = 'http://utility.arcgis.com/usrsvcs/servers/06fb34d68e7a4673ba885015b671387b/rest/services/CCRPC/SidewalkInventoryScoreAggregated/MapServer',
    AGG_DEFAULT_FEATURE = 'Sidewalk',
    AGG_DEFAULT_FIELD = 'ScoreCompliance',
    IND_URL = 'http://utility.arcgis.com/usrsvcs/servers/88a6a9f6dc45461f820659d2d0f13fff/rest/services/CCRPC/SidewalkInventoryScore/MapServer',
    BREAKS = [
      {
        maxValue: 60,
        label: '0 to 60',
        color: '#e61c1f'
      },
      {
        maxValue: 70,
        label: '> 60 to 70',
        color: '#ffb061'
      },
      {
        maxValue: 80,
        label: '> 70 to 80',
        color: '#fcfc5d'
      },
      {
        maxValue: 90,
        label: '> 80 to 90',
        color: '#80cee8'
      },
      {
        maxValue: 100,
        label: '> 90 to 100',
        color: '#2e80bf'
      }
    ],
    makeIndLayer = function(idx, markerType, markerSize) {
      var layer = new FeatureLayer(IND_URL + '/' + idx, {
        mode: FeatureLayer.MODE_ONDEMAND,
        outFields: ['*']
      });
      layer.setScaleRange(10001, 0);
      layer.setRenderer(makeIndRenderer(markerType, markerSize));
      return layer;
    },
    makeIndRenderer = function(markerType, markerSize) {
      var renderer = new ClassBreaksRenderer(
        makeMarkerSymbol(markerType, markerSize, '#bbbbbb'),
        'ScoreCompliance');
      array.forEach(BREAKS, function(b, i) {
        renderer.addBreak(
          (i > 0) ? BREAKS[i-1].maxValue + 0.000001 : 0,
          b.maxValue,
          makeMarkerSymbol(markerType, markerSize, b.color));
      });
      return renderer;
    },
    makeMarkerSymbol = function(markerType, markerSize, fillColor) {
      return new SimpleMarkerSymbol(
        markerType,
        markerSize,
        new SimpleLineSymbol(
          SimpleLineSymbol.STYLE_SOLID,
          new Color('#000000'),
          1
        ),
        new Color(fillColor)
      );
    },
    updateAggRenderer = function() {
      aggRenderer.attributeField =
        fieldName.options[fieldName.selectedIndex].value;
      aggLayer.setRenderer(aggRenderer);
      aggLayer.redraw();
    },
    initLegend = function() {
      var legend = new Legend({
        map: map,
        layerInfos: [{
          layer: aggLayer,
          title: 'Legend'
        }],
        autoUpdate: false
      }, document.getElementById('legend'));
      legend.startup();
    },
    map = new Map('map', {
      center: [-88.2, 40.1],
      zoom: 11,
      basemap: 'gray-vector'
    }),
    aggLayer = new FeatureLayer(AGG_URL + '/0', {
      mode: FeatureLayer.MODE_SNAPSHOT,
      outFields: ['*'],
      opacity: 0.5
    }),
    aggRenderer,
    crLayer = makeIndLayer(0, SimpleMarkerSymbol.STYLE_CIRCLE, 10),
    // cwLayer = makeIndLayer(1, SimpleMarkerSymbol.STYLE_CROSS, 10),
    // psLayer = makeIndLayer(2, SimpleMarkerSymbol.STYLE_DIAMOND, 10),
    // swLayer = makeIndLayer(3, SimpleMarkerSymbol.STYLE_CIRCLE, 10),
    fieldName = document.getElementById('fieldName'),
    featureType = document.getElementById('featureType'),
    updateButton = document.getElementById('updateMap'),
    featureFields = {},
    populateFieldChoices = function(fields) {
      array.forEach(
          featureType.children,
          function(ftOption) {
        featureFields[ftOption.value] = [];
        array.forEach(fields, function(field) {
          if (
              field.name.substr(0, ftOption.value.length) == ftOption.value &&
              field.alias.substr(field.alias.length - 5) == 'Score') {
            featureFields[ftOption.value].push({
              value: field.name,
              label: field.alias.substr(ftOption.text.length)
            });
          }
        });
      });
    },
    populateFieldList = function(featureType) {
      while (fieldName.firstChild) fieldName.removeChild(fieldName.firstChild);
      array.forEach(featureFields[featureType], function(field, i) {
        var option = document.createElement('option');
        option.value = field.value;
        option.appendChild(document.createTextNode(
          field.label.substr(0, field.label.length - 6)));
        fieldName.appendChild(option);
        if (field.label == 'Compliance Score') fieldName.selectedIndex = i;
      });
    };

    aggLayer.on('load', function(e) {
      initLegend();
      aggLayer.setScaleRange(0, 10000);
    });
    map.addLayer(aggLayer);
    map.addLayer(crLayer);

    request({
      url: AGG_URL + '/0',
      content: {
        f: 'json'
      },
      callbackParamName: 'callback'
    }).then(function(res) {
      // Set the minimum value for the renderer to 0.
      res.drawingInfo.renderer.minValue = 0;
      aggRenderer = new ClassBreaksRenderer(res.drawingInfo.renderer);

      // Populate the field choices object.
      populateFieldChoices(res.fields);

      // Set up the change handler for feature type.
      featureType.onchange = function() {
        populateFieldList(this.options[this.selectedIndex].value);
      };

      // Set the initial values of the feature type and field name selects.
      featureType.selectedIndex = 0;
      featureType.onchange();

      // Set up the click handler for the update map button.
      updateButton.onclick = function() {
        updateAggRenderer();
      };
      updateButton.removeAttribute('disabled');
    });

});
<|endoftext|>


<commit_before>/*jslint indent: 2, unparam: true*/
/*global document: false, window: false, XMLHttpRequest: false, chrome: false, btoa: false, localStorage:false */
"use strict";

var TogglButton = chrome.extension.getBackgroundPage().TogglButton;
var Db = chrome.extension.getBackgroundPage().Db;

var Settings = {
  $startAutomatically: null,
  $stopAutomatically: null,
  $showRightClickButton: null,
  $postPopup: null,
  $socket: null,
  $nanny: null,
  $pomodoroMode: null,
  $pomodoroSound: null,
  $pomodoroStopTimeTracking: null,
  $stopAtDayEnd: null,
  showPage: function () {
    document.querySelector("#version").innerHTML = "(" + chrome.runtime.getManifest().version + ")";
    Settings.setFromTo();
    document.querySelector("#nag-nanny-interval").value = Db.get("nannyInterval") / 60000;
    Settings.toggleState(Settings.$showRightClickButton, Db.get("showRightClickButton"));
    Settings.toggleState(Settings.$startAutomatically, Db.get("startAutomatically"));
    Settings.toggleState(Settings.$stopAutomatically, Db.get("stopAutomatically"));
    Settings.toggleState(Settings.$postPopup, Db.get("showPostPopup"));
    Settings.toggleState(Settings.$nanny, Db.get("nannyCheckEnabled"));
    Settings.toggleSetting(Settings.$socket, Db.get("socketEnabled") && TogglButton.$socket);
    Settings.toggleState(Settings.$idleDetection, Db.get("idleDetectionEnabled"));
    Settings.toggleState(Settings.$pomodoroMode, Db.get("pomodoroModeEnabled"));
    Settings.toggleState(Settings.$pomodoroSound, Db.get("pomodoroSoundEnabled"));
    Settings.toggleState(Settings.$pomodoroStopTimeTracking, Db.get("pomodoroStopTimeTrackingWhenTimerEnds"));
    document.querySelector("#pomodoro-interval").value = Db.get("pomodoroInterval");

    Settings.toggleState(Settings.$stopAtDayEnd, Db.get("stopAtDayEnd"));
    document.querySelector("#day-end-time").value = Db.get("dayEndTime");

    TogglButton.analytics("settings", null);
  },
  setFromTo: function () {
    var fromTo = Db.get("nannyFromTo").split("-");
    document.querySelector("#nag-nanny-from").value = fromTo[0];
    document.querySelector("#nag-nanny-to").value = fromTo[1];
  },
  toggleState: function (elem, state) {
    elem.checked = state;
  },
  toggleSetting: function (elem, state, type) {
    var request = {
      type: type,
      state: state
    };
    if (elem !== null) {
      Settings.toggleState(elem, state);
    }
    chrome.extension.sendMessage(request);
  },
  saveSetting: function (value, type) {
    Settings.toggleSetting(null, value, type);
  }
};
document.addEventListener('DOMContentLoaded', function (e) {
  Settings.$startAutomatically = document.querySelector("#start_automatically");
  Settings.$stopAutomatically = document.querySelector("#stop_automatically");
  Settings.$showRightClickButton = document.querySelector("#show_right_click_button");
  Settings.$postPopup = document.querySelector("#show_post_start_popup");
  Settings.$socket = document.querySelector("#websocket");
  Settings.$nanny = document.querySelector("#nag-nanny");
  Settings.$idleDetection = document.querySelector("#idle-detection");
  Settings.$pomodoroMode = document.querySelector("#pomodoro-mode");
  Settings.$pomodoroSound = document.querySelector("#enable-sound-signal");
  Settings.$pomodoroStopTimeTracking = document.querySelector("#pomodoro-stop-time");
  Settings.$stopAtDayEnd = document.querySelector("#stop-at-day-end");
  Settings.showPage();
  Settings.$showRightClickButton.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showRightClickButton") !== "true"), "toggle-right-click-button");
    TogglButton.toggleRightClickButton((localStorage.getItem("showRightClickButton") !== "true"));
  });
  Settings.$startAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("startAutomatically") !== "true"), "toggle-start-automatically");
  });
  Settings.$stopAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAutomatically") !== "true"), "toggle-stop-automatically");
  });
  Settings.$postPopup.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showPostPopup") !== "true"), "toggle-popup");
  });
  Settings.$socket.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("socketEnabled") !== "true"), "toggle-socket");
  });
  Settings.$nanny.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("nannyCheckEnabled") !== "true"), "toggle-nanny");
  });
  Settings.$idleDetection.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("idleDetectionEnabled") !== "true"), "toggle-idle");
  });
  Settings.$pomodoroMode.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroModeEnabled") !== "true"), "toggle-pomodoro");
  });
  Settings.$pomodoroSound.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroSoundEnabled") !== "true"), "toggle-pomodoro-sound");
  });
  Settings.$pomodoroStopTimeTracking.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroStopTimeTrackingWhenTimerEnds") !== "true"), "toggle-pomodoro-stop-time");
  });

  Settings.$stopAtDayEnd.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAtDayEnd") !== "true"), "toggle-stop-at-day-end");
  });
  document.querySelector(".tab-links").addEventListener('click', function (e) {
    var tab = e.target.getAttribute("data-tab");
    if (!document.querySelector(".tab-" + tab).classList.contains("active")) {
      document.querySelector(".tab.active").classList.remove("active");
      document.querySelector("header .active").classList.remove("active");
      document.querySelector(".tab-" + tab).classList.add("active");
      e.target.classList.add("active");
    }
  });

  document.querySelector("#nag-nanny-from").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    Settings.$fromTo = e.target.value + "-" + document.querySelector('#nag-nanny-to').value;
    Settings.saveSetting();
  });
  document.querySelector("#nag-nanny-to").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    var fromTo = document.querySelector('#nag-nanny-from').value + "-" + e.target.value;
    Settings.saveSetting(fromTo, "toggle-nanny-from-to");
  });
  document.querySelector("#nag-nanny-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#nag-nanny-interval').value * 60000), "toggle-nanny-interval");

  });

  document.querySelector("#pomodoro-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting(+(document.querySelector('#pomodoro-interval').value), "toggle-pomodoro-interval");

  });

  document.querySelector("#day-end-time").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#day-end-time').value), "toggle-day-end-time");
  });
});
<commit_msg>Fixed socket settings loading<commit_after>/*jslint indent: 2, unparam: true*/
/*global document: false, window: false, XMLHttpRequest: false, chrome: false, btoa: false, localStorage:false */
"use strict";

var TogglButton = chrome.extension.getBackgroundPage().TogglButton;
var Db = chrome.extension.getBackgroundPage().Db;

var Settings = {
  $startAutomatically: null,
  $stopAutomatically: null,
  $showRightClickButton: null,
  $postPopup: null,
  $socket: null,
  $nanny: null,
  $pomodoroMode: null,
  $pomodoroSound: null,
  $pomodoroStopTimeTracking: null,
  $stopAtDayEnd: null,
  showPage: function () {
    document.querySelector("#version").innerHTML = "(" + chrome.runtime.getManifest().version + ")";
    Settings.setFromTo();
    document.querySelector("#nag-nanny-interval").value = Db.get("nannyInterval") / 60000;
    Settings.toggleState(Settings.$showRightClickButton, Db.get("showRightClickButton"));
    Settings.toggleState(Settings.$startAutomatically, Db.get("startAutomatically"));
    Settings.toggleState(Settings.$stopAutomatically, Db.get("stopAutomatically"));
    Settings.toggleState(Settings.$postPopup, Db.get("showPostPopup"));
    Settings.toggleState(Settings.$nanny, Db.get("nannyCheckEnabled"));
    Settings.toggleState(Settings.$socket, Db.get("socketEnabled"));
    Settings.toggleState(Settings.$idleDetection, Db.get("idleDetectionEnabled"));
    Settings.toggleState(Settings.$pomodoroMode, Db.get("pomodoroModeEnabled"));
    Settings.toggleState(Settings.$pomodoroSound, Db.get("pomodoroSoundEnabled"));
    Settings.toggleState(Settings.$pomodoroStopTimeTracking, Db.get("pomodoroStopTimeTrackingWhenTimerEnds"));
    document.querySelector("#pomodoro-interval").value = Db.get("pomodoroInterval");

    Settings.toggleState(Settings.$stopAtDayEnd, Db.get("stopAtDayEnd"));
    document.querySelector("#day-end-time").value = Db.get("dayEndTime");

    TogglButton.analytics("settings", null);
  },
  setFromTo: function () {
    var fromTo = Db.get("nannyFromTo").split("-");
    document.querySelector("#nag-nanny-from").value = fromTo[0];
    document.querySelector("#nag-nanny-to").value = fromTo[1];
  },
  toggleState: function (elem, state) {
    elem.checked = state;
  },
  toggleSetting: function (elem, state, type) {
    var request = {
      type: type,
      state: state
    };
    if (elem !== null) {
      Settings.toggleState(elem, state);
    }
    chrome.extension.sendMessage(request);
  },
  saveSetting: function (value, type) {
    Settings.toggleSetting(null, value, type);
  }
};
document.addEventListener('DOMContentLoaded', function (e) {
  Settings.$startAutomatically = document.querySelector("#start_automatically");
  Settings.$stopAutomatically = document.querySelector("#stop_automatically");
  Settings.$showRightClickButton = document.querySelector("#show_right_click_button");
  Settings.$postPopup = document.querySelector("#show_post_start_popup");
  Settings.$socket = document.querySelector("#websocket");
  Settings.$nanny = document.querySelector("#nag-nanny");
  Settings.$idleDetection = document.querySelector("#idle-detection");
  Settings.$pomodoroMode = document.querySelector("#pomodoro-mode");
  Settings.$pomodoroSound = document.querySelector("#enable-sound-signal");
  Settings.$pomodoroStopTimeTracking = document.querySelector("#pomodoro-stop-time");
  Settings.$stopAtDayEnd = document.querySelector("#stop-at-day-end");
  Settings.showPage();
  Settings.$showRightClickButton.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showRightClickButton") !== "true"), "toggle-right-click-button");
    TogglButton.toggleRightClickButton((localStorage.getItem("showRightClickButton") !== "true"));
  });
  Settings.$startAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("startAutomatically") !== "true"), "toggle-start-automatically");
  });
  Settings.$stopAutomatically.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAutomatically") !== "true"), "toggle-stop-automatically");
  });
  Settings.$postPopup.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("showPostPopup") !== "true"), "toggle-popup");
  });
  Settings.$socket.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("socketEnabled") !== "true"), "toggle-socket");
  });
  Settings.$nanny.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("nannyCheckEnabled") !== "true"), "toggle-nanny");
  });
  Settings.$idleDetection.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("idleDetectionEnabled") !== "true"), "toggle-idle");
  });
  Settings.$pomodoroMode.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroModeEnabled") !== "true"), "toggle-pomodoro");
  });
  Settings.$pomodoroSound.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroSoundEnabled") !== "true"), "toggle-pomodoro-sound");
  });
  Settings.$pomodoroStopTimeTracking.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("pomodoroStopTimeTrackingWhenTimerEnds") !== "true"), "toggle-pomodoro-stop-time");
  });

  Settings.$stopAtDayEnd.addEventListener('click', function (e) {
    Settings.toggleSetting(e.target, (localStorage.getItem("stopAtDayEnd") !== "true"), "toggle-stop-at-day-end");
  });
  document.querySelector(".tab-links").addEventListener('click', function (e) {
    var tab = e.target.getAttribute("data-tab");
    if (!document.querySelector(".tab-" + tab).classList.contains("active")) {
      document.querySelector(".tab.active").classList.remove("active");
      document.querySelector("header .active").classList.remove("active");
      document.querySelector(".tab-" + tab).classList.add("active");
      e.target.classList.add("active");
    }
  });

  document.querySelector("#nag-nanny-from").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    Settings.$fromTo = e.target.value + "-" + document.querySelector('#nag-nanny-to').value;
    Settings.saveSetting();
  });
  document.querySelector("#nag-nanny-to").addEventListener('blur', function (e) {
    if (e.target.value.length === 0) {
      Settings.setFromTo();
      return;
    }
    var fromTo = document.querySelector('#nag-nanny-from').value + "-" + e.target.value;
    Settings.saveSetting(fromTo, "toggle-nanny-from-to");
  });
  document.querySelector("#nag-nanny-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#nag-nanny-interval').value * 60000), "toggle-nanny-interval");

  });

  document.querySelector("#pomodoro-interval").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting(+(document.querySelector('#pomodoro-interval').value), "toggle-pomodoro-interval");

  });

  document.querySelector("#day-end-time").addEventListener('blur', function (e) {
    if (e.target.value < 1) {
      e.target.value = 1;
      return;
    }
    Settings.saveSetting((document.querySelector('#day-end-time').value), "toggle-day-end-time");
  });
});
<|endoftext|>


<commit_before>// (c) 2016 Mapzen
// LICENSE: MIT
//
// SIDE NAVIGATION COMPONENT
//
// Handles interaction on the sub-navigation component.
//
// Dependencies: Bootstrap's "affix" JavaScript component
// (attached to its jQuery), and so there is a jQuery
// dependency that should get refactored out
// --------------------------------------------------------
(function (root, factory) {
  // Universal Module Definition (UMD)
  // via https://github.com/umdjs/umd/blob/master/templates/returnExports.js
  if (typeof module === 'object' && module.exports) {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.returnExports = factory();
  }
}(this, function () {
  'use strict';

  // Bail if there is no sub-nav component present on this page
  // TODO: This class selector could change
  var el = document.querySelector('.toc');
  var $el = $(el); // Remember the jQuery-wrapped version for affix
  if (!el) return;

  var nav = document.querySelector('nav.navbar');

  // Obtain the parent container that serves as the sub-nav's
  // maximum coverage area. This logic assumes that it's part
  // of a Bootstrap .container element. This could change
  // depending on how well it covers actual use cases.
  var container = getParentByClassName(el, 'container');

  // Affix for side nav bar
  // Don't turn on affix if the TOC height is greater than
  // document content, to prevent positioning bugs
  var affixState = false;

  if (el.offsetHeight < container.offsetHeight) {
    $el.affix({
      offset: {
        top: function () {
          var navHeight = nav ? nav.offsetHeight : 0;
          return $(container).offset().top - navHeight + 1;
        },
        bottom: function () {
          return $(document).height() - $(container).offset().top - $(container).outerHeight(true);
        }
      }
    });

    // Record that affix is on
    affixState = true;
  }

  var toggleEls = document.querySelectorAll('.toc-subnav-toggle');
  for (var i = 0, j = toggleEls.length; i < j; i++) {
    var toggle = toggleEls[i];
    toggle.addEventListener('click', function (e) {
      // Expands the submenu if there is no link to another page
      if (!e.target.href || e.target.href === '#') {
        var sublist = e.target.parentNode.nextElementSibling;
        sublist.classList.toggle('toc-expand');

        // Recalc affix position after expand transition finishes
        if (affixState === true) {
          $(sublist).one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function (e) {
            $el.affix('checkPosition');
          });
        }
      }
    });
  }

  // Guarantee only one active thing is up
  var activeEls = $('.toc li.active')
  activeEls.each(function (index) {
    if (index + 1 !== activeEls.length) {
      $(activeEls[index]).removeClass('active')
    }
  })

  // Utility functions
  function getParentByClassName (el, parentClass) {
    while (el !== document.documentElement && !el.classList.contains(parentClass)) {
      el = el.parentNode;
    }
    return el;
  }

  // Just return a value to define the module export.
  // This example returns an object, but the module
  // can return a function as the exported value.
  return {};
}));
<commit_msg>add explanation for affix bug hack<commit_after>// (c) 2016 Mapzen
// LICENSE: MIT
//
// SIDE NAVIGATION COMPONENT
//
// Handles interaction on the sub-navigation component.
//
// Dependencies: Bootstrap's "affix" JavaScript component
// (attached to its jQuery), and so there is a jQuery
// dependency that should get refactored out
// --------------------------------------------------------
(function (root, factory) {
  // Universal Module Definition (UMD)
  // via https://github.com/umdjs/umd/blob/master/templates/returnExports.js
  if (typeof module === 'object' && module.exports) {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    // Browser globals (root is window)
    root.returnExports = factory();
  }
}(this, function () {
  'use strict';

  // Bail if there is no sub-nav component present on this page
  // TODO: This class selector could change
  var el = document.querySelector('.toc');
  var $el = $(el); // Remember the jQuery-wrapped version for affix
  if (!el) return;

  var nav = document.querySelector('nav.navbar');

  // Obtain the parent container that serves as the sub-nav's
  // maximum coverage area. This logic assumes that it's part
  // of a Bootstrap .container element. This could change
  // depending on how well it covers actual use cases.
  var container = getParentByClassName(el, 'container');

  // Affix for side nav bar
  // Don't turn on affix if the TOC height is greater than
  // document content, to prevent positioning bugs
  var affixState = false;

  if (el.offsetHeight < container.offsetHeight) {
    $el.affix({
      offset: {
        top: function () {
          var navHeight = nav ? nav.offsetHeight : 0;
          // in bootstrap/affix.js line 45 there appears to be an off by 1 bug
          // which in some layouts causes the affix to incorrectly toggle
          // on mouseclick if the screen is scrolled all the way to the top
          // see: http://stackoverflow.com/questions/19711202/bootstrap-3-affix-plugin-click-bug
          // to avoid this we're adding 1 to offset.top
          return $(container).offset().top - navHeight + 1;
        },
        bottom: function () {
          return $(document).height() - $(container).offset().top - $(container).outerHeight(true);
        }
      }
    });

    // Record that affix is on
    affixState = true;
  }

  var toggleEls = document.querySelectorAll('.toc-subnav-toggle');
  for (var i = 0, j = toggleEls.length; i < j; i++) {
    var toggle = toggleEls[i];
    toggle.addEventListener('click', function (e) {
      // Expands the submenu if there is no link to another page
      if (!e.target.href || e.target.href === '#') {
        var sublist = e.target.parentNode.nextElementSibling;
        sublist.classList.toggle('toc-expand');

        // Recalc affix position after expand transition finishes
        if (affixState === true) {
          $(sublist).one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function (e) {
            $el.affix('checkPosition');
          });
        }
      }
    });
  }

  // Guarantee only one active thing is up
  var activeEls = $('.toc li.active')
  activeEls.each(function (index) {
    if (index + 1 !== activeEls.length) {
      $(activeEls[index]).removeClass('active')
    }
  })

  // Utility functions
  function getParentByClassName (el, parentClass) {
    while (el !== document.documentElement && !el.classList.contains(parentClass)) {
      el = el.parentNode;
    }
    return el;
  }

  // Just return a value to define the module export.
  // This example returns an object, but the module
  // can return a function as the exported value.
  return {};
}));
<|endoftext|>


<commit_before>import {Selection} from "./index";

function remove() {
  var parent = this.parentNode;
  if (parent) parent.removeChild(this);
}

export default function() {
  return this.each(remove);
};
<commit_msg>Remove unused import.<commit_after>function remove() {
  var parent = this.parentNode;
  if (parent) parent.removeChild(this);
}

export default function() {
  return this.each(remove);
};
<|endoftext|>


<commit_before>;(function () {

var logger = {
    log: function (){},
    warn: function (){},
    error: function (){}
};

// normalize environment
var RTCPeerConnection = null;
var getUserMedia = null;
var attachMediaStream = null;
var reattachMediaStream = null;
var webrtcDetectedBrowser = null;
var webRTCSupport = true;

if (navigator.mozGetUserMedia) {
    logger.log("This appears to be Firefox");

    webrtcDetectedBrowser = "firefox";

    // The RTCPeerConnection object.
    RTCPeerConnection = mozRTCPeerConnection;

    // The RTCSessionDescription object.
    RTCSessionDescription = mozRTCSessionDescription;

    // The RTCIceCandidate object.
    RTCIceCandidate = mozRTCIceCandidate;

    // Get UserMedia (only difference is the prefix).
    // Code from Adam Barth.
    getUserMedia = navigator.mozGetUserMedia.bind(navigator);

    // Attach a media stream to an element.
    attachMediaStream = function(element, stream) {
        element.mozSrcObject = stream;
        element.play();
    };

    reattachMediaStream = function(to, from) {
        to.mozSrcObject = from.mozSrcObject;
        to.play();
    };

    // Fake get{Video,Audio}Tracks
    MediaStream.prototype.getVideoTracks = function() {
        return [];
    };

    MediaStream.prototype.getAudioTracks = function() {
        return [];
    };
} else if (navigator.webkitGetUserMedia) {
    webrtcDetectedBrowser = "chrome";

    // The RTCPeerConnection object.
    RTCPeerConnection = webkitRTCPeerConnection;

    // Get UserMedia (only difference is the prefix).
    // Code from Adam Barth.
    getUserMedia = navigator.webkitGetUserMedia.bind(navigator);

    // Attach a media stream to an element.
    attachMediaStream = function(element, stream) {
        element.autoplay = true;
        element.src = webkitURL.createObjectURL(stream);
    };

    reattachMediaStream = function(to, from) {
        to.src = from.src;
    };

    // The representation of tracks in a stream is changed in M26.
    // Unify them for earlier Chrome versions in the coexisting period.
    if (!webkitMediaStream.prototype.getVideoTracks) {
        webkitMediaStream.prototype.getVideoTracks = function() {
            return this.videoTracks;
        };
        webkitMediaStream.prototype.getAudioTracks = function() {
            return this.audioTracks;
        };
    }

    // New syntax of getXXXStreams method in M26.
    if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
        webkitRTCPeerConnection.prototype.getLocalStreams = function() {
            return this.localStreams;
        };
        webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
            return this.remoteStreams;
        };
    }
} else {
    webRTCSupport = false;
    throw new Error("Browser does not appear to be WebRTC-capable");
}

// emitter that we use as a base
function WildEmitter() {
    this.callbacks = {};
}

// Listen on the given `event` with `fn`. Store a group name if present.
WildEmitter.prototype.on = function (event, groupName, fn) {
    var hasGroup = (arguments.length === 3),
        group = hasGroup ? arguments[1] : undefined,
        func = hasGroup ? arguments[2] : arguments[1];
    func._groupName = group;
    (this.callbacks[event] = this.callbacks[event] || []).push(func);
    return this;
};

// Adds an `event` listener that will be invoked a single
// time then automatically removed.
WildEmitter.prototype.once = function (event, fn) {
    var self = this;
    function on() {
        self.off(event, on);
        fn.apply(this, arguments);
    }
    this.on(event, on);
    return this;
};

// Unbinds an entire group
WildEmitter.prototype.releaseGroup = function (groupName) {
    var item, i, len, handlers;
    for (item in this.callbacks) {
        handlers = this.callbacks[item];
        for (i = 0, len = handlers.length; i < len; i++) {
            if (handlers[i]._groupName === groupName) {
                handlers.splice(i, 1);
                i--;
                len--;
            }
        }
    }
    return this;
};

// Remove the given callback for `event` or all
// registered callbacks.
WildEmitter.prototype.off = function (event, fn) {
    var callbacks = this.callbacks[event],
        i;

    if (!callbacks) return this;

    // remove all handlers
    if (arguments.length === 1) {
        delete this.callbacks[event];
        return this;
    }

    // remove specific handler
    i = callbacks.indexOf(fn);
    callbacks.splice(i, 1);
    return this;
};

// Emit `event` with the given args.
// also calls any `*` handlers
WildEmitter.prototype.emit = function (event) {
    var args = [].slice.call(arguments, 1),
        callbacks = this.callbacks[event],
        specialCallbacks = this.getWildcardCallbacks(event),
        i,
        len,
        item;

    if (callbacks) {
        for (i = 0, len = callbacks.length; i < len; ++i) {
            callbacks[i].apply(this, args);
        }
    }

    if (specialCallbacks) {
        for (i = 0, len = specialCallbacks.length; i < len; ++i) {
            specialCallbacks[i].apply(this, [event].concat(args));
        }
    }

    return this;
};

// Helper for for finding special wildcard event handlers that match the event
WildEmitter.prototype.getWildcardCallbacks = function (eventName) {
    var item,
        split,
        result = [];

    for (item in this.callbacks) {
        split = item.split('*');
        if (item === '*' || (split.length === 2 && eventName.slice(0, split[1].length) === split[1])) {
            result = result.concat(this.callbacks[item]);
        }
    }
    return result;
};

function WebRTC(opts) {
    var self = this,
        options = opts || {},
        config = this.config = {
            url: 'http://signaling.simplewebrtc.com:8888',
            log: false,
            localVideoEl: '',
            remoteVideosEl: '',
            autoRequestMedia: false
        },
        item,
        connection;

    // check for support
    if (!webRTCSupport) {
        console.error('Your browser doesn\'t seem to support WebRTC');
    }

    // set options
    for (item in options) {
        this.config[item] = options[item];
    }

    // log if configured to
    if (this.config.log) logger = console;

    // where we'll store our peer connections
    this.pcs = {};

    // our socket.io connection
    connection = this.connection = io.connect(this.config.url);

    connection.on('connect', function () {
        self.emit('ready', connection.socket.sessionid);
        self.sessionReady = true;
        self.testReadiness();
    });

    connection.on('message', function (message) {
        var existing = self.pcs[message.from];
        if (existing) {
            existing.handleMessage(message);
        } else {
            // create the conversation object
            self.pcs[message.from] = new Conversation({
                id: message.from,
                parent: self,
                initiator: false
            });
            self.pcs[message.from].handleMessage(message);
        }
    });

    connection.on('joined', function (room) {
        logger.log('got a joined', room);
        if (!self.pcs[room.id]) {
            self.startVideoCall(room.id);
        }
    });
    connection.on('left', function (room) {
        var conv = self.pcs[room.id];
        if (conv) conv.handleStreamRemoved();
    });

    WildEmitter.call(this);

    // log events
    this.on('*', function (event, val1, val2) {
        logger.log('event:', event, val1, val2);
    });

    // auto request if configured
    if (this.config.autoRequestMedia) this.startLocalVideo();
}

WebRTC.prototype = Object.create(WildEmitter.prototype, {
    constructor: {
        value: WebRTC
    }
});

WebRTC.prototype.getEl = function (idOrEl) {
    if (typeof idOrEl == 'string') {
        return document.getElementById(idOrEl);
    } else {
        return idOrEl;
    }
};

// this accepts either element ID or element
// and either the video tag itself or a container
// that will be used to put the video tag into.
WebRTC.prototype.getLocalVideoContainer = function () {
    var el = this.getEl(this.config.localVideoEl);
    if (el && el.tagName === 'VIDEO') {
        return el;
    } else {
        var video = document.createElement('video');
        el.appendChild(video);
        return video;
    }
};

WebRTC.prototype.getRemoteVideoContainer = function () {
    return this.getEl(this.config.remoteVideosEl);
};

WebRTC.prototype.startVideoCall = function (id) {
    this.pcs[id] = new Conversation({
        id: id,
        parent: this,
        initiator: true
    });
    this.pcs[id].start();
};

WebRTC.prototype.createRoom = function (name, cb) {
    if (arguments.length === 2) {
        this.connection.emit('create', name, cb);
    } else {
        this.connection.emit('create', name);
    }
};

WebRTC.prototype.joinRoom = function (name) {
    this.connection.emit('join', name);
};

WebRTC.prototype.leaveRoom = function (name) {
    this.connection.emit('leave', name);
};

WebRTC.prototype.handleIncomingIceCandidate = function (candidate, moreToFollow) {
    logger.log('received candidate');
    candidate = new IceCandidate(payload.label, payload.candidate);
    this.pc.processIceMessage(candidate);
};

WebRTC.prototype.testReadiness = function () {
    if (this.localStream && this.sessionReady) {
        this.emit('readyToCall', this.connection.socket.sessionid);
    }
};

WebRTC.prototype.startLocalVideo = function (element) {
    var self = this;
    getUserMedia({audio: true, video: {
        mandatory: {},
        optional: []
    }}, function (stream) {
        attachMediaStream(element || self.getLocalVideoContainer(), stream);
        self.localStream = stream;
        self.testReadiness();
    }, function () {
        throw new Error('Failed to get access to local media.');
    });
};

WebRTC.prototype.send = function (to, type, payload) {
    this.connection.emit('message', {
        to: to,
        type: type,
        payload: payload
    });
};

function Conversation(options) {
    var self = this,
        pc_config = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]},
        pc_constraints = {"optional": [{"DtlsSrtpKeyAgreement": true}]};

    this.id = options.id;
    this.parent = options.parent;
    this.initiator = options.initiator;
    if (webrtcDetectedBrowser == "firefox") {
        pc_config = {"iceServers":[{"url":"stun:23.21.150.121"}]};
    }
    // Create an RTCPeerConnection via the polyfill (adapter.js).
    this.pc = new RTCPeerConnection(pc_config, pc_constraints);
    //this.pc = new RTCPeerConnection({iceServers: [{url: "stun:stun.l.google.com:19302"}]}, {"optional": []});
    this.pc.onicecandidate = this.onIceCandidate.bind(this);
    this.pc.addStream(this.parent.localStream);
    this.pc.onaddstream = this.handleRemoteStreamAdded.bind(this);
    this.pc.onremovestream = this.handleStreamRemoved.bind(this);
    // for re-use
    this.mediaConstraints = {
        'mandatory': {
            'OfferToReceiveAudio':true,
            'OfferToReceiveVideo':true
        }
    };
    WildEmitter.call(this);

    // proxy events to parent
    this.on('*', function (name, value) {
        self.parent.emit(name, value, self);
    });
}

Conversation.prototype = Object.create(WildEmitter.prototype, {
    constructor: {
        value: Conversation
    }
});

Conversation.prototype.handleMessage = function (message) {
    if (message.type === 'offer') {
        logger.log('setting remote description');
        this.pc.setRemoteDescription(new RTCSessionDescription(message.payload));
        this.answer();
    } else if (message.type === 'answer') {
        this.pc.setRemoteDescription(new RTCSessionDescription(message.payload));
    } else if (message.type === 'candidate') {
        var candidate = new RTCIceCandidate({
            sdpMLineIndex: message.payload.label,
            candidate: message.payload.candidate
        });
        this.pc.addIceCandidate(candidate);
    }
};

Conversation.prototype.send = function (type, payload) {
    this.parent.send(this.id, type, payload);
};

Conversation.prototype.onIceCandidate = function (event) {
    if (event.candidate) {
        this.send('candidate', {
            label: event.candidate.sdpMLineIndex,
            id: event.candidate.sdpMid,
            candidate: event.candidate.candidate
        });
    } else {
      logger.log("End of candidates.");
    }
};

Conversation.prototype.start = function () {
    var self = this;
    this.pc.createOffer(function (sessionDescription) {
        logger.log('setting local description');
        self.pc.setLocalDescription(sessionDescription);
        logger.log('sending offer', sessionDescription);
        self.send('offer', sessionDescription);
    }, null, this.mediaConstraints);
};

Conversation.prototype.answer = function () {
    var self = this;
    logger.log('answer called');
    this.pc.createAnswer(function (sessionDescription) {
        logger.log('setting local description');
        self.pc.setLocalDescription(sessionDescription);
        logger.log('sending answer', sessionDescription);
        self.send('answer', sessionDescription);
    }, null, this.mediaConstraints);
};

Conversation.prototype.handleRemoteStreamAdded = function (event) {
    var stream = this.stream = event.stream,
        el = document.createElement('video'),
        container = this.parent.getRemoteVideoContainer();
    el.id = this.id;
    attachMediaStream(el, stream);
    if (container) container.appendChild(el);
    this.emit('videoAdded', el);
};

Conversation.prototype.handleStreamRemoved = function () {
    var video = document.getElementById(this.id),
        container = this.parent.getRemoteVideoContainer();
    if (video && container) container.removeChild(video);
    this.emit('videoRemoved', video);
};

// expose WebRTC
window.WebRTC = WebRTC;

}());
<commit_msg>remove console log<commit_after>;(function () {

var logger = {
    log: function (){},
    warn: function (){},
    error: function (){}
};

// normalize environment
var RTCPeerConnection = null;
var getUserMedia = null;
var attachMediaStream = null;
var reattachMediaStream = null;
var webrtcDetectedBrowser = null;
var webRTCSupport = true;

if (navigator.mozGetUserMedia) {
    logger.log("This appears to be Firefox");

    webrtcDetectedBrowser = "firefox";

    // The RTCPeerConnection object.
    RTCPeerConnection = mozRTCPeerConnection;

    // The RTCSessionDescription object.
    RTCSessionDescription = mozRTCSessionDescription;

    // The RTCIceCandidate object.
    RTCIceCandidate = mozRTCIceCandidate;

    // Get UserMedia (only difference is the prefix).
    // Code from Adam Barth.
    getUserMedia = navigator.mozGetUserMedia.bind(navigator);

    // Attach a media stream to an element.
    attachMediaStream = function(element, stream) {
        element.mozSrcObject = stream;
        element.play();
    };

    reattachMediaStream = function(to, from) {
        to.mozSrcObject = from.mozSrcObject;
        to.play();
    };

    // Fake get{Video,Audio}Tracks
    MediaStream.prototype.getVideoTracks = function() {
        return [];
    };

    MediaStream.prototype.getAudioTracks = function() {
        return [];
    };
} else if (navigator.webkitGetUserMedia) {
    webrtcDetectedBrowser = "chrome";

    // The RTCPeerConnection object.
    RTCPeerConnection = webkitRTCPeerConnection;

    // Get UserMedia (only difference is the prefix).
    // Code from Adam Barth.
    getUserMedia = navigator.webkitGetUserMedia.bind(navigator);

    // Attach a media stream to an element.
    attachMediaStream = function(element, stream) {
        element.autoplay = true;
        element.src = webkitURL.createObjectURL(stream);
    };

    reattachMediaStream = function(to, from) {
        to.src = from.src;
    };

    // The representation of tracks in a stream is changed in M26.
    // Unify them for earlier Chrome versions in the coexisting period.
    if (!webkitMediaStream.prototype.getVideoTracks) {
        webkitMediaStream.prototype.getVideoTracks = function() {
            return this.videoTracks;
        };
        webkitMediaStream.prototype.getAudioTracks = function() {
            return this.audioTracks;
        };
    }

    // New syntax of getXXXStreams method in M26.
    if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
        webkitRTCPeerConnection.prototype.getLocalStreams = function() {
            return this.localStreams;
        };
        webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
            return this.remoteStreams;
        };
    }
} else {
    webRTCSupport = false;
    throw new Error("Browser does not appear to be WebRTC-capable");
}

// emitter that we use as a base
function WildEmitter() {
    this.callbacks = {};
}

// Listen on the given `event` with `fn`. Store a group name if present.
WildEmitter.prototype.on = function (event, groupName, fn) {
    var hasGroup = (arguments.length === 3),
        group = hasGroup ? arguments[1] : undefined,
        func = hasGroup ? arguments[2] : arguments[1];
    func._groupName = group;
    (this.callbacks[event] = this.callbacks[event] || []).push(func);
    return this;
};

// Adds an `event` listener that will be invoked a single
// time then automatically removed.
WildEmitter.prototype.once = function (event, fn) {
    var self = this;
    function on() {
        self.off(event, on);
        fn.apply(this, arguments);
    }
    this.on(event, on);
    return this;
};

// Unbinds an entire group
WildEmitter.prototype.releaseGroup = function (groupName) {
    var item, i, len, handlers;
    for (item in this.callbacks) {
        handlers = this.callbacks[item];
        for (i = 0, len = handlers.length; i < len; i++) {
            if (handlers[i]._groupName === groupName) {
                handlers.splice(i, 1);
                i--;
                len--;
            }
        }
    }
    return this;
};

// Remove the given callback for `event` or all
// registered callbacks.
WildEmitter.prototype.off = function (event, fn) {
    var callbacks = this.callbacks[event],
        i;

    if (!callbacks) return this;

    // remove all handlers
    if (arguments.length === 1) {
        delete this.callbacks[event];
        return this;
    }

    // remove specific handler
    i = callbacks.indexOf(fn);
    callbacks.splice(i, 1);
    return this;
};

// Emit `event` with the given args.
// also calls any `*` handlers
WildEmitter.prototype.emit = function (event) {
    var args = [].slice.call(arguments, 1),
        callbacks = this.callbacks[event],
        specialCallbacks = this.getWildcardCallbacks(event),
        i,
        len,
        item;

    if (callbacks) {
        for (i = 0, len = callbacks.length; i < len; ++i) {
            callbacks[i].apply(this, args);
        }
    }

    if (specialCallbacks) {
        for (i = 0, len = specialCallbacks.length; i < len; ++i) {
            specialCallbacks[i].apply(this, [event].concat(args));
        }
    }

    return this;
};

// Helper for for finding special wildcard event handlers that match the event
WildEmitter.prototype.getWildcardCallbacks = function (eventName) {
    var item,
        split,
        result = [];

    for (item in this.callbacks) {
        split = item.split('*');
        if (item === '*' || (split.length === 2 && eventName.slice(0, split[1].length) === split[1])) {
            result = result.concat(this.callbacks[item]);
        }
    }
    return result;
};

function WebRTC(opts) {
    var self = this,
        options = opts || {},
        config = this.config = {
            url: 'http://signaling.simplewebrtc.com:8888',
            log: false,
            localVideoEl: '',
            remoteVideosEl: '',
            autoRequestMedia: false
        },
        item,
        connection;

    // check for support
    if (!webRTCSupport) {
        console.error('Your browser doesn\'t seem to support WebRTC');
    }

    // set options
    for (item in options) {
        this.config[item] = options[item];
    }

    // log if configured to
    if (this.config.log) logger = console;

    // where we'll store our peer connections
    this.pcs = {};

    // our socket.io connection
    connection = this.connection = io.connect(this.config.url);

    connection.on('connect', function () {
        self.emit('ready', connection.socket.sessionid);
        self.sessionReady = true;
        self.testReadiness();
    });

    connection.on('message', function (message) {
        var existing = self.pcs[message.from];
        if (existing) {
            existing.handleMessage(message);
        } else {
            // create the conversation object
            self.pcs[message.from] = new Conversation({
                id: message.from,
                parent: self,
                initiator: false
            });
            self.pcs[message.from].handleMessage(message);
        }
    });

    connection.on('joined', function (room) {
        logger.log('got a joined', room);
        if (!self.pcs[room.id]) {
            self.startVideoCall(room.id);
        }
    });
    connection.on('left', function (room) {
        var conv = self.pcs[room.id];
        if (conv) conv.handleStreamRemoved();
    });

    WildEmitter.call(this);

    // log events
    this.on('*', function (event, val1, val2) {
        logger.log('event:', event, val1, val2);
    });

    // auto request if configured
    if (this.config.autoRequestMedia) this.startLocalVideo();
}

WebRTC.prototype = Object.create(WildEmitter.prototype, {
    constructor: {
        value: WebRTC
    }
});

WebRTC.prototype.getEl = function (idOrEl) {
    if (typeof idOrEl == 'string') {
        return document.getElementById(idOrEl);
    } else {
        return idOrEl;
    }
};

// this accepts either element ID or element
// and either the video tag itself or a container
// that will be used to put the video tag into.
WebRTC.prototype.getLocalVideoContainer = function () {
    var el = this.getEl(this.config.localVideoEl);
    if (el && el.tagName === 'VIDEO') {
        return el;
    } else {
        var video = document.createElement('video');
        el.appendChild(video);
        return video;
    }
};

WebRTC.prototype.getRemoteVideoContainer = function () {
    return this.getEl(this.config.remoteVideosEl);
};

WebRTC.prototype.startVideoCall = function (id) {
    this.pcs[id] = new Conversation({
        id: id,
        parent: this,
        initiator: true
    });
    this.pcs[id].start();
};

WebRTC.prototype.createRoom = function (name, cb) {
    if (arguments.length === 2) {
        this.connection.emit('create', name, cb);
    } else {
        this.connection.emit('create', name);
    }
};

WebRTC.prototype.joinRoom = function (name) {
    this.connection.emit('join', name);
};

WebRTC.prototype.leaveRoom = function (name) {
    this.connection.emit('leave', name);
};

WebRTC.prototype.handleIncomingIceCandidate = function (candidate, moreToFollow) {
    logger.log('received candidate');
    candidate = new IceCandidate(payload.label, payload.candidate);
    this.pc.processIceMessage(candidate);
};

WebRTC.prototype.testReadiness = function () {
    if (this.localStream && this.sessionReady) {
        this.emit('readyToCall', this.connection.socket.sessionid);
    }
};

WebRTC.prototype.startLocalVideo = function (element) {
    var self = this;
    getUserMedia(this.config.media || {audio:true, video: {
        mandatory: {},
        optional: []
    }}, function (stream) {
        attachMediaStream(element || self.getLocalVideoContainer(), stream);
        self.localStream = stream;
        self.testReadiness();
    }, function () {
        throw new Error('Failed to get access to local media.');
    });
};

WebRTC.prototype.send = function (to, type, payload) {
    this.connection.emit('message', {
        to: to,
        type: type,
        payload: payload
    });
};

function Conversation(options) {
    var self = this,
        pc_config = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]},
        pc_constraints = {"optional": [{"DtlsSrtpKeyAgreement": true}]};

    this.id = options.id;
    this.parent = options.parent;
    this.initiator = options.initiator;
    if (webrtcDetectedBrowser == "firefox") {
        pc_config = {"iceServers":[{"url":"stun:23.21.150.121"}]};
    }
    // Create an RTCPeerConnection via the polyfill (adapter.js).
    this.pc = new RTCPeerConnection(pc_config, pc_constraints);
    //this.pc = new RTCPeerConnection({iceServers: [{url: "stun:stun.l.google.com:19302"}]}, {"optional": []});
    this.pc.onicecandidate = this.onIceCandidate.bind(this);
    this.pc.addStream(this.parent.localStream);
    this.pc.onaddstream = this.handleRemoteStreamAdded.bind(this);
    this.pc.onremovestream = this.handleStreamRemoved.bind(this);
    // for re-use
    this.mediaConstraints = {
        'mandatory': {
            'OfferToReceiveAudio':true,
            'OfferToReceiveVideo':true
        }
    };
    WildEmitter.call(this);

    // proxy events to parent
    this.on('*', function (name, value) {
        self.parent.emit(name, value, self);
    });
}

Conversation.prototype = Object.create(WildEmitter.prototype, {
    constructor: {
        value: Conversation
    }
});

Conversation.prototype.handleMessage = function (message) {
    if (message.type === 'offer') {
        logger.log('setting remote description');
        this.pc.setRemoteDescription(new RTCSessionDescription(message.payload));
        this.answer();
    } else if (message.type === 'answer') {
        this.pc.setRemoteDescription(new RTCSessionDescription(message.payload));
    } else if (message.type === 'candidate') {
        var candidate = new RTCIceCandidate({
            sdpMLineIndex: message.payload.label,
            candidate: message.payload.candidate
        });
        this.pc.addIceCandidate(candidate);
    }
};

Conversation.prototype.send = function (type, payload) {
    this.parent.send(this.id, type, payload);
};

Conversation.prototype.onIceCandidate = function (event) {
    if (event.candidate) {
        this.send('candidate', {
            label: event.candidate.sdpMLineIndex,
            id: event.candidate.sdpMid,
            candidate: event.candidate.candidate
        });
    } else {
      logger.log("End of candidates.");
    }
};

Conversation.prototype.start = function () {
    var self = this;
    this.pc.createOffer(function (sessionDescription) {
        logger.log('setting local description');
        self.pc.setLocalDescription(sessionDescription);
        logger.log('sending offer', sessionDescription);
        self.send('offer', sessionDescription);
    }, null, this.mediaConstraints);
};

Conversation.prototype.answer = function () {
    var self = this;
    logger.log('answer called');
    this.pc.createAnswer(function (sessionDescription) {
        logger.log('setting local description');
        self.pc.setLocalDescription(sessionDescription);
        logger.log('sending answer', sessionDescription);
        self.send('answer', sessionDescription);
    }, null, this.mediaConstraints);
};

Conversation.prototype.handleRemoteStreamAdded = function (event) {
    var stream = this.stream = event.stream,
        el = document.createElement('video'),
        container = this.parent.getRemoteVideoContainer();
    el.id = this.id;
    attachMediaStream(el, stream);
    if (container) container.appendChild(el);
    this.emit('videoAdded', el);
};

Conversation.prototype.handleStreamRemoved = function () {
    var video = document.getElementById(this.id),
        container = this.parent.getRemoteVideoContainer();
    if (video && container) container.removeChild(video);
    this.emit('videoRemoved', video);
};

// expose WebRTC
window.WebRTC = WebRTC;

}());
<|endoftext|>


<commit_before>angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])
.factory('spyAPI', function($rootScope, $timeout, $window, $document, scrollContainerAPI, duScrollGreedy, duScrollSpyWait) {
  'use strict';

  var createScrollHandler = function(context) {
    var timer = false, queued = false;
    var handler = function() {
      queued = false;
      var container = context.container,
          containerEl = container[0],
          containerOffset = 0,
          bottomReached;

      if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {
        containerOffset = containerEl.getBoundingClientRect().top;
        bottomReached = Math.round(containerEl.scrollTop + containerEl.clientHeight) >= containerEl.scrollHeight;
      } else {
        bottomReached = Math.round($window.pageYOffset + $window.innerHeight) >= $document[0].body.scrollHeight;
      }
      var compareProperty = (bottomReached ? 'bottom' : 'top')

      var i, currentlyActive, toBeActive, spies, spy, pos;
      spies = context.spies;
      currentlyActive = context.currentlyActive;
      toBeActive = undefined;

      for(i = 0; i < spies.length; i++) {
        spy = spies[i];
        pos = spy.getTargetPosition();
        if (!pos) continue;

        if(bottomReached || (pos.top + spy.offset - containerOffset < 20 && (duScrollGreedy || pos.top*-1 + containerOffset) < pos.height)) {
          //Find the one closest the viewport top or the page bottom if it's reached
          if(!toBeActive || toBeActive[compareProperty] < pos[compareProperty]) {
            toBeActive = {
              spy: spy
            };
            toBeActive[compareProperty] = pos[compareProperty];
          }
        }
      }

      if(toBeActive) {
        toBeActive = toBeActive.spy;
      }
      if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;
      if(currentlyActive) {
        currentlyActive.$element.removeClass('active');
        $rootScope.$broadcast('duScrollspy:becameInactive', currentlyActive.$element);
      }
      if(toBeActive) {
        toBeActive.$element.addClass('active');
        $rootScope.$broadcast('duScrollspy:becameActive', toBeActive.$element);
      }
      context.currentlyActive = toBeActive;
    };

    if(!duScrollSpyWait) {
      return handler;
    }

    //Debounce for potential performance savings
    return function() {
      if(!timer) {
        handler();
        timer = $timeout(function() {
          timer = false;
          if(queued) {
            handler();
          }
        }, duScrollSpyWait, false);
      } else {
        queued = true;
      }
    };
  };

  var contexts = {};

  var createContext = function($scope) {
    var id = $scope.$id;
    var context = {
      spies: []
    };

    context.handler = createScrollHandler(context);
    contexts[id] = context;

    $scope.$on('$destroy', function() {
      destroyContext($scope);
    });

    return id;
  };

  var destroyContext = function($scope) {
    var id = $scope.$id;
    var context = contexts[id], container = context.container;
    if(container) {
      container.off('scroll', context.handler);
    }
    delete contexts[id];
  };

  var defaultContextId = createContext($rootScope);

  var getContextForScope = function(scope) {
    if(contexts[scope.$id]) {
      return contexts[scope.$id];
    }
    if(scope.$parent) {
      return getContextForScope(scope.$parent);
    }
    return contexts[defaultContextId];
  };

  var getContextForSpy = function(spy) {
    var context, contextId, scope = spy.$scope;
    if(scope) {
      return getContextForScope(scope);
    }
    //No scope, most likely destroyed
    for(contextId in contexts) {
      context = contexts[contextId];
      if(context.spies.indexOf(spy) !== -1) {
        return context;
      }
    }
  };

  var isElementInDocument = function(element) {
    while (element.parentNode) {
      element = element.parentNode;
      if (element === document) {
        return true;
      }
    }
    return false;
  };

  var addSpy = function(spy) {
    var context = getContextForSpy(spy);
    if (!context) return;
    context.spies.push(spy);
    if (!context.container || !isElementInDocument(context.container)) {
      if(context.container) {
        context.container.off('scroll', context.handler);
      }
      context.container = scrollContainerAPI.getContainer(spy.$scope);
      context.container.on('scroll', context.handler).triggerHandler('scroll');
    }
  };

  var removeSpy = function(spy) {
    var context = getContextForSpy(spy);
    if(spy === context.currentlyActive) {
      context.currentlyActive = null;
    }
    var i = context.spies.indexOf(spy);
    if(i !== -1) {
      context.spies.splice(i, 1);
    }
		spy.$element = null;
  };

  return {
    addSpy: addSpy,
    removeSpy: removeSpy,
    createContext: createContext,
    destroyContext: destroyContext,
    getContextForScope: getContextForScope
  };
});
<commit_msg>Fixed missing semi-colon.<commit_after>angular.module('duScroll.spyAPI', ['duScroll.scrollContainerAPI'])
.factory('spyAPI', function($rootScope, $timeout, $window, $document, scrollContainerAPI, duScrollGreedy, duScrollSpyWait) {
  'use strict';

  var createScrollHandler = function(context) {
    var timer = false, queued = false;
    var handler = function() {
      queued = false;
      var container = context.container,
          containerEl = container[0],
          containerOffset = 0,
          bottomReached;

      if (typeof HTMLElement !== 'undefined' && containerEl instanceof HTMLElement || containerEl.nodeType && containerEl.nodeType === containerEl.ELEMENT_NODE) {
        containerOffset = containerEl.getBoundingClientRect().top;
        bottomReached = Math.round(containerEl.scrollTop + containerEl.clientHeight) >= containerEl.scrollHeight;
      } else {
        bottomReached = Math.round($window.pageYOffset + $window.innerHeight) >= $document[0].body.scrollHeight;
      }
      var compareProperty = (bottomReached ? 'bottom' : 'top');

      var i, currentlyActive, toBeActive, spies, spy, pos;
      spies = context.spies;
      currentlyActive = context.currentlyActive;
      toBeActive = undefined;

      for(i = 0; i < spies.length; i++) {
        spy = spies[i];
        pos = spy.getTargetPosition();
        if (!pos) continue;

        if(bottomReached || (pos.top + spy.offset - containerOffset < 20 && (duScrollGreedy || pos.top*-1 + containerOffset) < pos.height)) {
          //Find the one closest the viewport top or the page bottom if it's reached
          if(!toBeActive || toBeActive[compareProperty] < pos[compareProperty]) {
            toBeActive = {
              spy: spy
            };
            toBeActive[compareProperty] = pos[compareProperty];
          }
        }
      }

      if(toBeActive) {
        toBeActive = toBeActive.spy;
      }
      if(currentlyActive === toBeActive || (duScrollGreedy && !toBeActive)) return;
      if(currentlyActive) {
        currentlyActive.$element.removeClass('active');
        $rootScope.$broadcast('duScrollspy:becameInactive', currentlyActive.$element);
      }
      if(toBeActive) {
        toBeActive.$element.addClass('active');
        $rootScope.$broadcast('duScrollspy:becameActive', toBeActive.$element);
      }
      context.currentlyActive = toBeActive;
    };

    if(!duScrollSpyWait) {
      return handler;
    }

    //Debounce for potential performance savings
    return function() {
      if(!timer) {
        handler();
        timer = $timeout(function() {
          timer = false;
          if(queued) {
            handler();
          }
        }, duScrollSpyWait, false);
      } else {
        queued = true;
      }
    };
  };

  var contexts = {};

  var createContext = function($scope) {
    var id = $scope.$id;
    var context = {
      spies: []
    };

    context.handler = createScrollHandler(context);
    contexts[id] = context;

    $scope.$on('$destroy', function() {
      destroyContext($scope);
    });

    return id;
  };

  var destroyContext = function($scope) {
    var id = $scope.$id;
    var context = contexts[id], container = context.container;
    if(container) {
      container.off('scroll', context.handler);
    }
    delete contexts[id];
  };

  var defaultContextId = createContext($rootScope);

  var getContextForScope = function(scope) {
    if(contexts[scope.$id]) {
      return contexts[scope.$id];
    }
    if(scope.$parent) {
      return getContextForScope(scope.$parent);
    }
    return contexts[defaultContextId];
  };

  var getContextForSpy = function(spy) {
    var context, contextId, scope = spy.$scope;
    if(scope) {
      return getContextForScope(scope);
    }
    //No scope, most likely destroyed
    for(contextId in contexts) {
      context = contexts[contextId];
      if(context.spies.indexOf(spy) !== -1) {
        return context;
      }
    }
  };

  var isElementInDocument = function(element) {
    while (element.parentNode) {
      element = element.parentNode;
      if (element === document) {
        return true;
      }
    }
    return false;
  };

  var addSpy = function(spy) {
    var context = getContextForSpy(spy);
    if (!context) return;
    context.spies.push(spy);
    if (!context.container || !isElementInDocument(context.container)) {
      if(context.container) {
        context.container.off('scroll', context.handler);
      }
      context.container = scrollContainerAPI.getContainer(spy.$scope);
      context.container.on('scroll', context.handler).triggerHandler('scroll');
    }
  };

  var removeSpy = function(spy) {
    var context = getContextForSpy(spy);
    if(spy === context.currentlyActive) {
      context.currentlyActive = null;
    }
    var i = context.spies.indexOf(spy);
    if(i !== -1) {
      context.spies.splice(i, 1);
    }
		spy.$element = null;
  };

  return {
    addSpy: addSpy,
    removeSpy: removeSpy,
    createContext: createContext,
    destroyContext: destroyContext,
    getContextForScope: getContextForScope
  };
});
<|endoftext|>


<commit_before>import cache from './cache';
import analytics from './analytics';

const weatherDefaults = {
  mode: 'json',
  units: 'internal',
  cnt: '5',
  type: 'accurate'
};

const API_KEYS = WEATHER_API_KEY.split(',');

const locationConfig = {
  enableHighAccuracy: true,
  timeout: 5000, // 5s
  maximumAge: 1000 * 60 * 60 // 1 hour
};

const TIMEOUT_INCREASE = 1000;
const CACHE_ID = 'weatherCache';
const CACHE_TTL = 30 * 60 * 1000; // 30 minutes

const getRandom = items => items[Math.floor(Math.random() * items.length)];
const cacheKey = location => `${CACHE_ID}@${location}`;

class Weather {
  getCurrentPosition () {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(resolve, reject, locationConfig);
    });
  }

  getLocation () {
    return this.getCurrentPosition().then(
      position => position,
      positionError => {
        if (positionError.code === 3) {
          locationConfig.timeout += TIMEOUT_INCREASE;
          return this.getLocation();
        }
        return Promise.reject({
          type: 'PositionError',
          message: 'Can not retrieve location',
          error: positionError
        });
      }
    );
  }

  getApiKey () {
    return getRandom(API_KEYS);
  }

  createUrlFromPosition (position) {
    const lat = position.coords.latitude.toFixed(5);
    const lon = position.coords.longitude.toFixed(5);
    const {mode, type, units, cnt} = weatherDefaults;
    const apikey = this.getApiKey();

    return `http://api.openweathermap.org/data/2.5/forecast/daily?&mode=${mode}&type=${type}&units=${units}&cnt=${cnt}&lat=${lat}&lon=${lon}&APPID=${apikey}`;
  }

  createUrlFromLocation (location) {
    const {mode, type, units, cnt} = weatherDefaults;
    const apikey = this.getApiKey();

    return Promise.resolve(
      `http://api.openweathermap.org/data/2.5/forecast/daily?&mode=${mode}&type=${type}&units=${units}&cnt=${cnt}&q=${location}&APPID=${apikey}`
    );
  }

  getWeatherUrl (location) {
    if (location !== 'auto') {
      return this.createUrlFromLocation(location);
    }
    return this.getLocation().then(this.createUrlFromPosition.bind(this));
  }

  loadWeatherData (url) {
    return fetch(url).then(
      response => response.json(),
      err => Promise.reject(err)
    );
  }

  getWeather (location) {
    return cache.getItem(cacheKey(location)).then(
      cachedWeather => {
        analytics.trackEvent('weather', 'gotCache');
        return cachedWeather;
      },
      () => {
        analytics.trackEvent('weather', 'fetch');
        return this.getWeatherUrl(location)
          .then(this.loadWeatherData)
          .then(data => {
            if (!data.city || !data.list) return Promise.reject(data);
            return cache.setItem(cacheKey(location), data, {ttl: CACHE_TTL}).then(() => {
              analytics.trackEvent('weather', 'setCache');
              return data;
            });
          });
      });
  }
}

const weather = new Weather();

export default weather;
<commit_msg>add weather errors tracking<commit_after>import cache from './cache';
import analytics from './analytics';

const weatherDefaults = {
  mode: 'json',
  units: 'internal',
  cnt: '5',
  type: 'accurate'
};

const API_KEYS = WEATHER_API_KEY.split(',');

const locationConfig = {
  enableHighAccuracy: true,
  timeout: 5000, // 5s
  maximumAge: 1000 * 60 * 60 // 1 hour
};

const TIMEOUT_INCREASE = 1000;
const CACHE_ID = 'weatherCache';
const CACHE_TTL = 30 * 60 * 1000; // 30 minutes

const getRandom = items => items[Math.floor(Math.random() * items.length)];
const cacheKey = location => `${CACHE_ID}@${location}`;

class Weather {
  getCurrentPosition () {
    return new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(resolve, reject, locationConfig);
    });
  }

  getLocation () {
    return this.getCurrentPosition().then(
      position => position,
      positionError => {
        if (positionError.code === 3) {
          locationConfig.timeout += TIMEOUT_INCREASE;
          return this.getLocation();
        }
        return Promise.reject({
          type: 'PositionError',
          message: 'Can not retrieve location',
          error: positionError
        });
      }
    );
  }

  getApiKey () {
    return getRandom(API_KEYS);
  }

  createUrlFromPosition (position) {
    const lat = position.coords.latitude.toFixed(5);
    const lon = position.coords.longitude.toFixed(5);
    const {mode, type, units, cnt} = weatherDefaults;
    const apikey = this.getApiKey();

    return `http://api.openweathermap.org/data/2.5/forecast/daily?&mode=${mode}&type=${type}&units=${units}&cnt=${cnt}&lat=${lat}&lon=${lon}&APPID=${apikey}`;
  }

  createUrlFromLocation (location) {
    const {mode, type, units, cnt} = weatherDefaults;
    const apikey = this.getApiKey();

    return Promise.resolve(
      `http://api.openweathermap.org/data/2.5/forecast/daily?&mode=${mode}&type=${type}&units=${units}&cnt=${cnt}&q=${location}&APPID=${apikey}`
    );
  }

  getWeatherUrl (location) {
    if (location !== 'auto') {
      return this.createUrlFromLocation(location);
    }
    return this.getLocation().then(this.createUrlFromPosition.bind(this));
  }

  loadWeatherData (url) {
    return fetch(url).then(
      response => response.json(),
      err => Promise.reject(err)
    );
  }

  getWeather (location) {
    return cache.getItem(cacheKey(location)).then(
      cachedWeather => {
        analytics.trackEvent('weather', 'gotCache');
        return cachedWeather;
      },
      () => {
        analytics.trackEvent('weather', 'fetch');
        return this.getWeatherUrl(location)
          .then(this.loadWeatherData)
          .then(data => {
            if (!data.city || !data.list) return Promise.reject(data);
            return cache.setItem(cacheKey(location), data, {ttl: CACHE_TTL}).then(() => {
              analytics.trackEvent('weather', 'setCache');
              return data;
            });
          }).catch(err => {
            analytics.trackEvent('weather', 'error');
            return Promise.reject(err);
          });
      });
  }
}

const weather = new Weather();

export default weather;
<|endoftext|>


<commit_before>/* globals $ */

import { updateUrlQuery } from '../uri.js'
import { showSliders, readSliders } from '../sliders.js'

let currentStar = null

export default class Star {
  constructor(name, sliderNames) {
    this.name = name
    this.sliderNames = sliderNames
    this.use = () => {
      currentStar = this
      showSliders(...this.controls)
      updateUrlQuery('s', this.name)
      this.refreshPath()
    }
  }

  refreshPath() {
    const pathString = this.path(...readSliders(...this.sliderNames))
    $('#star').attr('d', pathString)
  }

  path(...inputs) {
    const points = this.points(...inputs)
    return this.constructPath(...points)
  }
}

export function updateStarPath() {
  currentStar.refreshPath()
}
<commit_msg>Fixed bug.<commit_after>/* globals $ */

import { updateUrlQuery } from '../uri.js'
import { showSliders, readSliders } from '../sliders.js'

let currentStar = null

export default class Star {
  constructor(name, sliderNames) {
    this.name = name
    this.sliderNames = sliderNames
    this.use = () => {
      currentStar = this
      showSliders(...this.sliderNames)
      updateUrlQuery('s', this.name)
      this.refreshPath()
    }
  }

  refreshPath() {
    const pathString = this.path(...readSliders(...this.sliderNames))
    $('#star').attr('d', pathString)
  }

  path(...inputs) {
    const points = this.points(...inputs)
    return this.constructPath(...points)
  }
}

export function updateStarPath() {
  currentStar.refreshPath()
}
<|endoftext|>


<commit_before>'use strict'
const url = require('url')

const Bluebird = require('bluebird')
const cheerio = require('cheerio')

const Site = use('site')

class Wikipedia extends Site {
  static matches (siteUrlStr) {
    return /youtube[.]com[/]watch[?]v=|youtu.be/.test(siteUrlStr)
  }

  constructor (siteUrlStr) {
    super(siteUrlStr)
    this.publisher = 'youtube.com'
    this.publisherName = 'You Tube'
    const matches = siteUrlStr.match(/[/]watch[?]v=(.*)$/) || siteUrlStr.match(/youtu.be[/](.*)/)
    this.id = matches[1]
  }

  getFicMetadata (fetch, fic) {
    fic.link = this.link
    fic.publisher = this.publisherName
    // currently we only support /art/ urls, which can only have one thing on them
    return this.getChapter(fetch, this.link).then(info => {
      fic.title = info.name
      fic.link = this.normalizeLink(info.finalUrl)
      fic.author = info.author
      fic.authorUrl = info.authorUrl
      fic.publisher = this.publisherName
      fic.description = info.description
      fic.addChapter({name: info.name || info.author, link: this.normalizeLink(info.finalUrl)})
    })
  }

  scrapeFicMetadata (fetch, fic) {
    // There's never any reason to scrape Wikipedia content.
    return Bluebird.resolve()
  }

  getChapter (fetch, chapter) {
    return fetch(chapter).spread((meta, html) => {
      let $ = cheerio.load(html)
      let base = $('base').attr('href') || meta.finalUrl
      let title = ($('meta[property="og:title"]').attr('content') || '').replace(/- YouTube$/, '')
      if (!title) throw new Error('Skipping due to missing video or shutdown account.')
      let desc = $('meta[property="og:description"]').attr('content')
      let width = $('meta[property="og:video:width"]').attr('content')
      let height = $('meta[property="og:video:height"]').attr('content')
      let link = $('meta[property="og:url"]').attr('content')
      let image = $('link[itemprop="thumbnailUrl"]').attr('href')
      let $author = $('div.yt-user-info')
      let author = $author.find('a').text()
      let authorUrl = url.resolve(base, $author.find('a').attr('href'))

      return {
        meta: chapter,
        name: title,
        description: desc,
        finalUrl: link,
        base: base,
        author: author,
        authorUrl: authorUrl,
        raw: html,
        headings: true,
        content: `<a external="false" href="${link}"><img width="${width}" height="${height}" src="${image}" alt="${title} by ${author}"></a>`
      }
    })
  }
}
module.exports = Wikipedia
<commit_msg>site/youtube: Use the correct class name<commit_after>'use strict'
const url = require('url')

const Bluebird = require('bluebird')
const cheerio = require('cheerio')

const Site = use('site')

class Youtube extends Site {
  static matches (siteUrlStr) {
    return /youtube[.]com[/]watch[?]v=|youtu.be/.test(siteUrlStr)
  }

  constructor (siteUrlStr) {
    super(siteUrlStr)
    this.publisher = 'youtube.com'
    this.publisherName = 'You Tube'
    const matches = siteUrlStr.match(/[/]watch[?]v=(.*)$/) || siteUrlStr.match(/youtu.be[/](.*)/)
    this.id = matches[1]
  }

  getFicMetadata (fetch, fic) {
    fic.link = this.link
    fic.publisher = this.publisherName
    // currently we only support /art/ urls, which can only have one thing on them
    return this.getChapter(fetch, this.link).then(info => {
      fic.title = info.name
      fic.link = this.normalizeLink(info.finalUrl)
      fic.author = info.author
      fic.authorUrl = info.authorUrl
      fic.publisher = this.publisherName
      fic.description = info.description
      fic.addChapter({name: info.name || info.author, link: this.normalizeLink(info.finalUrl)})
    })
  }

  scrapeFicMetadata (fetch, fic) {
    // There's never any reason to scrape Youtube content.
    return Bluebird.resolve()
  }

  getChapter (fetch, chapter) {
    return fetch(chapter).spread((meta, html) => {
      let $ = cheerio.load(html)
      let base = $('base').attr('href') || meta.finalUrl
      let title = ($('meta[property="og:title"]').attr('content') || '').replace(/- YouTube$/, '')
      if (!title) throw new Error('Skipping due to missing video or shutdown account.')
      let desc = $('meta[property="og:description"]').attr('content')
      let width = $('meta[property="og:video:width"]').attr('content')
      let height = $('meta[property="og:video:height"]').attr('content')
      let link = $('meta[property="og:url"]').attr('content')
      let image = $('link[itemprop="thumbnailUrl"]').attr('href')
      let $author = $('div.yt-user-info')
      let author = $author.find('a').text()
      let authorUrl = url.resolve(base, $author.find('a').attr('href'))

      return {
        meta: chapter,
        name: title,
        description: desc,
        finalUrl: link,
        base: base,
        author: author,
        authorUrl: authorUrl,
        raw: html,
        headings: true,
        content: `<a external="false" href="${link}"><img width="${width}" height="${height}" src="${image}" alt="${title} by ${author}"></a>`
      }
    })
  }
}
module.exports = Youtube
<|endoftext|>


<commit_before>/* We don't import parcel.component.js from this file intentionally. See comment
 * in that file for why
 */

// React context that gives any react component the single-spa props
export let SingleSpaContext = null

const defaultOpts = {
  // required opts
  React: null,
  ReactDOM: null,
  rootComponent: null,
  loadRootComponent: null,
  suppressComponentDidCatchWarning: false,

  // optional opts
  domElementGetter: null,
  parcelCanUpdate: true, // by default, allow parcels created with single-spa-react to be updated
}

export default function singleSpaReact(userOpts) {
  if (typeof userOpts !== 'object') {
    throw new Error(`single-spa-react requires a configuration object`);
  }

  const opts = {
    ...defaultOpts,
    ...userOpts,
  };

  if (!opts.React) {
    throw new Error(`single-spa-react must be passed opts.React`);
  }

  if (!opts.ReactDOM) {
    throw new Error(`single-spa-react must be passed opts.ReactDOM`);
  }

  if (!opts.rootComponent && !opts.loadRootComponent) {
    throw new Error(`single-spa-react must be passed opts.rootComponent or opts.loadRootComponent`);
  }

  if (!SingleSpaContext && opts.React.createContext) {
    SingleSpaContext = opts.React.createContext()
  }

  const lifecycles = {
    bootstrap: bootstrap.bind(null, opts),
    mount: mount.bind(null, opts),
    unmount: unmount.bind(null, opts),
  };

  if (opts.parcelCanUpdate) {
    lifecycles.update = update.bind(null, opts)
  }

  return lifecycles
}

function bootstrap(opts, props) {
  if (opts.rootComponent) {
    // This is a class or stateless function component
    return Promise.resolve()
  } else {
    // They passed a promise that resolves with the react component. Wait for it to resolve before mounting
    return opts
      .loadRootComponent()
      .then(resolvedComponent => {
        opts.rootComponent = resolvedComponent
      })
  }
}

function mount(opts, props) {
  return new Promise((resolve, reject) => {

    if (!opts.suppressComponentDidCatchWarning && atLeastReact16(opts.React)) {
      if (!opts.rootComponent.prototype) {
        console.warn(`single-spa-react: ${props.name || props.appName || props.childAppName}'s rootComponent does not have a prototype.  If using a functional component, wrap it in an error boundary or other class that implements componentDidCatch to avoid accidentally unmounting the entire single-spa application`)
      } else if (!opts.rootComponent.prototype.componentDidCatch) {
        console.warn(`single-spa-react: ${props.name || props.appName || props.childAppName}'s rootComponent should implement componentDidCatch to avoid accidentally unmounting the entire single-spa application.`);
      }
    }

    const domElementGetter = chooseDomElementGetter(opts, props)

    if (typeof domElementGetter !== 'function') {
      throw new Error(`single-spa-react: the domElementGetter for react application '${props.appName || props.name}' is not a function`)
    }

    const whenFinished = function() {
      resolve(this);
    };

    const rootComponentElement = opts.React.createElement(opts.rootComponent, props)
    const elementToRender = SingleSpaContext ? opts.React.createElement(SingleSpaContext.Provider, {value: props}, rootComponentElement) : rootComponentElement
    const domElement = getRootDomEl(domElementGetter, props)
    const renderedComponent = reactDomRender({elementToRender, domElement, whenFinished, opts})
    opts.domElement = domElement
  })
}

function unmount(opts, props) {
  return Promise
    .resolve()
    .then(() => {
      opts.ReactDOM.unmountComponentAtNode(opts.domElement);
    })
}

function update(opts, props) {
  return new Promise((resolve, reject) => {
    const whenFinished = function() {
      resolve(this);
    };

    const rootComponentElement = opts.React.createElement(opts.rootComponent, props)
    const elementToRender = SingleSpaContext ? opts.React.createElement(SingleSpaContext.Provider, {value: props}, rootComponentElement) : rootComponentElement
    const renderedComponent = reactDomRender({elementToRender, domElement:opts.domElement, whenFinished, opts})
  })
}

function getRootDomEl(domElementGetter, props) {
  const el = domElementGetter();
  if (!el) {
    throw new Error(`single-spa-react: domElementGetter function for application '${props.appName || props.name}' did not return a valid dom element. Please pass a valid domElement or domElementGetter via opts or props`);
  }

  return el;
}

function atLeastReact16(React) {
  if (React && typeof React.version === 'string' && React.version.indexOf('.') >= 0) {
    const majorVersionString = React.version.slice(0, React.version.indexOf('.'));
    try {
      return Number(majorVersionString) >= 16;
    } catch(err) {
      return false;
    }
  } else {
    return false;
  }
}

function chooseDomElementGetter(opts, props) {
  props = props && props.customProps ? props.customProps : props
  if (props.domElement) {
    return () => props.domElement
  } else if (props.domElementGetter) {
    return props.domElementGetter
  } else if (opts.domElementGetter) {
    return opts.domElementGetter
  } else {
    return defaultDomElementGetter(props)
  }
}

function defaultDomElementGetter(props) {
  const htmlId = `single-spa-application:${props.appName || props.name}`
  if (!htmlId) {
    throw Error(`single-spa-react was not given an application name as a prop, so it can't make a unique dom element container for the react application`)
  }

  return function defaultDomEl() {
    let domElement = document.getElementById(htmlId)
    if (!domElement) {
      domElement = document.createElement('div')
      domElement.id = htmlId
      document.body.appendChild(domElement)
    }

    return domElement
  }
}

function reactDomRender({opts, elementToRender, domElement, whenFinished}) {
  if(opts.renderType === 'createRoot') {
    return opts.ReactDOM.createRoot(domElement).render(elementToRender, whenFinished)
  }

  if(opts.renderType === 'createBlockingRoot') {
    return opts.ReactDOM.createBlockingRoot(domElement).render(elementToRender, whenFinished)
  }

  if(opts.renderType === 'hydrate') {
    return opts.ReactDOM.hydrate(elementToRender, domElement, whenFinished)
  }

  // default to this if 'renderType' is null or doesn't match the other options
  return opts.ReactDOM.render(elementToRender, domElement, whenFinished)
}
<commit_msg>Fix check to ensure an application name is given as a prop (#59)<commit_after>/* We don't import parcel.component.js from this file intentionally. See comment
 * in that file for why
 */

// React context that gives any react component the single-spa props
export let SingleSpaContext = null

const defaultOpts = {
  // required opts
  React: null,
  ReactDOM: null,
  rootComponent: null,
  loadRootComponent: null,
  suppressComponentDidCatchWarning: false,

  // optional opts
  domElementGetter: null,
  parcelCanUpdate: true, // by default, allow parcels created with single-spa-react to be updated
}

export default function singleSpaReact(userOpts) {
  if (typeof userOpts !== 'object') {
    throw new Error(`single-spa-react requires a configuration object`);
  }

  const opts = {
    ...defaultOpts,
    ...userOpts,
  };

  if (!opts.React) {
    throw new Error(`single-spa-react must be passed opts.React`);
  }

  if (!opts.ReactDOM) {
    throw new Error(`single-spa-react must be passed opts.ReactDOM`);
  }

  if (!opts.rootComponent && !opts.loadRootComponent) {
    throw new Error(`single-spa-react must be passed opts.rootComponent or opts.loadRootComponent`);
  }

  if (!SingleSpaContext && opts.React.createContext) {
    SingleSpaContext = opts.React.createContext()
  }

  const lifecycles = {
    bootstrap: bootstrap.bind(null, opts),
    mount: mount.bind(null, opts),
    unmount: unmount.bind(null, opts),
  };

  if (opts.parcelCanUpdate) {
    lifecycles.update = update.bind(null, opts)
  }

  return lifecycles
}

function bootstrap(opts, props) {
  if (opts.rootComponent) {
    // This is a class or stateless function component
    return Promise.resolve()
  } else {
    // They passed a promise that resolves with the react component. Wait for it to resolve before mounting
    return opts
      .loadRootComponent()
      .then(resolvedComponent => {
        opts.rootComponent = resolvedComponent
      })
  }
}

function mount(opts, props) {
  return new Promise((resolve, reject) => {

    if (!opts.suppressComponentDidCatchWarning && atLeastReact16(opts.React)) {
      if (!opts.rootComponent.prototype) {
        console.warn(`single-spa-react: ${props.name || props.appName || props.childAppName}'s rootComponent does not have a prototype.  If using a functional component, wrap it in an error boundary or other class that implements componentDidCatch to avoid accidentally unmounting the entire single-spa application`)
      } else if (!opts.rootComponent.prototype.componentDidCatch) {
        console.warn(`single-spa-react: ${props.name || props.appName || props.childAppName}'s rootComponent should implement componentDidCatch to avoid accidentally unmounting the entire single-spa application.`);
      }
    }

    const domElementGetter = chooseDomElementGetter(opts, props)

    if (typeof domElementGetter !== 'function') {
      throw new Error(`single-spa-react: the domElementGetter for react application '${props.appName || props.name}' is not a function`)
    }

    const whenFinished = function() {
      resolve(this);
    };

    const rootComponentElement = opts.React.createElement(opts.rootComponent, props)
    const elementToRender = SingleSpaContext ? opts.React.createElement(SingleSpaContext.Provider, {value: props}, rootComponentElement) : rootComponentElement
    const domElement = getRootDomEl(domElementGetter, props)
    const renderedComponent = reactDomRender({elementToRender, domElement, whenFinished, opts})
    opts.domElement = domElement
  })
}

function unmount(opts, props) {
  return Promise
    .resolve()
    .then(() => {
      opts.ReactDOM.unmountComponentAtNode(opts.domElement);
    })
}

function update(opts, props) {
  return new Promise((resolve, reject) => {
    const whenFinished = function() {
      resolve(this);
    };

    const rootComponentElement = opts.React.createElement(opts.rootComponent, props)
    const elementToRender = SingleSpaContext ? opts.React.createElement(SingleSpaContext.Provider, {value: props}, rootComponentElement) : rootComponentElement
    const renderedComponent = reactDomRender({elementToRender, domElement:opts.domElement, whenFinished, opts})
  })
}

function getRootDomEl(domElementGetter, props) {
  const el = domElementGetter();
  if (!el) {
    throw new Error(`single-spa-react: domElementGetter function for application '${props.appName || props.name}' did not return a valid dom element. Please pass a valid domElement or domElementGetter via opts or props`);
  }

  return el;
}

function atLeastReact16(React) {
  if (React && typeof React.version === 'string' && React.version.indexOf('.') >= 0) {
    const majorVersionString = React.version.slice(0, React.version.indexOf('.'));
    try {
      return Number(majorVersionString) >= 16;
    } catch(err) {
      return false;
    }
  } else {
    return false;
  }
}

function chooseDomElementGetter(opts, props) {
  props = props && props.customProps ? props.customProps : props
  if (props.domElement) {
    return () => props.domElement
  } else if (props.domElementGetter) {
    return props.domElementGetter
  } else if (opts.domElementGetter) {
    return opts.domElementGetter
  } else {
    return defaultDomElementGetter(props)
  }
}

function defaultDomElementGetter(props) {
  const appName = props.appName || props.name
  if (!appName) {
    throw Error(`single-spa-react was not given an application name as a prop, so it can't make a unique dom element container for the react application`)
  }
  const htmlId = `single-spa-application:${appName}`

  return function defaultDomEl() {
    let domElement = document.getElementById(htmlId)
    if (!domElement) {
      domElement = document.createElement('div')
      domElement.id = htmlId
      document.body.appendChild(domElement)
    }

    return domElement
  }
}

function reactDomRender({opts, elementToRender, domElement, whenFinished}) {
  if(opts.renderType === 'createRoot') {
    return opts.ReactDOM.createRoot(domElement).render(elementToRender, whenFinished)
  }

  if(opts.renderType === 'createBlockingRoot') {
    return opts.ReactDOM.createBlockingRoot(domElement).render(elementToRender, whenFinished)
  }

  if(opts.renderType === 'hydrate') {
    return opts.ReactDOM.hydrate(elementToRender, domElement, whenFinished)
  }

  // default to this if 'renderType' is null or doesn't match the other options
  return opts.ReactDOM.render(elementToRender, domElement, whenFinished)
}
<|endoftext|>