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

  angular
    .module('openSenseMapApp')
    .controller('RegisterController', RegisterController);

  RegisterController.$inject = ['$scope', '$translate', '$timeout', 'SensorIcons', 'WizardHandler', 'AccountService', 'osemMapData'];

  function RegisterController ($scope, $translate, $timeout, SensorIcons, WizardHandler, AccountService, osemMapData) {
    var vm = this;
    vm.stepTitle = '';
    vm.stepIndex = 0;
    vm.showNext = true;
    vm.tag = '';
    vm.geolocationError = {
      error: false,
      message: ''
    };

    //new sensebox object
    vm.newSenseBox = {
      name: '',
      exposure: '',
      location: []
    };
    vm.modelSelected = {
      id: '',
      name: false
    };
    vm.extensions = {
      feinstaub: {
        id: ''
      }
    }
    vm.mqtt = {
      enabled: false,
      url: '',
      topic: '',
      messageFormat: '',
      decodeOptions: '',
      connectionOptions: ''
    };
    vm.ttnEnabled = false;
    vm.validTTNconfig = true;
    vm.validMQTTURL = false;
    vm.ttn = {
      profile: 'sensebox/home',
      app_id: '',
      dev_id: '',
      decodeOptions: '[]'
    };
    vm.open = {
      collapse1: true,
      collapse2: false,
      collapse3: false,
      collapse4: false,
      collapse5: false
    };

    vm.markers = {};
    vm.sensors = [];
    vm.editMarkerInput = {};
    vm.sensorSetup = '';
    vm.editing = {};
    vm.isCustom = {};
    vm.events = {
      autolocation: true
    };

    vm.enterEvent = enterEvent;
    vm.add = add;
    vm.remove = remove;
    vm.setSensorIcon = setSensorIcon;
    vm.completeRegistration = completeRegistration;
    vm.setStepTitle = setStepTitle;
    vm.stepBack = stepBack;
    vm.stepForward = stepForward;
    vm.stepIsValidChange = stepIsValidChange;
    vm.isSenseBoxModel = isSenseBoxModel;
    vm.stepIsValid = false;
    vm.senseBoxSetupValid = senseBoxSetupValid;

    activate();

    ////

    function activate() {
      vm.icons = SensorIcons;
      vm.editMarkerInput =  angular.copy(vm.markers);
      vm.registering = false;
      $translate('STEP0').then(function (msg) {
        vm.stepTitle = msg;
      });
    }

    function isSenseBoxModel () {
      if (vm.modelSelected.id.startsWith('home')) {
        return false;
      }

      return true;
    }

    function setStepTitle() {
      vm.stepTitle = WizardHandler.wizard('RegistrationWizard').currentStepTitle();
    }

    function stepBack () {
      WizardHandler.wizard('RegistrationWizard').previous();
      setStepTitle();
      if (WizardHandler.wizard('RegistrationWizard').currentStepNumber()-1 === 2) {
        vm.showNext = true;
      } else {
        vm.showNext = true;
        vm.stepIsValid = false;
      }
    }

    function stepForward () {
      WizardHandler.wizard('RegistrationWizard').next();
      setStepTitle();
      if (WizardHandler.wizard('RegistrationWizard').currentStepNumber() === 2) {
        vm.showNext = false;
      } else {
        vm.showNext = true;
      }
    }

    function stepIsValidChange (value) {
      if (WizardHandler.wizard('RegistrationWizard').currentStepNumber() === 2) {
        vm.stepIsValid = value;
      }
    }

    function enterEvent(keyEvent) {
      if (keyEvent.which === 13)
        var stepNumber = WizardHandler.wizard('RegistrationWizard').currentStepNumber();
        switch (stepNumber) {
          case 2:
            if (vm.modelSelected.id === false) {
              vm.invalidHardware = true;
            } else {
              vm.invalidHardware = false;
            }
            if (vm.sensors.length === 0) {
              vm.sensorIncomplete = true;
            } else {
              vm.sensorIncomplete = false;
            }
            if (senseboxForm.$valid) {
              WizardHandler.wizard('RegistrationWizard').next();
            }
            break;
          case 3:
            completeRegistration();
        }
    }

    function add () {
      var sensor = {
        id: vm.sensors.length,
        icon: '',
        title: '',
        unit: '',
        sensorType: ''
      };
      vm.sensors.push(sensor);
      vm.sensorSetup = JSON.stringify(vm.sensors);
      edit(sensor.id);
      vm.sensorIncomplete = false;
    };

    function remove (index) {
        vm.sensors.splice(index,1);
        vm.isCustom[index]=false;
        vm.editing[index]=false;
        for(var i=0; i < vm.sensors.length; i++){
          vm.sensors[i].id=i;
        }
        if ( vm.sensors.length === 0) {
          vm.sensorSetup = '';
          vm.sensorIncomplete = true;
        } else {
          vm.sensorSetup = JSON.stringify($scope.sensors);
        }
      };

    function edit (index) {
      vm.editing[index] = true;
    };

    function setSensorIcon (sensor, newIcon) {
      sensor.icon = newIcon.name;
    };

    function downloadArduino (boxId) {
      AccountService.getScript(boxId)
        .then(function (data) {
          vm.boxScript = data;
        })
        .catch(function (error) {
          //todo: display error and reload button
        });
    };

    function completeRegistration () {
      setStepTitle();
      vm.alerts = [];

      if (vm.mqtt.enabled) {
        vm.newSenseBox.mqtt = vm.mqtt;
      }
      if (vm.ttnEnabled) {
        vm.newSenseBox.ttn = vm.ttn;
      }
      vm.newSenseBox.location.push(vm.markers.box.lng);
      vm.newSenseBox.location.push(vm.markers.box.lat);
      vm.registering = true;

      if (vm.tag !== '') {
        vm.newSenseBox.grouptag = vm.tag;
      }

      if (vm.modelSelected.id === 'custom') {
        for (var i = 0; i < vm.sensors.length; i++) {
          vm.newSenseBox.sensors = vm.sensors;
        }
      } else {
        vm.newSenseBox.model = vm.modelSelected.id;
      }

      if (vm.extensions.feinstaub.id !== '') {
        vm.newSenseBox.model = vm.newSenseBox.model + vm.extensions.feinstaub.id;
      }

      AccountService.postNewBox(vm.newSenseBox)
        .then(function (data) {
          console.log('new sensebox successful', data);
          vm.newSenseBox.id = data.data._id;
          WizardHandler.wizard('RegistrationWizard').next();
          vm.registering = false;
          $translate('REGISTRATION_SUCCESS').then(function (msg) {
            var alert = {
              type: 'success',
              msg: msg
            };
            vm.alerts.push(alert);
            vm.regSuccess = true;
          });
          downloadArduino(data.data._id);
          vm.registeredSensors = data.data['sensors'];
          vm.stepIsValid = true;
          vm.stepIndex = 0;
        })
        .catch(function (error) {
          console.log(error);
          $translate('REGISTRATION_FAIL').then(function (msg) {
            var alert = {
              type: 'danger',
              msg: msg
            };
            vm.alerts.push(alert);
          });
          vm.registering = false;
        });
    }

    function senseBoxSetupValid () {
      var validTTN = true;
      var validMQTT = true;

      if (vm.ttnEnabled) {
        validTTN = vm.validTTNconfig;
      }

      if (vm.mqtt.enabled) {
        validMQTT = vm.validMQTTURL;
      }

      return validTTN && validMQTT;
    }

    ////

    $scope.$on('osemMapClick.map_register', function(e, args) {
      if (Object.keys(vm.markers).length === 0) {
        vm.markers = {
          box: {
            'lat': parseFloat(args.latlng.lat.toFixed(6)),
            'lng': parseFloat(args.latlng.lng.toFixed(6)),
            'draggable': true,
            'icon': {
              markerColor: 'green'
            }
          }
        };
      } else {
        vm.markers = angular.copy(vm.markers);
        vm.markers.box.lat = parseFloat(args.latlng.lat.toFixed(6));
        vm.markers.box.lng = parseFloat(args.latlng.lng.toFixed(6));
      }
      vm.editMarkerInput =  angular.copy(vm.markers);
    });

    $scope.$on('osemMarkerDragend.map_register', function(e, args) {
      vm.markers = angular.copy(vm.markers);
      vm.markers.box.lat = parseFloat(args.target._latlng.lat.toFixed(6));
      vm.markers.box.lng = parseFloat(args.target._latlng.lng.toFixed(6));
      vm.editMarkerInput =  angular.copy(vm.markers);
    });

    $scope.$on('osemMapOnLocationFound.map_register', function (e, args) {
      vm.map = args.map;
      if (Object.keys(vm.markers).length === 0) {
        vm.markers = {
          box: {
            'lat': parseFloat(args.latlng.lat.toFixed(6)),
            'lng': parseFloat(args.latlng.lng.toFixed(6)),
            'draggable': true,
            'icon': {
              'markerColor': 'green'
            }
          }
        };
      } else {
        vm.markers = angular.copy(vm.markers);
        vm.markers.box.lat = parseFloat(args.latlng.lat.toFixed(6));
        vm.markers.box.lng = parseFloat(args.latlng.lng.toFixed(6));
      }
      vm.editMarkerInput =  angular.copy(vm.markers);
    });

    $scope.$on('osemMapOnLocationError.map_register', function(e, args){
      vm.geolocationError.error = true;
      vm.geolocationError.message = args.message;
    });

    $scope.$on('wizard:stepChanged', function (step, index) {
      vm.stepIndex = index.index;

      $translate(index.step.wzData.translation).then(function (msg) {
        vm.stepTitle = msg;
      });
      if (index.index === 1) {
        $timeout(function () {
          osemMapData.getMap('map_register')
            .then(function (map) {
              map.invalidateSize();
              if (!vm.geolocationError.error) {
                map.setView([vm.markers.box.lat,vm.markers.box.lng],16);
              }
            })
            .catch(function (error) {
              console.log(error);
            });
        }, 200);
      }
    });

    $scope.$watch('register.modelSelected.id', function(newValue) {
      switch(newValue) {
        case 'homeEthernet':
          vm.modelSelected.name = 'senseBox Home Ethernet';
          vm.sensorSetup = vm.modelSelected.id;
          break;
        case 'homeWifi':
          vm.modelSelected.name = 'senseBox Home Wifi';
          vm.sensorSetup = vm.modelSelected.id;
          break;
        case 'luftdaten_sds011':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor ohne Temperatur/Feuchtesensor';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_dht11':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit DHT11';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_dht22':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit DHT22';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_bmp180':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit BMP180';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_bme280':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit BME280';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'custom':
          vm.sensorSetup = '';
          vm.extensions.feinstaub.id = '';
          break;
        default:
          vm.modelSelected.name = false;
          break;
      }
      vm.invalidHardware = false;
    });

    // check if valid json for ttn decodeOptions
    $scope.$watch('register.ttn.decodeOptions', function(newValue) {
      if (!newValue.length) {
        return vm.validTTNconfig = true;
      }
      try {
        if (JSON.parse(vm.ttn.decodeOptions).constructor !== Array) {
          throw 'must be an array';
        }
        vm.validTTNconfig = true;
      } catch (e) {
        vm.validTTNconfig = false;
      }
    });

    $scope.$watch('register.mqtt.url', function(newValue) {
      if (angular.isUndefined(newValue) || !newValue.length) {
        return vm.validMQTTURL = false;
      }
      try {
        if (vm.mqtt.url.indexOf('mqtt://') == 0 || vm.mqtt.url.indexOf('mqtts://') == 0) {
          vm.validMQTTURL = true;
        } else {
          throw 'must start with mqtt[s]://';
        }
      } catch (e) {
        vm.validMQTTURL = false;
      }
    });

    $scope.$watchCollection('register.open.collapse3',function (newValue) {
      if (newValue) {
        vm.modelSelected.id = 'custom';
      }
    });

    $scope.$watchCollection('register.editMarkerInput.box', function (newValue, oldValue) {
      if (newValue && newValue.lat && newValue.lng && !angular.equals(newValue, oldValue)) {
        if (Object.keys(vm.markers).length === 0) {
          vm.markers = {
            box: {
              'lat': parseFloat(newValue.lat.toFixed(6)),
              'lng': parseFloat(newValue.lng.toFixed(6)),
              'draggable': true,
              'icon': {
                'markerColor': 'green'
              }
            }
          };
        } else {
          vm.markers = angular.copy(vm.markers);
          vm.markers.box.lat = parseFloat(newValue.lat.toFixed(6));
          vm.markers.box.lng = parseFloat(newValue.lng.toFixed(6));
        }
        vm.editMarkerInput =  angular.copy(vm.markers);
      }
    });
  }
})();
<commit_msg>fix TTN decodeOptions registration<commit_after>(function () {
  'use strict';

  angular
    .module('openSenseMapApp')
    .controller('RegisterController', RegisterController);

  RegisterController.$inject = ['$scope', '$translate', '$timeout', 'SensorIcons', 'WizardHandler', 'AccountService', 'osemMapData'];

  function RegisterController ($scope, $translate, $timeout, SensorIcons, WizardHandler, AccountService, osemMapData) {
    var vm = this;
    vm.stepTitle = '';
    vm.stepIndex = 0;
    vm.showNext = true;
    vm.tag = '';
    vm.geolocationError = {
      error: false,
      message: ''
    };

    //new sensebox object
    vm.newSenseBox = {
      name: '',
      exposure: '',
      location: []
    };
    vm.modelSelected = {
      id: '',
      name: false
    };
    vm.extensions = {
      feinstaub: {
        id: ''
      }
    }
    vm.mqtt = {
      enabled: false,
      url: '',
      topic: '',
      messageFormat: '',
      decodeOptions: '',
      connectionOptions: ''
    };
    vm.ttnEnabled = false;
    vm.validTTNconfig = true;
    vm.validMQTTURL = false;
    vm.ttn = {
      profile: 'sensebox/home',
      app_id: '',
      dev_id: '',
      decodeOptions: '[]'
    };
    vm.open = {
      collapse1: true,
      collapse2: false,
      collapse3: false,
      collapse4: false,
      collapse5: false
    };

    vm.markers = {};
    vm.sensors = [];
    vm.editMarkerInput = {};
    vm.sensorSetup = '';
    vm.editing = {};
    vm.isCustom = {};
    vm.events = {
      autolocation: true
    };

    vm.enterEvent = enterEvent;
    vm.add = add;
    vm.remove = remove;
    vm.setSensorIcon = setSensorIcon;
    vm.completeRegistration = completeRegistration;
    vm.setStepTitle = setStepTitle;
    vm.stepBack = stepBack;
    vm.stepForward = stepForward;
    vm.stepIsValidChange = stepIsValidChange;
    vm.isSenseBoxModel = isSenseBoxModel;
    vm.stepIsValid = false;
    vm.senseBoxSetupValid = senseBoxSetupValid;

    activate();

    ////

    function activate() {
      vm.icons = SensorIcons;
      vm.editMarkerInput =  angular.copy(vm.markers);
      vm.registering = false;
      $translate('STEP0').then(function (msg) {
        vm.stepTitle = msg;
      });
    }

    function isSenseBoxModel () {
      if (vm.modelSelected.id.startsWith('home')) {
        return false;
      }

      return true;
    }

    function setStepTitle() {
      vm.stepTitle = WizardHandler.wizard('RegistrationWizard').currentStepTitle();
    }

    function stepBack () {
      WizardHandler.wizard('RegistrationWizard').previous();
      setStepTitle();
      if (WizardHandler.wizard('RegistrationWizard').currentStepNumber()-1 === 2) {
        vm.showNext = true;
      } else {
        vm.showNext = true;
        vm.stepIsValid = false;
      }
    }

    function stepForward () {
      WizardHandler.wizard('RegistrationWizard').next();
      setStepTitle();
      if (WizardHandler.wizard('RegistrationWizard').currentStepNumber() === 2) {
        vm.showNext = false;
      } else {
        vm.showNext = true;
      }
    }

    function stepIsValidChange (value) {
      if (WizardHandler.wizard('RegistrationWizard').currentStepNumber() === 2) {
        vm.stepIsValid = value;
      }
    }

    function enterEvent(keyEvent) {
      if (keyEvent.which === 13)
        var stepNumber = WizardHandler.wizard('RegistrationWizard').currentStepNumber();
        switch (stepNumber) {
          case 2:
            if (vm.modelSelected.id === false) {
              vm.invalidHardware = true;
            } else {
              vm.invalidHardware = false;
            }
            if (vm.sensors.length === 0) {
              vm.sensorIncomplete = true;
            } else {
              vm.sensorIncomplete = false;
            }
            if (senseboxForm.$valid) {
              WizardHandler.wizard('RegistrationWizard').next();
            }
            break;
          case 3:
            completeRegistration();
        }
    }

    function add () {
      var sensor = {
        id: vm.sensors.length,
        icon: '',
        title: '',
        unit: '',
        sensorType: ''
      };
      vm.sensors.push(sensor);
      vm.sensorSetup = JSON.stringify(vm.sensors);
      edit(sensor.id);
      vm.sensorIncomplete = false;
    };

    function remove (index) {
        vm.sensors.splice(index,1);
        vm.isCustom[index]=false;
        vm.editing[index]=false;
        for(var i=0; i < vm.sensors.length; i++){
          vm.sensors[i].id=i;
        }
        if ( vm.sensors.length === 0) {
          vm.sensorSetup = '';
          vm.sensorIncomplete = true;
        } else {
          vm.sensorSetup = JSON.stringify($scope.sensors);
        }
      };

    function edit (index) {
      vm.editing[index] = true;
    };

    function setSensorIcon (sensor, newIcon) {
      sensor.icon = newIcon.name;
    };

    function downloadArduino (boxId) {
      AccountService.getScript(boxId)
        .then(function (data) {
          vm.boxScript = data;
        })
        .catch(function (error) {
          //todo: display error and reload button
        });
    };

    function completeRegistration () {
      setStepTitle();
      vm.alerts = [];

      if (vm.mqtt.enabled) {
        vm.newSenseBox.mqtt = vm.mqtt;
      }
      if (vm.ttnEnabled) {
        vm.newSenseBox.ttn = vm.ttn;
        vm.newSenseBox.ttn.decodeOptions = JSON.parse(vm.ttn.decodeOptions);
      }
      vm.newSenseBox.location.push(vm.markers.box.lng);
      vm.newSenseBox.location.push(vm.markers.box.lat);
      vm.registering = true;

      if (vm.tag !== '') {
        vm.newSenseBox.grouptag = vm.tag;
      }

      if (vm.modelSelected.id === 'custom') {
        for (var i = 0; i < vm.sensors.length; i++) {
          vm.newSenseBox.sensors = vm.sensors;
        }
      } else {
        vm.newSenseBox.model = vm.modelSelected.id;
      }

      if (vm.extensions.feinstaub.id !== '') {
        vm.newSenseBox.model = vm.newSenseBox.model + vm.extensions.feinstaub.id;
      }

      AccountService.postNewBox(vm.newSenseBox)
        .then(function (data) {
          console.log('new sensebox successful', data);
          vm.newSenseBox.id = data.data._id;
          WizardHandler.wizard('RegistrationWizard').next();
          vm.registering = false;
          $translate('REGISTRATION_SUCCESS').then(function (msg) {
            var alert = {
              type: 'success',
              msg: msg
            };
            vm.alerts.push(alert);
            vm.regSuccess = true;
          });
          downloadArduino(data.data._id);
          vm.registeredSensors = data.data['sensors'];
          vm.stepIsValid = true;
          vm.stepIndex = 0;
        })
        .catch(function (error) {
          console.log(error);
          $translate('REGISTRATION_FAIL').then(function (msg) {
            var alert = {
              type: 'danger',
              msg: msg
            };
            vm.alerts.push(alert);
          });
          vm.registering = false;
        });
    }

    function senseBoxSetupValid () {
      var validTTN = true;
      var validMQTT = true;

      if (vm.ttnEnabled) {
        validTTN = vm.validTTNconfig;
      }

      if (vm.mqtt.enabled) {
        validMQTT = vm.validMQTTURL;
      }

      return validTTN && validMQTT;
    }

    ////

    $scope.$on('osemMapClick.map_register', function(e, args) {
      if (Object.keys(vm.markers).length === 0) {
        vm.markers = {
          box: {
            'lat': parseFloat(args.latlng.lat.toFixed(6)),
            'lng': parseFloat(args.latlng.lng.toFixed(6)),
            'draggable': true,
            'icon': {
              markerColor: 'green'
            }
          }
        };
      } else {
        vm.markers = angular.copy(vm.markers);
        vm.markers.box.lat = parseFloat(args.latlng.lat.toFixed(6));
        vm.markers.box.lng = parseFloat(args.latlng.lng.toFixed(6));
      }
      vm.editMarkerInput =  angular.copy(vm.markers);
    });

    $scope.$on('osemMarkerDragend.map_register', function(e, args) {
      vm.markers = angular.copy(vm.markers);
      vm.markers.box.lat = parseFloat(args.target._latlng.lat.toFixed(6));
      vm.markers.box.lng = parseFloat(args.target._latlng.lng.toFixed(6));
      vm.editMarkerInput =  angular.copy(vm.markers);
    });

    $scope.$on('osemMapOnLocationFound.map_register', function (e, args) {
      vm.map = args.map;
      if (Object.keys(vm.markers).length === 0) {
        vm.markers = {
          box: {
            'lat': parseFloat(args.latlng.lat.toFixed(6)),
            'lng': parseFloat(args.latlng.lng.toFixed(6)),
            'draggable': true,
            'icon': {
              'markerColor': 'green'
            }
          }
        };
      } else {
        vm.markers = angular.copy(vm.markers);
        vm.markers.box.lat = parseFloat(args.latlng.lat.toFixed(6));
        vm.markers.box.lng = parseFloat(args.latlng.lng.toFixed(6));
      }
      vm.editMarkerInput =  angular.copy(vm.markers);
    });

    $scope.$on('osemMapOnLocationError.map_register', function(e, args){
      vm.geolocationError.error = true;
      vm.geolocationError.message = args.message;
    });

    $scope.$on('wizard:stepChanged', function (step, index) {
      vm.stepIndex = index.index;

      $translate(index.step.wzData.translation).then(function (msg) {
        vm.stepTitle = msg;
      });
      if (index.index === 1) {
        $timeout(function () {
          osemMapData.getMap('map_register')
            .then(function (map) {
              map.invalidateSize();
              if (!vm.geolocationError.error) {
                map.setView([vm.markers.box.lat,vm.markers.box.lng],16);
              }
            })
            .catch(function (error) {
              console.log(error);
            });
        }, 200);
      }
    });

    $scope.$watch('register.modelSelected.id', function(newValue) {
      switch(newValue) {
        case 'homeEthernet':
          vm.modelSelected.name = 'senseBox Home Ethernet';
          vm.sensorSetup = vm.modelSelected.id;
          break;
        case 'homeWifi':
          vm.modelSelected.name = 'senseBox Home Wifi';
          vm.sensorSetup = vm.modelSelected.id;
          break;
        case 'luftdaten_sds011':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor ohne Temperatur/Feuchtesensor';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_dht11':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit DHT11';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_dht22':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit DHT22';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_bmp180':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit BMP180';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'luftdaten_sds011_bme280':
          vm.modelSelected.name = 'Luftdaten.info Feinstaubsensor mit BME280';
          vm.sensorSetup = vm.modelSelected.id;
          vm.extensions.feinstaub.id = '';
          break;
        case 'custom':
          vm.sensorSetup = '';
          vm.extensions.feinstaub.id = '';
          break;
        default:
          vm.modelSelected.name = false;
          break;
      }
      vm.invalidHardware = false;
    });

    // check if valid json for ttn decodeOptions
    $scope.$watch('register.ttn.decodeOptions', function(newValue) {
      if (!newValue.length) {
        return vm.validTTNconfig = true;
      }
      try {
        if (JSON.parse(vm.ttn.decodeOptions).constructor !== Array) {
          throw 'must be an array';
        }
        vm.validTTNconfig = true;
      } catch (e) {
        vm.validTTNconfig = false;
      }
    });

    $scope.$watch('register.mqtt.url', function(newValue) {
      if (angular.isUndefined(newValue) || !newValue.length) {
        return vm.validMQTTURL = false;
      }
      try {
        if (vm.mqtt.url.indexOf('mqtt://') == 0 || vm.mqtt.url.indexOf('mqtts://') == 0) {
          vm.validMQTTURL = true;
        } else {
          throw 'must start with mqtt[s]://';
        }
      } catch (e) {
        vm.validMQTTURL = false;
      }
    });

    $scope.$watchCollection('register.open.collapse3',function (newValue) {
      if (newValue) {
        vm.modelSelected.id = 'custom';
      }
    });

    $scope.$watchCollection('register.editMarkerInput.box', function (newValue, oldValue) {
      if (newValue && newValue.lat && newValue.lng && !angular.equals(newValue, oldValue)) {
        if (Object.keys(vm.markers).length === 0) {
          vm.markers = {
            box: {
              'lat': parseFloat(newValue.lat.toFixed(6)),
              'lng': parseFloat(newValue.lng.toFixed(6)),
              'draggable': true,
              'icon': {
                'markerColor': 'green'
              }
            }
          };
        } else {
          vm.markers = angular.copy(vm.markers);
          vm.markers.box.lat = parseFloat(newValue.lat.toFixed(6));
          vm.markers.box.lng = parseFloat(newValue.lng.toFixed(6));
        }
        vm.editMarkerInput =  angular.copy(vm.markers);
      }
    });
  }
})();
<|endoftext|>


<commit_before>import {
  NEXGDDP_SET_MAP_ZOOM,
  NEXGDDP_SET_MAP_CENTER,
  NEXGDDP_SET_MARKER_POSITION,
  NEXGDDP_SET_MAP_MODE,
  NEXGDDP_SET_GRAPH_MODE,
  NEXGDDP_SET_SCENARIO_OPTIONS,
  NEXGDDP_SET_SCENARIO_SELECTION,
  NEXGDDP_SET_RANGE1_OPTIONS,
  NEXGDDP_SET_RANGE1_SELECTION,
  NEXGDDP_SET_RANGE2_OPTIONS,
  NEXGDDP_SET_RANGE2_SELECTION
} from '../constants';

const initialState = {
  // Stat of the map
  map: {
    zoom: 3,
    center: [20, -30]
  },
  // Position of the marker
  /** @type {[number, number]} marker */
  marker: undefined,
  // Search input text content
  /** @type {string} search */
  search: undefined,
  // Mode of the map
  /** @type {'difference'|'side_by_side'|'toggle'} mapMode */
  mapMode: 'difference',
  // Mode of the graph
  /** @type {'timeseries'|'seasonal'} */
  graphMode: 'timeseries',
  // List of the scenario options and the current selection
  scenario: {
    /** @type {{ label: string, value: string }[]} options */
    options: [],
    /** @type {{ label: string, value: string }} selection */
    selection: undefined
  },
  // Range for the visualization
  range1: {
    /** @type {{ label: string, value: string }[]} options */
    options: [],
    /** @type {{ label: string, value: string }} selection */
    selection: undefined
  },
  // Comparison range for the visualization
  range2: {
    /** @type {{ label: string, value: string }[]} options */
    options: [],
    /** @type {{ label: string, value: string }} selection */
    selection: undefined
  }
};

export default function (state = initialState, action) {
  switch (action.type) {
    case NEXGDDP_SET_MAP_ZOOM: {
      return Object.assign({}, state, {
        map: Object.assign({}, state.map, { zoom: action.payload })
      });
    }

    case NEXGDDP_SET_MAP_CENTER: {
      return Object.assign({}, state, {
        map: Object.assign({}, state.map, { center: action.payload })
      });
    }

    case NEXGDDP_SET_MARKER_POSITION: {
      return Object.assign({}, state, { marker: action.payload });
    }

    case NEXGDDP_SET_MAP_MODE: {
      return Object.assign({}, state, { mapMode: action.payload });
    }

    case NEXGDDP_SET_GRAPH_MODE: {
      return Object.assign({}, state, { graphMode: action.payload });
    }

    case NEXGDDP_SET_SCENARIO_OPTIONS: {
      return Object.assign({}, state, {
        scenario: Object.assign({}, state.scenario, { options: action.payload })
      });
    }

    case NEXGDDP_SET_SCENARIO_SELECTION: {
      return Object.assign({}, state, {
        scenario: Object.assign({}, state.scenario, { selection: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE1_OPTIONS: {
      return Object.assign({}, state, {
        range1: Object.assign({}, state.range1, { options: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE1_SELECTION: {
      return Object.assign({}, state, {
        range1: Object.assign({}, state.range1, { selection: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE2_OPTIONS: {
      return Object.assign({}, state, {
        range2: Object.assign({}, state.range2, { options: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE2_SELECTION: {
      return Object.assign({}, state, {
        range2: Object.assign({}, state.range2, { selection: action.payload })
      });
    }

    default: {
      return state;
    }
  }
}
<commit_msg>Remove an unused property from the store<commit_after>import {
  NEXGDDP_SET_MAP_ZOOM,
  NEXGDDP_SET_MAP_CENTER,
  NEXGDDP_SET_MARKER_POSITION,
  NEXGDDP_SET_MAP_MODE,
  NEXGDDP_SET_GRAPH_MODE,
  NEXGDDP_SET_SCENARIO_OPTIONS,
  NEXGDDP_SET_SCENARIO_SELECTION,
  NEXGDDP_SET_RANGE1_OPTIONS,
  NEXGDDP_SET_RANGE1_SELECTION,
  NEXGDDP_SET_RANGE2_OPTIONS,
  NEXGDDP_SET_RANGE2_SELECTION
} from '../constants';

const initialState = {
  // Stat of the map
  map: {
    zoom: 3,
    center: [20, -30]
  },
  // Position of the marker
  /** @type {[number, number]} marker */
  marker: undefined,
  // Mode of the map
  /** @type {'difference'|'side_by_side'|'toggle'} mapMode */
  mapMode: 'difference',
  // Mode of the graph
  /** @type {'timeseries'|'seasonal'} */
  graphMode: 'timeseries',
  // List of the scenario options and the current selection
  scenario: {
    /** @type {{ label: string, value: string }[]} options */
    options: [],
    /** @type {{ label: string, value: string }} selection */
    selection: undefined
  },
  // Range for the visualization
  range1: {
    /** @type {{ label: string, value: string }[]} options */
    options: [],
    /** @type {{ label: string, value: string }} selection */
    selection: undefined
  },
  // Comparison range for the visualization
  range2: {
    /** @type {{ label: string, value: string }[]} options */
    options: [],
    /** @type {{ label: string, value: string }} selection */
    selection: undefined
  }
};

export default function (state = initialState, action) {
  switch (action.type) {
    case NEXGDDP_SET_MAP_ZOOM: {
      return Object.assign({}, state, {
        map: Object.assign({}, state.map, { zoom: action.payload })
      });
    }

    case NEXGDDP_SET_MAP_CENTER: {
      return Object.assign({}, state, {
        map: Object.assign({}, state.map, { center: action.payload })
      });
    }

    case NEXGDDP_SET_MARKER_POSITION: {
      return Object.assign({}, state, { marker: action.payload });
    }

    case NEXGDDP_SET_MAP_MODE: {
      return Object.assign({}, state, { mapMode: action.payload });
    }

    case NEXGDDP_SET_GRAPH_MODE: {
      return Object.assign({}, state, { graphMode: action.payload });
    }

    case NEXGDDP_SET_SCENARIO_OPTIONS: {
      return Object.assign({}, state, {
        scenario: Object.assign({}, state.scenario, { options: action.payload })
      });
    }

    case NEXGDDP_SET_SCENARIO_SELECTION: {
      return Object.assign({}, state, {
        scenario: Object.assign({}, state.scenario, { selection: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE1_OPTIONS: {
      return Object.assign({}, state, {
        range1: Object.assign({}, state.range1, { options: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE1_SELECTION: {
      return Object.assign({}, state, {
        range1: Object.assign({}, state.range1, { selection: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE2_OPTIONS: {
      return Object.assign({}, state, {
        range2: Object.assign({}, state.range2, { options: action.payload })
      });
    }

    case NEXGDDP_SET_RANGE2_SELECTION: {
      return Object.assign({}, state, {
        range2: Object.assign({}, state.range2, { selection: action.payload })
      });
    }

    default: {
      return state;
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

app.service ('RepoService', ['$resource', '$q', '$filter', 'GITHUB_API_URL', function ($resource, $q, $filter, GITHUB_API_URL) {
    var repoResource = $resource (GITHUB_API_URL + '/repos/:user/:repo', {user: '@user', repo: '@repo'}, {
            branches: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/branches' ,
                isArray: true
            } ,
            releases: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/releases' ,
                isArray: true
            } ,
            contributors: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/contributors' ,
                isArray: true
            } ,
            issues: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/issues' ,
                isArray: true
            } ,
            languages: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/languages'
            }
        });

    return {
        populate: function (user, repo) {
            var deferred = $q.defer () ,
                repoRequest = repoResource.get ({user: user, repo: repo}) ,
                branchesRequest = repoResource.branches ({user: user, repo: repo}) ,
                releasesRequest = repoResource.releases ({user: user, repo: repo}) ,
                contributorsRequest = repoResource.contributors ({user: user, repo: repo}) ,
                issuesRequest = repoResource.issues ({user: user, repo: repo}) ,
                languagesRequest = repoResource.languages ({user: user, repo: repo});

            $q
                .all ({
                    repo: repoRequest.$promise ,
                    branches: branchesRequest.$promise ,
                    releases: releasesRequest.$promise ,
                    contributors: contributorsRequest.$promise ,
                    issues: issuesRequest.$promise ,
                    languages: languagesRequest.$promise
                })
                .then (function (data) {
                    var repoData = data.repo ,
                        orderBy = $filter ('orderBy') ,
                        rawLanguages = [] ,
                        languages = [] ,
                        limit = 4 ,
                        total = 0;

                    repoData.branches = data.branches.length;
                    repoData.releases = data.releases.length;
                    repoData.contributors = data.contributors.length;
                    repoData.issues = data.issues.length;

                    angular.forEach (data.languages, function (perc, lang) {
                        if (lang !== '$promise' && lang !== '$resolved') {
                            if (data.languages.hasOwnProperty (lang)) {
                                rawLanguages.push ({name: lang, percentage: perc});
                            }
                        }
                    });

                    if (rawLanguages.length > 0) {
                        rawLanguages = orderBy (rawLanguages, 'perc', true);

                        if (rawLanguages.length < limit) limit = rawLanguages.length;
                        angular.forEach (rawLanguages, function (language) {
                            languages.push (language);
                            total += language.percentage;
                            limit--;

                            if (limit === 0) return false;
                        });

                        angular.forEach (languages, function (lang) {
                            lang.percentage = Math.round ((lang.percentage * 100) / total);
                        });

                        repoData.languages = languages;
                    }

                    deferred.resolve (repoData);
                }, function (error) {
                    deferred.reject (error);
                });

            return deferred.promise;
        }
    };
}]);<commit_msg>changed promise chain to get every repo data, changed issues with tags and added sort of pagination to retrieve more data<commit_after>'use strict';

app.service ('RepoService', ['$resource', '$q', '$filter', 'GITHUB_API_URL', function ($resource, $q, $filter, GITHUB_API_URL) {
    var repoResource = $resource (GITHUB_API_URL + '/repos/:user/:repo', {user: '@user', repo: '@repo'}, {
            branches: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/branches?page=0&per_page=100' ,
                isArray: true
            } ,
            releases: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/releases?page=0&per_page=100' ,
                isArray: true
            } ,
            contributors: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/contributors?page=0&per_page=100' ,
                isArray: true
            } ,
            tags: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/tags?page=0&per_page=100' ,
                isArray: true
            } ,
            languages: {
                method: 'GET' ,
                url: GITHUB_API_URL + '/repos/:user/:repo/languages?page=0&per_page=100'
            }
        });

    return {
        populate: function (user, repo) {
            var deferred = $q.defer () ,
                repoRequest = repoResource.get ({user: user, repo: repo}) ,
                branchesRequest = repoResource.branches ({user: user, repo: repo}) ,
                releasesRequest = repoResource.releases ({user: user, repo: repo}) ,
                contributorsRequest = repoResource.contributors ({user: user, repo: repo}) ,
                tagsRequest = repoResource.tags ({user: user, repo: repo}) ,
                languagesRequest = repoResource.languages ({user: user, repo: repo}) ,
                repoData = {};

            $q
                .when (repoRequest.$promise)
                .then (function (data) {
                    repoData = data;
                    return branchesRequest.$promise;
                }, function (error) {
                    repoData = {};
                    return branchesRequest.$promise;
                })
                .then (function (data) {
                    repoData.branches = data.length;
                    return releasesRequest.$promise;
                }, function (error) {
                    repoData.branches = 0;
                    return releasesRequest.$promise;
                })
                .then (function (data) {
                    repoData.releases = data.length;
                    return contributorsRequest.$promise;
                }, function (error) {
                    repoData.releases = 0;
                    return contributorsRequest.$promise;
                })
                .then (function (data) {
                    repoData.contributors = data.length;
                    return tagsRequest.$promise;
                }, function (error) {
                    repoData.contributors = 0;
                    return tagsRequest.$promise;
                })
                .then (function (data) {
                    repoData.tags = data.length;
                    return languagesRequest.$promise;
                }, function (error) {
                    repoData.tags = 0;
                    return languagesRequest.$promise;
                })
                .then (function (data) {
                    var orderBy = $filter ('orderBy') ,
                        rawLanguages = [] ,
                        languages = [] ,
                        limit = 4 ,
                        total = 0;

                    angular.forEach (data, function (perc, lang) {
                        if (lang !== '$promise' && lang !== '$resolved') {
                            if (data.hasOwnProperty (lang)) {
                                rawLanguages.push ({name: lang, percentage: perc});
                            }
                        }
                    });

                    if (rawLanguages.length > 0) {
                        rawLanguages = orderBy (rawLanguages, 'perc', true);

                        if (rawLanguages.length < limit) limit = rawLanguages.length;
                        angular.forEach (rawLanguages, function (language) {
                            languages.push (language);
                            total += language.percentage;
                            limit--;

                            if (limit === 0) return false;
                        });

                        angular.forEach (languages, function (lang) {
                            lang.percentage = Math.round ((lang.percentage * 100) / total);
                        });

                        repoData.languages = languages;
                    }
                    else repoData.languages = [];

                    deferred.resolve (repoData);
                }, function (error) {
                    repoData.languages = [];

                    deferred.resolve (repoData);
                });

            return deferred.promise;
        }
    };
}]);<|endoftext|>


<commit_before>/**
 * This is a module that is loaded per-request and provides the current
 * webapp JSGI env, request, and config objects to other modules.
 *
 * Note that this module should be loaded within the function that uses
 * it to make sure we get a fresh version. Otherwise, the state of the
 * loaded module will depend on whether the loading module is shared or not.
 */

var fileutil = require('ringo/fileutil');

var request;
var configs = [];
var configIds = [];

exports.setRequest = function(req) {
    request = req;
};

exports.getRequest = function() {
    return request;
};

/**
 * Adds a config module to the config module array and sets it as the
 * current module.
 */
exports.pushConfig = function(config, configId) {
    configs.push(config);
    configIds.push(configId);
    exports.config = config;
};

exports.getConfigs = function() {
    return configs;
};

exports.getConfig = function() {
    return config;
};

exports.loadMacros = function(context) {
    for (var i = 0; i < configs.length; i++) {
        var config = configs[i];
        if (config && Array.isArray(config.macros)) {
            for each (var moduleId in config.macros) {
                context = Object.merge(context, loadModule(moduleId, configIds[i]));
            }
        }
    }
    return context;
};

function loadModule(moduleId, parent) {
    if (typeof moduleId == "string") {
        return require(fileutil.resolveRelative(parent, moduleId));
    } else {
        return moduleId;
    }
}
<commit_msg>Forgot to commit with fileutil -> fileutils switch<commit_after>/**
 * This is a module that is loaded per-request and provides the current
 * webapp JSGI env, request, and config objects to other modules.
 *
 * Note that this module should be loaded within the function that uses
 * it to make sure we get a fresh version. Otherwise, the state of the
 * loaded module will depend on whether the loading module is shared or not.
 */

require('core/string');
var fileutils = require('ringo/fileutils');

var request;
var configs = [];
var configIds = [];

exports.setRequest = function(req) {
    request = req;
};

exports.getRequest = function() {
    return request;
};

/**
 * Adds a config module to the config module array and sets it as the
 * current module.
 */
exports.pushConfig = function(config, configId) {
    configs.push(config);
    configIds.push(configId);
    exports.config = config;
};

exports.getConfigs = function() {
    return configs;
};

exports.getConfig = function() {
    return config;
};

exports.loadMacros = function(context) {
    for (var i = 0; i < configs.length; i++) {
        var config = configs[i];
        if (config && Array.isArray(config.macros)) {
            for each (var moduleId in config.macros) {
                context = Object.merge(context,
                        loadModule(moduleId, configIds[i]));
            }
        }
    }
    return context;
};

function loadModule(moduleId, parent) {
    if (typeof moduleId == "string") {
        return require(fileutils.resolveRelative(parent, moduleId));
    } else {
        return moduleId;
    }
}
<|endoftext|>


<commit_before>import _clone from 'lodash-es/clone';
import _isEmpty from 'lodash-es/isEmpty';
import _isEqual from 'lodash-es/isEqual';

import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    selectAll as d3_selectAll
} from 'd3-selection';

import { t, textDirection } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { actionChangeTags } from '../actions';
import { modeBrowse } from '../modes';
import { svgIcon } from '../svg';
import { uiPresetIcon } from './preset_icon';
import { uiRawMemberEditor } from './raw_member_editor';
import { uiRawMembershipEditor } from './raw_membership_editor';
import { uiRawTagEditor } from './raw_tag_editor';
import { uiTagReference } from './tag_reference';
import { uiPresetEditor } from './preset_editor';
import { utilCleanTags, utilRebind } from '../util';

export function uiEntityEditor(context) {
    var dispatch = d3_dispatch('choose');
    var _state = 'select';
    var _coalesceChanges = false;
    var _modified = false;
    var _base;
    var _entityID;
    var _activePreset;
    var _tagReference;

    var presetEditor = uiPresetEditor(context).on('change', changeTags);
    var rawTagEditor = uiRawTagEditor(context).on('change', changeTags);
    var rawMemberEditor = uiRawMemberEditor(context);
    var rawMembershipEditor = uiRawMembershipEditor(context);

    function entityEditor(selection) {
        var entity = context.entity(_entityID);
        var tags = _clone(entity.tags);

        // Header
        var header = selection.selectAll('.header')
            .data([0]);

        // Enter
        var enter = header.enter()
            .append('div')
            .attr('class', 'header fillL cf');

        enter
            .append('button')
            .attr('class', 'fl preset-reset preset-choose')
            .call(svgIcon((textDirection === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));

        enter
            .append('button')
            .attr('class', 'fr preset-close')
            .on('click', function() { context.enter(modeBrowse(context)); })
            .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));

        enter
            .append('h3')
            .text(t('inspector.edit'));

        // Update
        header = header
            .merge(enter);

        header.selectAll('.preset-reset')
            .on('click', function() {
                dispatch.call('choose', this, _activePreset);
            });

        // Body
        var body = selection.selectAll('.inspector-body')
            .data([0]);

        // Enter
        enter = body.enter()
            .append('div')
            .attr('class', 'inspector-body');

        enter
            .append('div')
            .attr('class', 'preset-list-item inspector-inner')
            .append('div')
            .attr('class', 'preset-list-button-wrap')
            .append('button')
            .attr('class', 'preset-list-button preset-reset')
            .call(tooltip().title(t('inspector.back_tooltip')).placement('bottom'))
            .append('div')
            .attr('class', 'label')
            .append('div')
            .attr('class', 'label-inner');

        enter
            .append('div')
            .attr('class', 'inspector-border preset-editor');

        enter
            .append('div')
            .attr('class', 'inspector-border raw-tag-editor inspector-inner');

        enter
            .append('div')
            .attr('class', 'inspector-border raw-member-editor inspector-inner');

        enter
            .append('div')
            .attr('class', 'raw-membership-editor inspector-inner');

        enter
            .append('input')
            .attr('type', 'text')
            .attr('class', 'key-trap');

        // Update
        body = body
            .merge(enter);

        if (_tagReference) {
            body.selectAll('.preset-list-button-wrap')
                .call(_tagReference.button);

            body.selectAll('.preset-list-item')
                .call(_tagReference.body);
        }

        body.selectAll('.preset-reset')
            .on('click', function() {
                dispatch.call('choose', this, _activePreset);
            });

        body.select('.preset-list-item button')
            .call(uiPresetIcon()
                .geometry(context.geometry(_entityID))
                .preset(_activePreset)
            );

        var label = body.select('.label-inner');
        var nameparts = label.selectAll('.namepart')
            .data(_activePreset.name().split(' - '), function(d) { return d; });

        nameparts.exit()
            .remove();

        nameparts
            .enter()
            .append('div')
            .attr('class', 'namepart')
            .text(function(d) { return d; });

        body.select('.preset-editor')
            .call(presetEditor
                .preset(_activePreset)
                .entityID(_entityID)
                .tags(tags)
                .state(_state)
            );

        body.select('.raw-tag-editor')
            .call(rawTagEditor
                .preset(_activePreset)
                .entityID(_entityID)
                .tags(tags)
                .state(_state)
            );

        if (entity.type === 'relation') {
            body.select('.raw-member-editor')
                .style('display', 'block')
                .call(rawMemberEditor
                    .entityID(_entityID)
                );
        } else {
            body.select('.raw-member-editor')
                .style('display', 'none');
        }

        body.select('.raw-membership-editor')
            .call(rawMembershipEditor
                .entityID(_entityID)
            );

        body.select('.key-trap')
            .on('keydown.key-trap', function() {
                // On tabbing, send focus back to the first field on the inspector-body
                // (probably the `name` field) #4159
                if (d3_event.keyCode === 9 && !d3_event.shiftKey) {
                    d3_event.preventDefault();
                    body.select('input').node().focus();
                }
            });

        context.history()
            .on('change.entity-editor', historyChanged);

        function historyChanged() {
            if (_state === 'hide') return;

            var entity = context.hasEntity(_entityID);
            var graph = context.graph();
            if (!entity) return;

            var match = context.presets().match(entity, graph);
            var activePreset = entityEditor.preset();
            var weakPreset = activePreset && _isEmpty(activePreset.addTags);

            // A "weak" preset doesn't set any tags. (e.g. "Address")
            // Don't replace a weak preset with a fallback preset (e.g. "Point")
            if (!(weakPreset && match.isFallback())) {
                match.visible(true);
                context.presets().choose(match);
                entityEditor.preset(match);
            }
            entityEditor.modified(_base !== graph);
            entityEditor(selection);
        }
    }

    // Tag changes that fire on input can all get coalesced into a single
    // history operation when the user leaves the field.  #2342
    function changeTags(changed, onInput) {
        var entity = context.entity(_entityID);
        var annotation = t('operations.change_tags.annotation');
        var tags = _clone(entity.tags);

        for (var k in changed) {
            if (!k) continue;
            var v = changed[k];
            if (v !== undefined || tags.hasOwnProperty(k)) {
                tags[k] = v;
            }
        }

        if (!onInput) {
            tags = utilCleanTags(tags);
        }

        if (!_isEqual(entity.tags, tags)) {
            if (_coalesceChanges) {
                context.overwrite(actionChangeTags(_entityID, tags), annotation);
            } else {
                context.perform(actionChangeTags(_entityID, tags), annotation);
                // context.presets().choose(context.presets().match(context.graph))
                _coalesceChanges = !!onInput;
            }
        }
    }

    entityEditor.modified = function(_) {
        if (!arguments.length) return _modified;
        _modified = _;
        d3_selectAll('button.preset-close use')
            .attr('xlink:href', (_modified ? '#iD-icon-apply' : '#iD-icon-close'));
        return entityEditor;
    };

    entityEditor.state = function(_) {
        if (!arguments.length) return _state;
        _state = _;
        return entityEditor;
    };

    entityEditor.entityID = function(_) {
        if (!arguments.length) return _entityID;
        _entityID = _;
        _base = context.graph();
        _coalesceChanges = false;

        // reset the scroll to the top of the inspector
        var body = d3_selectAll('.entity-editor-pane .inspector-body');
        if (!body.empty()) {
            body.node().scrollTop = 0;
        }

        var presetMatch = context.presets().match(context.entity(_entityID), _base);

        return entityEditor
            .preset(presetMatch)
            .modified(false);
    };

    entityEditor.preset = function(_) {
        if (!arguments.length) return _activePreset;
        if (_ !== _activePreset) {
            _activePreset = _;
            _tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context)
                .showing(false);
        }
        return entityEditor;
    };

    return utilRebind(entityEditor, dispatch, 'on');
}
<commit_msg>remove a comment<commit_after>import _clone from 'lodash-es/clone';
import _isEmpty from 'lodash-es/isEmpty';
import _isEqual from 'lodash-es/isEqual';

import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    selectAll as d3_selectAll
} from 'd3-selection';

import { t, textDirection } from '../util/locale';
import { tooltip } from '../util/tooltip';
import { actionChangeTags } from '../actions';
import { modeBrowse } from '../modes';
import { svgIcon } from '../svg';
import { uiPresetIcon } from './preset_icon';
import { uiRawMemberEditor } from './raw_member_editor';
import { uiRawMembershipEditor } from './raw_membership_editor';
import { uiRawTagEditor } from './raw_tag_editor';
import { uiTagReference } from './tag_reference';
import { uiPresetEditor } from './preset_editor';
import { utilCleanTags, utilRebind } from '../util';

export function uiEntityEditor(context) {
    var dispatch = d3_dispatch('choose');
    var _state = 'select';
    var _coalesceChanges = false;
    var _modified = false;
    var _base;
    var _entityID;
    var _activePreset;
    var _tagReference;

    var presetEditor = uiPresetEditor(context).on('change', changeTags);
    var rawTagEditor = uiRawTagEditor(context).on('change', changeTags);
    var rawMemberEditor = uiRawMemberEditor(context);
    var rawMembershipEditor = uiRawMembershipEditor(context);

    function entityEditor(selection) {
        var entity = context.entity(_entityID);
        var tags = _clone(entity.tags);

        // Header
        var header = selection.selectAll('.header')
            .data([0]);

        // Enter
        var enter = header.enter()
            .append('div')
            .attr('class', 'header fillL cf');

        enter
            .append('button')
            .attr('class', 'fl preset-reset preset-choose')
            .call(svgIcon((textDirection === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));

        enter
            .append('button')
            .attr('class', 'fr preset-close')
            .on('click', function() { context.enter(modeBrowse(context)); })
            .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));

        enter
            .append('h3')
            .text(t('inspector.edit'));

        // Update
        header = header
            .merge(enter);

        header.selectAll('.preset-reset')
            .on('click', function() {
                dispatch.call('choose', this, _activePreset);
            });

        // Body
        var body = selection.selectAll('.inspector-body')
            .data([0]);

        // Enter
        enter = body.enter()
            .append('div')
            .attr('class', 'inspector-body');

        enter
            .append('div')
            .attr('class', 'preset-list-item inspector-inner')
            .append('div')
            .attr('class', 'preset-list-button-wrap')
            .append('button')
            .attr('class', 'preset-list-button preset-reset')
            .call(tooltip().title(t('inspector.back_tooltip')).placement('bottom'))
            .append('div')
            .attr('class', 'label')
            .append('div')
            .attr('class', 'label-inner');

        enter
            .append('div')
            .attr('class', 'inspector-border preset-editor');

        enter
            .append('div')
            .attr('class', 'inspector-border raw-tag-editor inspector-inner');

        enter
            .append('div')
            .attr('class', 'inspector-border raw-member-editor inspector-inner');

        enter
            .append('div')
            .attr('class', 'raw-membership-editor inspector-inner');

        enter
            .append('input')
            .attr('type', 'text')
            .attr('class', 'key-trap');

        // Update
        body = body
            .merge(enter);

        if (_tagReference) {
            body.selectAll('.preset-list-button-wrap')
                .call(_tagReference.button);

            body.selectAll('.preset-list-item')
                .call(_tagReference.body);
        }

        body.selectAll('.preset-reset')
            .on('click', function() {
                dispatch.call('choose', this, _activePreset);
            });

        body.select('.preset-list-item button')
            .call(uiPresetIcon()
                .geometry(context.geometry(_entityID))
                .preset(_activePreset)
            );

        var label = body.select('.label-inner');
        var nameparts = label.selectAll('.namepart')
            .data(_activePreset.name().split(' - '), function(d) { return d; });

        nameparts.exit()
            .remove();

        nameparts
            .enter()
            .append('div')
            .attr('class', 'namepart')
            .text(function(d) { return d; });

        body.select('.preset-editor')
            .call(presetEditor
                .preset(_activePreset)
                .entityID(_entityID)
                .tags(tags)
                .state(_state)
            );

        body.select('.raw-tag-editor')
            .call(rawTagEditor
                .preset(_activePreset)
                .entityID(_entityID)
                .tags(tags)
                .state(_state)
            );

        if (entity.type === 'relation') {
            body.select('.raw-member-editor')
                .style('display', 'block')
                .call(rawMemberEditor
                    .entityID(_entityID)
                );
        } else {
            body.select('.raw-member-editor')
                .style('display', 'none');
        }

        body.select('.raw-membership-editor')
            .call(rawMembershipEditor
                .entityID(_entityID)
            );

        body.select('.key-trap')
            .on('keydown.key-trap', function() {
                // On tabbing, send focus back to the first field on the inspector-body
                // (probably the `name` field) #4159
                if (d3_event.keyCode === 9 && !d3_event.shiftKey) {
                    d3_event.preventDefault();
                    body.select('input').node().focus();
                }
            });

        context.history()
            .on('change.entity-editor', historyChanged);

        function historyChanged() {
            if (_state === 'hide') return;

            var entity = context.hasEntity(_entityID);
            var graph = context.graph();
            if (!entity) return;

            var match = context.presets().match(entity, graph);
            var activePreset = entityEditor.preset();
            var weakPreset = activePreset && _isEmpty(activePreset.addTags);

            // A "weak" preset doesn't set any tags. (e.g. "Address")
            // Don't replace a weak preset with a fallback preset (e.g. "Point")
            if (!(weakPreset && match.isFallback())) {
                match.visible(true);
                context.presets().choose(match);
                entityEditor.preset(match);
            }
            entityEditor.modified(_base !== graph);
            entityEditor(selection);
        }
    }

    // Tag changes that fire on input can all get coalesced into a single
    // history operation when the user leaves the field.  #2342
    function changeTags(changed, onInput) {
        var entity = context.entity(_entityID);
        var annotation = t('operations.change_tags.annotation');
        var tags = _clone(entity.tags);

        for (var k in changed) {
            if (!k) continue;
            var v = changed[k];
            if (v !== undefined || tags.hasOwnProperty(k)) {
                tags[k] = v;
            }
        }

        if (!onInput) {
            tags = utilCleanTags(tags);
        }

        if (!_isEqual(entity.tags, tags)) {
            if (_coalesceChanges) {
                context.overwrite(actionChangeTags(_entityID, tags), annotation);
            } else {
                context.perform(actionChangeTags(_entityID, tags), annotation);
                _coalesceChanges = !!onInput;
            }
        }
    }

    entityEditor.modified = function(_) {
        if (!arguments.length) return _modified;
        _modified = _;
        d3_selectAll('button.preset-close use')
            .attr('xlink:href', (_modified ? '#iD-icon-apply' : '#iD-icon-close'));
        return entityEditor;
    };

    entityEditor.state = function(_) {
        if (!arguments.length) return _state;
        _state = _;
        return entityEditor;
    };

    entityEditor.entityID = function(_) {
        if (!arguments.length) return _entityID;
        _entityID = _;
        _base = context.graph();
        _coalesceChanges = false;

        // reset the scroll to the top of the inspector
        var body = d3_selectAll('.entity-editor-pane .inspector-body');
        if (!body.empty()) {
            body.node().scrollTop = 0;
        }

        var presetMatch = context.presets().match(context.entity(_entityID), _base);

        return entityEditor
            .preset(presetMatch)
            .modified(false);
    };

    entityEditor.preset = function(_) {
        if (!arguments.length) return _activePreset;
        if (_ !== _activePreset) {
            _activePreset = _;
            _tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context)
                .showing(false);
        }
        return entityEditor;
    };

    return utilRebind(entityEditor, dispatch, 'on');
}
<|endoftext|>


<commit_before>import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    select as d3_select
} from 'd3-selection';

import { t } from '../util/locale';
import { modeBrowse } from '../modes/browse';
import { uiDisclosure } from './disclosure';
import { uiField } from './field';
import { uiFormFields } from './form_fields';
import { utilArrayUnion, utilRebind } from '../util';

export function uiPresetEditor(context) {
    var dispatch = d3_dispatch('change');
    var formFields = uiFormFields(context);
    var _state;
    var _fieldsArr;
    var _preset;
    var _tags;
    var _entityID;

    function presetEditor(selection) {
        selection.call(uiDisclosure(context, 'preset_fields', true)
            .title(t('inspector.all_fields'))
            .content(render)
        );
    }

    function render(selection) {
        if (!_fieldsArr) {
            var entity = context.entity(_entityID);
            var geometry = context.geometry(_entityID);
            var presets = context.presets();

            _fieldsArr = [];

            _preset.fields.forEach(function(field) {
                if (field.matchGeometry(geometry)) {
                    _fieldsArr.push(
                        uiField(context, field, entity)
                    );
                }
            });

            if (entity.isHighwayIntersection(context.graph()) && presets.field('restrictions')) {
                _fieldsArr.push(
                    uiField(context, presets.field('restrictions'), entity)
                );
            }

            var additionalFields = utilArrayUnion(_preset.moreFields, presets.universal());
            additionalFields.sort(function(field1, field2) {
                return field1.label() > field2.label();
            });

            additionalFields.forEach(function(field) {
                if (_preset.fields.indexOf(field) === -1) {
                    _fieldsArr.push(
                        uiField(context, field, entity, { show: false })
                    );
                }
            });

            _fieldsArr.forEach(function(field) {
                field
                    .on('change', function(t, onInput) {
                        dispatch.call('change', field, t, onInput);
                    });
            });
        }

        _fieldsArr.forEach(function(field) {
            field
                .state(_state)
                .tags(_tags);
        });

        selection
            .call(formFields
                .fieldsArr(_fieldsArr)
                .state(_state)
                .klass('inspector-inner fillL3')
            );

        selection.selectAll('.wrap-form-field input')
            .on('keydown', function() {
                // if user presses enter, and combobox is not active, accept edits..
                if (d3_event.keyCode === 13 && d3_select('.combobox').empty()) {
                    context.enter(modeBrowse(context));
                }
            });
    }

    presetEditor.preset = function(val) {
        if (!arguments.length) return _preset;
        if (_preset && _preset.id === val.id) return presetEditor;
        _preset = val;
        _fieldsArr = null;
        return presetEditor;
    };

    presetEditor.state = function(val) {
        if (!arguments.length) return _state;
        _state = val;
        return presetEditor;
    };

    presetEditor.tags = function(val) {
        if (!arguments.length) return _tags;
        _tags = val;
        // Don't reset _fieldsArr here.
        return presetEditor;
    };

    presetEditor.entityID = function(val) {
        if (!arguments.length) return _entityID;
        if (_entityID === val) return presetEditor;
        _entityID = val;
        _fieldsArr = null;
        return presetEditor;
    };

    return utilRebind(presetEditor, dispatch, 'on');
}
<commit_msg>Sort moreFields in the UI with locale-specific alphabetization (close #6937)<commit_after>import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    select as d3_select
} from 'd3-selection';

import { currentLocale, t } from '../util/locale';
import { modeBrowse } from '../modes/browse';
import { uiDisclosure } from './disclosure';
import { uiField } from './field';
import { uiFormFields } from './form_fields';
import { utilArrayUnion, utilRebind } from '../util';

export function uiPresetEditor(context) {
    var dispatch = d3_dispatch('change');
    var formFields = uiFormFields(context);
    var _state;
    var _fieldsArr;
    var _preset;
    var _tags;
    var _entityID;

    function presetEditor(selection) {
        selection.call(uiDisclosure(context, 'preset_fields', true)
            .title(t('inspector.all_fields'))
            .content(render)
        );
    }

    function render(selection) {
        if (!_fieldsArr) {
            var entity = context.entity(_entityID);
            var geometry = context.geometry(_entityID);
            var presets = context.presets();

            _fieldsArr = [];

            _preset.fields.forEach(function(field) {
                if (field.matchGeometry(geometry)) {
                    _fieldsArr.push(
                        uiField(context, field, entity)
                    );
                }
            });

            if (entity.isHighwayIntersection(context.graph()) && presets.field('restrictions')) {
                _fieldsArr.push(
                    uiField(context, presets.field('restrictions'), entity)
                );
            }

            var additionalFields = utilArrayUnion(_preset.moreFields, presets.universal());
            additionalFields.sort(function(field1, field2) {
                return field1.label().localeCompare(field2.label(), currentLocale, { sensitivity: 'base' });
            });

            additionalFields.forEach(function(field) {
                if (_preset.fields.indexOf(field) === -1) {
                    _fieldsArr.push(
                        uiField(context, field, entity, { show: false })
                    );
                }
            });

            _fieldsArr.forEach(function(field) {
                field
                    .on('change', function(t, onInput) {
                        dispatch.call('change', field, t, onInput);
                    });
            });
        }

        _fieldsArr.forEach(function(field) {
            field
                .state(_state)
                .tags(_tags);
        });

        selection
            .call(formFields
                .fieldsArr(_fieldsArr)
                .state(_state)
                .klass('inspector-inner fillL3')
            );

        selection.selectAll('.wrap-form-field input')
            .on('keydown', function() {
                // if user presses enter, and combobox is not active, accept edits..
                if (d3_event.keyCode === 13 && d3_select('.combobox').empty()) {
                    context.enter(modeBrowse(context));
                }
            });
    }

    presetEditor.preset = function(val) {
        if (!arguments.length) return _preset;
        if (_preset && _preset.id === val.id) return presetEditor;
        _preset = val;
        _fieldsArr = null;
        return presetEditor;
    };

    presetEditor.state = function(val) {
        if (!arguments.length) return _state;
        _state = val;
        return presetEditor;
    };

    presetEditor.tags = function(val) {
        if (!arguments.length) return _tags;
        _tags = val;
        // Don't reset _fieldsArr here.
        return presetEditor;
    };

    presetEditor.entityID = function(val) {
        if (!arguments.length) return _entityID;
        if (_entityID === val) return presetEditor;
        _entityID = val;
        _fieldsArr = null;
        return presetEditor;
    };

    return utilRebind(presetEditor, dispatch, 'on');
}
<|endoftext|>


<commit_before>const fs = require('fs');
const { app } = require('electron');
const path = require('path');

module.exports = class {
    constructor() {
        this.moduleBasePath = path.resolve(app.getAppPath(), __dirname, '..', '..', 'modules');
        this.allHardwareList = [];
        this.initialize();
    }

    /**
     * 파일을 읽어와 리스트에 작성한다.
     *
     */
    initialize() {
        // noinspection JSCheckFunctionSignatures
        try {
            fs.readdirSync(this.moduleBasePath)
                .filter((file) => !!file.match(/\.json$/))
                .map((file) => fs.readFileSync(path.join(this.moduleBasePath, file)))
                .map(JSON.parse)
                .filter((config) => config.platform &&
                    config.platform.indexOf(process.platform) > -1)
                .sort((left, right) => {
                    const lName = left.name.ko.trim();
                    const rName = right.name.ko.trim();

                    if (lName > rName) {
                        return 1;
                    } else if (lName < rName) {
                        return -1;
                    } else {
                        return 0;
                    }
                })
                .forEach((config) => this.allHardwareList.push(config));
        } catch (e) {
            console.error('error occurred while reading module json files');
        }
    }
};
<commit_msg>[fix] priorHardwareList 에 현재 없는 하드웨어가 존재하는 경우에 대한 방어코드 추가<commit_after>const fs = require('fs');
const { app } = require('electron');
const path = require('path');

module.exports = class {
    constructor() {
        this.moduleBasePath = path.resolve(app.getAppPath(), __dirname, '..', '..', 'modules');
        this.allHardwareList = [];
        this.initialize();
    }

    /**
     * 파일을 읽어와 리스트에 작성한다.
     *
     */
    initialize() {
        // noinspection JSCheckFunctionSignatures
        try {
            fs.readdirSync(this.moduleBasePath)
                .filter((file) => !!file.match(/\.json$/))
                .map((file) => fs.readFileSync(path.join(this.moduleBasePath, file)))
                .map(JSON.parse)
                .filter((config) => config.platform &&
                    config.platform.indexOf(process.platform) > -1)
                .sort((left, right) => {
                    const lName = left.name.ko.trim();
                    const rName = right.name.ko.trim();

                    if (lName > rName) {
                        return 1;
                    } else if (lName < rName) {
                        return -1;
                    } else {
                        return 0;
                    }
                })
                .forEach((config) => config && this.allHardwareList.push(config));
        } catch (e) {
            console.error('error occurred while reading module json files');
        }
    }
};
<|endoftext|>


<commit_before>// @flow

import { css } from 'styled-components'

export const contentBlockSpacing = css`
    padding-top: ${props => props.theme.pageEdgePadding};
    padding-bottom: 15vh;
    overflow-y: scroll;
`

export const materialShadow = `
    border: 1px solid;
    border-color: #e5e6e9 #dfe0e4 #d0d1d5;
`

export const card = `
    ${materialShadow}
    background-color: white;
    border-radius: $base-border-radius;
`

export const cardContent = `
    padding: 1em;
`

export const cardActions = css`
    border-top: ${props => props.theme.materialDivider};
    padding: 1em;
`

export const headingNeutral = `
    font-size: inherit;
    font-weight: inherit;
    margin-top: 0;
    margin-bottom: 0;
`

export const linkUndecorated = `
    text-decoration: none;
    color: inherit;
`

export const listInline = `
    display: inline-block;
    list-style: none;
    margin-top: 0;
    margin-bottom: 0;
    padding-left: 0;

    & > li {
        display: inline-block;
    }
`

export const listUnstyled = `
    margin: 0;
    padding: 0;
    list-style: none;
`

export const noSelect = `
    user-select: none;
    cursor: default;
`
<commit_msg>fix variable reference in theme/mixins<commit_after>// @flow

import { css } from 'styled-components'

export const contentBlockSpacing = css`
    padding-top: ${props => props.theme.pageEdgePadding};
    padding-bottom: 15vh;
    overflow-y: scroll;
`

export const materialShadow = `
    border: 1px solid;
    border-color: #e5e6e9 #dfe0e4 #d0d1d5;
`

export const card = css`
    ${materialShadow}
    background-color: white;
    border-radius: ${props => props.theme.baseBorderRadius};
`

export const cardContent = `
    padding: 1em;
`

export const cardActions = css`
    border-top: ${props => props.theme.materialDivider};
    padding: 1em;
`

export const headingNeutral = `
    font-size: inherit;
    font-weight: inherit;
    margin-top: 0;
    margin-bottom: 0;
`

export const linkUndecorated = `
    text-decoration: none;
    color: inherit;
`

export const listInline = `
    display: inline-block;
    list-style: none;
    margin-top: 0;
    margin-bottom: 0;
    padding-left: 0;

    & > li {
        display: inline-block;
    }
`

export const listUnstyled = `
    margin: 0;
    padding: 0;
    list-style: none;
`

export const noSelect = `
    user-select: none;
    cursor: default;
`
<|endoftext|>


<commit_before>var EXPORTED_SYMBOLS = ['GM_xmlhttpRequester'];

Components.utils.import("resource://greasemonkey/util.js");

function GM_xmlhttpRequester(wrappedContentWin, chromeWindow, originUrl) {
  this.wrappedContentWin = wrappedContentWin;
  this.chromeWindow = chromeWindow;
  this.originUrl = originUrl;
}

// this function gets called by user scripts in content security scope to
// start a cross-domain xmlhttp request.
//
// details should look like:
// {method,url,onload,onerror,onreadystatechange,headers,data}
// headers should be in the form {name:value,name:value,etc}
// can't support mimetype because i think it's only used for forcing
// text/xml and we can't support that
GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
  if (!GM_util.apiLeakCheck("GM_xmlhttpRequest", arguments)) {
    return;
  }

  try {
    // Validate and parse the (possibly relative) given URL.
    var uri = GM_util.uriFromUrl(details.url, this.originUrl);
    var url = uri.spec;
  } catch (e) {
    // A malformed URL won't be parsed properly.
    throw new Error(
        this.stringBundle.GetStringFromName('error.invalidUrl')
            .replace('%1', name)
        );
  }

  // This is important - without it, GM_xmlhttpRequest can be used to get
  // access to things like files and chrome. Careful.
  switch (uri.scheme) {
    case "http":
    case "https":
    case "ftp":
        var req = new this.chromeWindow.XMLHttpRequest();
        GM_util.hitch(this, "chromeStartRequest", url, details, req)();
      break;
    default:
      throw new Error(
          this.stringBundle.GetStringFromName('error.disallowedScheme')
              .replace('%1', details.url)
          );
  }

  var rv = {
    __exposedProps__: {
        finalUrl: "r",
        readyState: "r",
        responseHeaders: "r",
        responseText: "r",
        status: "r",
        statusText: "r",
        abort: "r"
        },
    abort: function () { return req.abort(); }
  };
  if (!!details.synchronous) {
    rv.finalUrl = req.finalUrl;
    rv.readyState = req.readyState;
    rv.responseHeaders = req.getAllResponseHeaders();
    rv.responseText = req.responseText;
    rv.status = req.status;
    rv.statusText = req.statusText;
  }
  return rv;
};

// this function is intended to be called in chrome's security context, so
// that it can access other domains without security warning
GM_xmlhttpRequester.prototype.chromeStartRequest =
function(safeUrl, details, req) {
  this.setupReferer(details, req);

  var setupRequestEvent = GM_util.hitch(
      this, 'setupRequestEvent', this.wrappedContentWin);

  setupRequestEvent(req, "abort", details);
  setupRequestEvent(req, "error", details);
  setupRequestEvent(req, "load", details);
  setupRequestEvent(req, "progress", details);
  setupRequestEvent(req, "readystatechange", details);
  setupRequestEvent(req, "timeout", details);
  if (details.upload) {
    setupRequestEvent(req.upload, "abort", details.upload);
    setupRequestEvent(req.upload, "error", details.upload);
    setupRequestEvent(req.upload, "load", details.upload);
    setupRequestEvent(req.upload, "progress", details.upload);
  }

  req.mozBackgroundRequest = !!details.mozBackgroundRequest;

  req.open(details.method, safeUrl,
      !details.synchronous, details.user || "", details.password || "");

  if (details.overrideMimeType) {
    req.overrideMimeType(details.overrideMimeType);
  }

  if (details.timeout) {
    req.timeout = details.timeout;
  }

  if ('redirectionLimit' in details) {
    try {
      var httpChannel = req.channel.QueryInterface(
          Components.interfaces.nsIHttpChannel);
      httpChannel.redirectionLimit = details.redirectionLimit;
    } catch (e) {
      // Ignore.
    }
  }

  if (details.headers) {
    var headers = details.headers;

    for (var prop in headers) {
      if (Object.prototype.hasOwnProperty.call(headers, prop)) {
        req.setRequestHeader(prop, headers[prop]);
      }
    }
  }

  var body = details.data ? details.data : null;
  if (details.binary) {
    req.sendAsBinary(body);
  } else {
    req.send(body);
  }
};

// sets the "Referer" HTTP header for this GM_XHR request.
// Firefox does not let chrome JS set the "Referer" HTTP heade via XHR
// directly. However, we can still set it indirectly via an
// http-on-modify-request observer.
GM_xmlhttpRequester.prototype.setupReferer =
function(details, req) {
  if (!details.headers || !details.headers.Referer) return;

  var observerService = Components.classes["@mozilla.org/observer-service;1"]
      .getService(Components.interfaces.nsIObserverService);
  var requestObserver = {};
  requestObserver.observe = function(subject, topic, data) {
      observerService.removeObserver(requestObserver, "http-on-modify-request");

      var channel = subject.QueryInterface(Components.interfaces.nsIChannel);
      if (channel == req.channel) {
        var httpChannel = subject.QueryInterface(
            Components.interfaces.nsIHttpChannel);
        httpChannel.setRequestHeader("Referer", details.headers.Referer, false);
      }
    };
  observerService.addObserver(requestObserver, "http-on-modify-request", false);
};

// arranges for the specified 'event' on xmlhttprequest 'req' to call the
// method by the same name which is a property of 'details' in the content
// window's security context.
GM_xmlhttpRequester.prototype.setupRequestEvent =
function(wrappedContentWin, req, event, details) {
  if (!details["on" + event]) return;

  req.addEventListener(event, function(evt) {
    var responseState = {
      __exposedProps__: {
          context: "r",
          finalUrl: "r",
          lengthComputable: "r",
          loaded: "r",
          readyState: "r",
          responseHeaders: "r",
          responseText: "r",
          status: "r",
          statusText: "r",
          total: "r",
          },
      context: details.context || null,
      // Can't support responseXML because security won't
      // let the browser call properties on it.
      responseText: req.responseText,
      readyState: req.readyState,
      responseHeaders: null,
      status: null,
      statusText: null,
      finalUrl: null
    };

    switch (event) {
      case "progress":
        responseState.lengthComputable = evt.lengthComputable;
        responseState.loaded = evt.loaded;
        responseState.total = evt.total;
        break;
      case "error":
        break;
      default:
        if (4 != req.readyState) break;
        responseState.responseHeaders = req.getAllResponseHeaders();
        responseState.status = req.status;
        responseState.statusText = req.statusText;
        responseState.finalUrl = req.channel.URI.spec;
        break;
    }

    if (GM_util.windowIsClosed(wrappedContentWin)) {
      return;
    }

    // Pop back onto browser thread and call event handler.
    // Have to use nested function here instead of GM_util.hitch because
    // otherwise details[event].apply can point to window.setTimeout, which
    // can be abused to get increased privileges.
    new XPCNativeWrapper(wrappedContentWin, "setTimeout()")
      .setTimeout(function(){ details["on" + event](responseState); }, 0);
  }, false);
};
<commit_msg>Add `responseType` GM_xmlhttpRequest option.<commit_after>var EXPORTED_SYMBOLS = ['GM_xmlhttpRequester'];

Components.utils.import("resource://greasemonkey/util.js");

function GM_xmlhttpRequester(wrappedContentWin, chromeWindow, originUrl) {
  this.wrappedContentWin = wrappedContentWin;
  this.chromeWindow = chromeWindow;
  this.originUrl = originUrl;
}

// this function gets called by user scripts in content security scope to
// start a cross-domain xmlhttp request.
//
// details should look like:
// {method,url,onload,onerror,onreadystatechange,headers,data}
// headers should be in the form {name:value,name:value,etc}
// can't support mimetype because i think it's only used for forcing
// text/xml and we can't support that
GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
  if (!GM_util.apiLeakCheck("GM_xmlhttpRequest", arguments)) {
    return;
  }

  try {
    // Validate and parse the (possibly relative) given URL.
    var uri = GM_util.uriFromUrl(details.url, this.originUrl);
    var url = uri.spec;
  } catch (e) {
    // A malformed URL won't be parsed properly.
    throw new Error(
        this.stringBundle.GetStringFromName('error.invalidUrl')
            .replace('%1', name)
        );
  }

  // This is important - without it, GM_xmlhttpRequest can be used to get
  // access to things like files and chrome. Careful.
  switch (uri.scheme) {
    case "http":
    case "https":
    case "ftp":
        var req = new this.chromeWindow.XMLHttpRequest();
        GM_util.hitch(this, "chromeStartRequest", url, details, req)();
      break;
    default:
      throw new Error(
          this.stringBundle.GetStringFromName('error.disallowedScheme')
              .replace('%1', details.url)
          );
  }

  var rv = {
    __exposedProps__: {
        finalUrl: "r",
        readyState: "r",
        responseHeaders: "r",
        responseText: "r",
        status: "r",
        statusText: "r",
        abort: "r"
        },
    abort: function () { return req.abort(); }
  };
  if (!!details.synchronous) {
    rv.finalUrl = req.finalUrl;
    rv.readyState = req.readyState;
    rv.responseHeaders = req.getAllResponseHeaders();
    rv.responseText = req.responseText;
    rv.status = req.status;
    rv.statusText = req.statusText;
  }
  return rv;
};

// this function is intended to be called in chrome's security context, so
// that it can access other domains without security warning
GM_xmlhttpRequester.prototype.chromeStartRequest =
function(safeUrl, details, req) {
  this.setupReferer(details, req);

  var setupRequestEvent = GM_util.hitch(
      this, 'setupRequestEvent', this.wrappedContentWin);

  setupRequestEvent(req, "abort", details);
  setupRequestEvent(req, "error", details);
  setupRequestEvent(req, "load", details);
  setupRequestEvent(req, "progress", details);
  setupRequestEvent(req, "readystatechange", details);
  setupRequestEvent(req, "timeout", details);
  if (details.upload) {
    setupRequestEvent(req.upload, "abort", details.upload);
    setupRequestEvent(req.upload, "error", details.upload);
    setupRequestEvent(req.upload, "load", details.upload);
    setupRequestEvent(req.upload, "progress", details.upload);
  }

  req.mozBackgroundRequest = !!details.mozBackgroundRequest;

  req.open(details.method, safeUrl,
      !details.synchronous, details.user || "", details.password || "");

  if (details.overrideMimeType) {
    req.overrideMimeType(details.overrideMimeType);
  }
  if (details.responseType) {
    req.responseType = details.responseType;
  }
  if (details.timeout) {
    req.timeout = details.timeout;
  }

  if ('redirectionLimit' in details) {
    try {
      var httpChannel = req.channel.QueryInterface(
          Components.interfaces.nsIHttpChannel);
      httpChannel.redirectionLimit = details.redirectionLimit;
    } catch (e) {
      // Ignore.
    }
  }

  if (details.headers) {
    var headers = details.headers;

    for (var prop in headers) {
      if (Object.prototype.hasOwnProperty.call(headers, prop)) {
        req.setRequestHeader(prop, headers[prop]);
      }
    }
  }

  var body = details.data ? details.data : null;
  if (details.binary) {
    req.sendAsBinary(body);
  } else {
    req.send(body);
  }
};

// sets the "Referer" HTTP header for this GM_XHR request.
// Firefox does not let chrome JS set the "Referer" HTTP heade via XHR
// directly. However, we can still set it indirectly via an
// http-on-modify-request observer.
GM_xmlhttpRequester.prototype.setupReferer =
function(details, req) {
  if (!details.headers || !details.headers.Referer) return;

  var observerService = Components.classes["@mozilla.org/observer-service;1"]
      .getService(Components.interfaces.nsIObserverService);
  var requestObserver = {};
  requestObserver.observe = function(subject, topic, data) {
      observerService.removeObserver(requestObserver, "http-on-modify-request");

      var channel = subject.QueryInterface(Components.interfaces.nsIChannel);
      if (channel == req.channel) {
        var httpChannel = subject.QueryInterface(
            Components.interfaces.nsIHttpChannel);
        httpChannel.setRequestHeader("Referer", details.headers.Referer, false);
      }
    };
  observerService.addObserver(requestObserver, "http-on-modify-request", false);
};

// arranges for the specified 'event' on xmlhttprequest 'req' to call the
// method by the same name which is a property of 'details' in the content
// window's security context.
GM_xmlhttpRequester.prototype.setupRequestEvent =
function(wrappedContentWin, req, event, details) {
  if (!details["on" + event]) return;

  req.addEventListener(event, function(evt) {
    var responseState = {
      __exposedProps__: {
          context: "r",
          finalUrl: "r",
          lengthComputable: "r",
          loaded: "r",
          readyState: "r",
          response: "r",
          responseHeaders: "r",
          responseText: "r",
          status: "r",
          statusText: "r",
          total: "r",
          },
      context: details.context || null,
      // Can't support responseXML because security won't
      // let the browser call properties on it.
      readyState: req.readyState,
      response: req.response,
      responseHeaders: null,
      responseText: null,
      status: null,
      statusText: null,
      finalUrl: null
    };

    try {
      responseState.responseText = req.responseText;
    } catch (e) {
      // Some response types don't have .responseText (but do have e.g. blob
      // .response).  Ignore.
    }

    switch (event) {
      case "progress":
        responseState.lengthComputable = evt.lengthComputable;
        responseState.loaded = evt.loaded;
        responseState.total = evt.total;
        break;
      case "error":
        break;
      default:
        if (4 != req.readyState) break;
        responseState.responseHeaders = req.getAllResponseHeaders();
        responseState.status = req.status;
        responseState.statusText = req.statusText;
        responseState.finalUrl = req.channel.URI.spec;
        break;
    }

    if (GM_util.windowIsClosed(wrappedContentWin)) {
      return;
    }

    // Pop back onto browser thread and call event handler.
    // Have to use nested function here instead of GM_util.hitch because
    // otherwise details[event].apply can point to window.setTimeout, which
    // can be abused to get increased privileges.
    new XPCNativeWrapper(wrappedContentWin, "setTimeout()")
      .setTimeout(function(){ details["on" + event](responseState); }, 0);
  }, false);
};
<|endoftext|>


<commit_before>var intervalId = -1;

$(document).ready(function() {
	// From https://gist.github.com/alanhamlett/6316427
	$.ajaxSetup({
		beforeSend: function(xhr, settings) {
			if (settings.type == 'POST' || settings.type == 'PUT' || settings.type == 'DELETE' || settings.type == 'PATCH') {
				xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
			}
		}
	});

	function updateTimers() {
		$.ajax({
			url: '/status/',
			method: 'GET',
			data: {
				'key': webKey,
			},
			contentType: 'application/json',
			success: function(data) {
				data = JSON.parse(data);

				for (i in data) {
					var goal = data[i];
					var goalElement = $(".goal[data-slug=" + goal.slug + "]");

					if (goalElement.hasClass("stop")) {
						goalElement.find(".timer .num").html(goal.current_elapsed);
						goalElement.find(".timer .seconds").html(goal.current_elapsed_in_seconds);
						goalElement.find(".info .current").html(goal.current_amount);
					}
				}
			},
			error: function(data) {
				console.log("error", data);
			},
		});
	}

	function checkTimers() {
		if (intervalId == -1) {
			intervalId = setInterval(updateTimers, 1000);
		}
	}

	if ($(".goal .button.stop").length > 0) {
		checkTimers();
	}

	function toggleButtonClass(button, data) {
		if (button.hasClass("start")) {
			// Turn into a Stop button
			button.addClass("stop").removeClass("start");
			button.html("Stop");

			// Show the timer
			button.siblings(".timer").removeClass("hidden");

			checkTimers();
		} else if (button.hasClass("stop")) {
			// Turn into a Start button
			button.addClass("start").removeClass("stop");
			button.html("Start");

			// Hide the timer
			button.siblings(".timer").addClass("hidden");

			// Stop pinging the status web service if there aren't any more active timers
			if ($(".goal .button.stop").length == 0) {
				clearInterval(intervalId);
				intervalId = -1;
			}
		}
	}

	// Start/stop buttons
	$(".button").on("click touchstart", function() {
		// Get the slug of the goal we want
		var goalSlug = $(this).parents(".goal").attr("data-slug");
		var button = $(this);

		if (goalSlug != '') {
			// Send in the timer request
			// It's GET so we can use it in Launch Center Pro, Drafts, etc.
			$.ajax({
				url: '/' + goalSlug + '/timer/',
				method: 'GET',
				data: {
					'key': webKey,
				},
				contentType: 'application/json',
				success: function(data) {
					data = JSON.parse(data);
					toggleButtonClass(button, data);
				},
				error: function(data) {
					console.log("error", data);
				},
			});
		}

		return false;
	});
});
<commit_msg>Fixed bug in bugfix<commit_after>var intervalId = -1;

$(document).ready(function() {
	// From https://gist.github.com/alanhamlett/6316427
	$.ajaxSetup({
		beforeSend: function(xhr, settings) {
			if (settings.type == 'POST' || settings.type == 'PUT' || settings.type == 'DELETE' || settings.type == 'PATCH') {
				xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
			}
		}
	});

	function updateTimers() {
		$.ajax({
			url: '/status/',
			method: 'GET',
			data: {
				'key': webKey,
			},
			contentType: 'application/json',
			success: function(data) {
				data = JSON.parse(data);

				for (i in data) {
					var goal = data[i];
					var goalElement = $(".goal[data-slug=" + goal.slug + "]");

					if (goalElement.find(".button.stop")) {
						goalElement.find(".timer .num").html(goal.current_elapsed);
						goalElement.find(".timer .seconds").html(goal.current_elapsed_in_seconds);
						goalElement.find(".info .current").html(goal.current_amount);
					}
				}
			},
			error: function(data) {
				console.log("error", data);
			},
		});
	}

	function checkTimers() {
		if (intervalId == -1) {
			intervalId = setInterval(updateTimers, 1000);
		}
	}

	if ($(".goal .button.stop").length > 0) {
		checkTimers();
	}

	function toggleButtonClass(button, data) {
		if (button.hasClass("start")) {
			// Turn into a Stop button
			button.addClass("stop").removeClass("start");
			button.html("Stop");

			// Show the timer
			button.siblings(".timer").removeClass("hidden");

			checkTimers();
		} else if (button.hasClass("stop")) {
			// Turn into a Start button
			button.addClass("start").removeClass("stop");
			button.html("Start");

			// Hide the timer
			button.siblings(".timer").addClass("hidden");

			// Stop pinging the status web service if there aren't any more active timers
			if ($(".goal .button.stop").length == 0) {
				clearInterval(intervalId);
				intervalId = -1;
			}
		}
	}

	// Start/stop buttons
	$(".button").on("click touchstart", function() {
		// Get the slug of the goal we want
		var goalSlug = $(this).parents(".goal").attr("data-slug");
		var button = $(this);

		if (goalSlug != '') {
			// Send in the timer request
			// It's GET so we can use it in Launch Center Pro, Drafts, etc.
			$.ajax({
				url: '/' + goalSlug + '/timer/',
				method: 'GET',
				data: {
					'key': webKey,
				},
				contentType: 'application/json',
				success: function(data) {
					data = JSON.parse(data);
					toggleButtonClass(button, data);
				},
				error: function(data) {
					console.log("error", data);
				},
			});
		}

		return false;
	});
});
<|endoftext|>