<commit_before>'use strict';

angular.module('mcp.search.services', [])

    .controller('SearchServiceMapController', ['$scope', '$filter', 'mapService', 'leafletData', 'ServiceInstanceService', 'searchServiceFilterModel',
      function ($scope, $filter, mapService, leafletData, ServiceInstanceService, searchServiceFilterModel) {

        var SEARCHMAP_ID = 'searchmap';

        $scope.allServices = ServiceInstanceService.query();
        $scope.filter = searchServiceFilterModel.filter;
        $scope.data = searchServiceFilterModel.data;

        angular.extend($scope, {
          element: {},
          filterLocation: {
            lat: $scope.filter.location ? $scope.filter.location.lat : 51,
            lng: $scope.filter.location ? $scope.filter.location.lng : 0,
            draggable: false,
            message: "",
            focus: false
          },
          mouseLocation: {lat: 0, lng: 0},
          map: {
            defaults: {
              scrollWheelZoom: true,
              zoomControl: true,
              attributionControl: true,
              zoomAnimation: true
            },
            paths: {} //mapService.shapesToPaths($scope.service.coverage)
          },
          events: {
            map: {
              enable: ['click', 'mousemove'],
              logic: 'emit'
            }
          },
          markers: {
          },
          services: $scope.allServices,
          selectedService: null,
          highlightedService: null,
          servicesLayer: L.featureGroup(),
          servicesLayerMap: {}
        });

        function featureGroupCallback(featureGroup) {

          // (called whenever servicesToLayers creates a layer)
          featureGroup.on('click', clickEventHandler);
          featureGroup.on('mousemove', mouseMoveEventHandler);
          featureGroup.on('mouseover', function (e) {
            $scope.highlightService(e.target.service);
          });
          featureGroup.on('mouseout', function (e) {
            $scope.unhighlightService(e.target.service);
          });
          $scope.servicesLayerMap[featureGroup.service.id] = featureGroup;
        }

        $scope.clearFilterlocation = function () {
          delete $scope.markers.filterLocation;

          // (this change will trigger filter watch!)
          delete $scope.filter.location;

          // reset to show all services
          $scope.selectedService = null;
        };

        $scope.moveFilterLocation = function (latlng) {
          if (!$scope.markers.filterLocation)
            $scope.markers.filterLocation = $scope.filterLocation;
          $scope.filterLocation.lat = latlng.lat;
          $scope.filterLocation.lng = latlng.lng;

          // Clear any previously selected service
          $scope.selectedService = null;

          // (this change will trigger filter watch!)
          $scope.filter.location = latlng;
        };

        function filterServices(filterBy) {

          //FIXME: should delegate to server instead (...or at least in advance)

          var allServices = $scope.allServices;
          var services = [];

          // filter by predefined criterias (see match function)
          allServices.forEach(function (service) {
            if (match(service, filterBy)) {
              services.push(service);
            }
          });

          if (filterBy.anyText) {
            var searchFilter = {$: filterBy.anyText};
            services = $filter('filter')(services, searchFilter, false)
          }

          // filter services to those that contains the filterLocation
          if (filterBy.location)
            services = mapService.filterServicesAtLocation(filterBy.location, services);

          return services;
        }

        function match(service, filter) {

          if (filter.provider && service.provider.name !== filter.provider.name)
            return false;

          if (filter.operationalService && service.specification.operationalService.id !== filter.operationalService.id)
            return false;

          if (filter.technicalSpecification && service.specification.id !== filter.technicalSpecification.id)
            return false;

          if (filter.transportType && service.specification.transportType !== filter.transportType)
            return false;

          return true;
        }

        function filterChanged() {
          updateLocationMarker();
          filterAndShowServices();
        }

        function updateLocationMarker() {
          if (!$scope.filter.location)
            $scope.clearFilterlocation();
        }

        function filterAndShowServices() {
          $scope.services = filterServices($scope.filter);

          // share the result with the service filter
          $scope.data.result = $scope.services;

          // update marker info
          $scope.filterLocation.message = "" + $scope.services.length + " services near this location";

          showServices($scope.services);

          // Autoselect single service
          if ($scope.services.length === 1) {
            $scope.unselectService();
            if ($scope.filter.location)
              $scope.selectService($scope.services[0]);
          }

          // Clear selected service if not in list
          if ($scope.selectedService) {
            if ($scope.services.indexOf($scope.selectedService) === -1) {
              $scope.unselectService();
            } else {
              console.log($scope.filter.location);
              // when location marker is placed we should refresh the selection of the service layer
              var service = $scope.selectedService;
              $scope.servicesLayerMap[service.id].select();
            }
          }

        }

        function showServices(servicesAtLocation) {
          // Cleanup
          $scope.servicesLayerMap = {};
          $scope.servicesLayer.clearLayers();
          // Rebuild
          $scope.servicesLayer.addLayer(L.featureGroup(mapService.servicesToLayers(servicesAtLocation, featureGroupCallback)));
          fitToSelectedLayers();
        }

        function clickEventHandler(e) {
          //console.log('Shape clicked: ', e.latlng);
          $scope.moveFilterLocation(e.latlng);
          $scope.$apply();
        }

        $scope.$on('leafletDirectiveMap.click', function (event, args) {
          //console.log("Map clicked: ", event, args);
          $scope.moveFilterLocation(args.leafletEvent.latlng);
        });

        function mouseMoveEventHandler(e) {
          // update mouse location
          $scope.mouseLocation = e.latlng;
          $scope.$apply();

          // show distance in meters to filterMarker
          $scope.distance = e.latlng.distanceTo($scope.filterLocation);
        }

        function fitToSelectedLayers() {
          if ($scope.services.length)
            fitToLayer($scope.servicesLayer);
        }

        function fitToLayer(layer) {
          leafletData.getMap(SEARCHMAP_ID).then(function (map) {
            if (layer) {
              //console.log('fit map to services', layer.getBounds());
              map.fitBounds(layer.getBounds(), {paddingBottomRight: [$scope.selectedService ? $scope.element.offsetWidth : 0, 0]});
            }
          });
        }

        $scope.toggleSelectService = function (service) {
          if (service === $scope.selectedService) {
            $scope.unselectService();
          } else {
            $scope.selectService(service);
          }
        };

        $scope.unselectService = function () {
          if ($scope.selectedService) {

            // unselect prevous service layer
            if ($scope.servicesLayerMap[$scope.selectedService.id])
              $scope.servicesLayerMap[$scope.selectedService.id].unselect();

            $scope.selectedService = null;
            fitToSelectedLayers();
          }
        };

        $scope.selectService = function (service) {
          // unselect prevous service
          if ($scope.selectedService && $scope.servicesLayerMap[$scope.selectedService.id])
            $scope.servicesLayerMap[$scope.selectedService.id].unselect();

          if (service !== $scope.selectedService) {
            $scope.selectedService = service;
            $scope.servicesLayerMap[service.id].select();
            fitToLayer($scope.servicesLayerMap[service.id]);
          }
        };

        $scope.highlightService = function (service) {
          $scope.highlightedService = service;
          $scope.servicesLayerMap[service.id].highlight();
        };

        $scope.unhighlightService = function (service) {
          $scope.highlightedService = null;
          $scope.servicesLayerMap[service.id].unhighlight();
        };

        if ($scope.filter.location) {
           $scope.moveFilterLocation($scope.filter.location);
        }

        filterAndShowServices();

        $scope.$watch('filter', function (newValue, oldValue) {
          filterChanged();
        }, true);

        leafletData.getMap(SEARCHMAP_ID).then(function (map) {
          map.addLayer($scope.servicesLayer);
          map.on('mousemove', mouseMoveEventHandler);
        });

      }])

    // Search Filter Object
    // that holds the various filters supplied by controls in eg. the sidebar and used to filter services
    .service('searchServiceFilterModel', function (OperationalServiceService, SpecificationService, OrganizationService) {

      this.data = {
        operationalServices: OperationalServiceService.query(),
        technicalSpecifications: null,
        organizations: OrganizationService.query(),
        transportTypes: {
          mms: 'MMS',
          rest: 'REST',
          soap: 'SOAP',
          www: 'WWW',
          tcp: 'TCP',
          udp: 'UDP',
          aisasm: 'AISASM',
          tel: 'TEL',
          vhf: 'VHF',
          dgnss: 'DGNSS',
          other: 'OTHER'
        },
        result: []
      };

      this.filter = {
        anyText: null,
        location: null,
        operationalService: null,
        provider: null,
        technicalSpecification: null,
        transportType: null
      };

      this.clean = function () {
        delete this.filter.anyText;
        delete this.filter.location;
        delete this.filter.operationalService;
        delete this.filter.provider;
        delete this.filter.technicalSpecification;
        delete this.filter.transportType;
      };

    });

;
<commit_msg>ECLOUD-109: Fixed part II of issue with service instance id not being globally unique, must use organization name as part of key - Also, refactored code<commit_after>'use strict';

angular.module('mcp.search.services', [])

    .controller('SearchServiceMapController', ['$scope', '$filter', 'mapService', 'leafletData', 'ServiceInstanceService', 'searchServiceFilterModel',
      function ($scope, $filter, mapService, leafletData, ServiceInstanceService, searchServiceFilterModel) {

        var SEARCHMAP_ID = 'searchmap';

        $scope.allServices = ServiceInstanceService.query();
        $scope.filter = searchServiceFilterModel.filter;
        $scope.data = searchServiceFilterModel.data;

        angular.extend($scope, {
          element: {},
          filterLocation: {
            lat: $scope.filter.location ? $scope.filter.location.lat : 51,
            lng: $scope.filter.location ? $scope.filter.location.lng : 0,
            draggable: false,
            message: "",
            focus: false
          },
          mouseLocation: {lat: 0, lng: 0},
          map: {
            defaults: {
              scrollWheelZoom: true,
              zoomControl: true,
              attributionControl: true,
              zoomAnimation: true
            },
            paths: {} //mapService.shapesToPaths($scope.service.coverage)
          },
          events: {
            map: {
              enable: ['click', 'mousemove'],
              logic: 'emit'
            }
          },
          markers: {
          },
          services: $scope.allServices,
          selectedService: null,
          highlightedService: null,
          servicesLayer: L.featureGroup(),
          servicesLayerMap: {}
        });

        $scope.clearFilterlocation = function () {
          delete $scope.markers.filterLocation;

          // (this change will trigger filter watch!)
          delete $scope.filter.location;

          // reset to show all services
          $scope.selectedService = null;
        };

        $scope.moveFilterLocation = function (latlng) {
          if (!$scope.markers.filterLocation)
            $scope.markers.filterLocation = $scope.filterLocation;
          $scope.filterLocation.lat = latlng.lat;
          $scope.filterLocation.lng = latlng.lng;

          // Clear any previously selected service
          $scope.selectedService = null;

          // (this change will trigger filter watch!)
          $scope.filter.location = latlng;
        };

        function filterServices(filterBy) {

          //FIXME: should delegate to server instead (...or at least in advance)

          var allServices = $scope.allServices;
          var services = [];

          // filter by predefined criterias (see match function)
          allServices.forEach(function (service) {
            if (match(service, filterBy)) {
              services.push(service);
            }
          });

          if (filterBy.anyText) {
            var searchFilter = {$: filterBy.anyText};
            services = $filter('filter')(services, searchFilter, false)
          }

          // filter services to those that contains the filterLocation
          if (filterBy.location)
            services = mapService.filterServicesAtLocation(filterBy.location, services);

          return services;
        }

        function match(service, filter) {

          if (filter.provider && service.provider.name !== filter.provider.name)
            return false;

          if (filter.operationalService && service.specification.operationalService.id !== filter.operationalService.id)
            return false;

          if (filter.technicalSpecification && service.specification.id !== filter.technicalSpecification.id)
            return false;

          if (filter.transportType && service.specification.transportType !== filter.transportType)
            return false;

          return true;
        }

        function filterChanged() {
          updateLocationMarker();
          filterAndShowServices();
        }

        function updateLocationMarker() {
          if (!$scope.filter.location)
            $scope.clearFilterlocation();
        }

        function filterAndShowServices() {
          $scope.services = filterServices($scope.filter);

          // share the result with the service filter
          $scope.data.result = $scope.services;

          // update marker info
          $scope.filterLocation.message = "" + $scope.services.length + " services near this location";

          showServices($scope.services);

          // Autoselect single service
          if ($scope.services.length === 1) {
            $scope.unselectService();
            if ($scope.filter.location)
              $scope.selectService($scope.services[0]);
          }

          // Clear selected service if not in list
          if ($scope.selectedService) {
            if ($scope.services.indexOf($scope.selectedService) === -1) {
              $scope.unselectService();
            } else {
              console.log($scope.filter.location);
              // when location marker is placed we should refresh the selection of the service layer
              var service = $scope.selectedService;
              serviceLayer(service).select();
            }
          }

        }

        function showServices(servicesAtLocation) {
          // Cleanup
          $scope.servicesLayerMap = {};
          $scope.servicesLayer.clearLayers();
          // Rebuild
          $scope.servicesLayer.addLayer(L.featureGroup(mapService.servicesToLayers(servicesAtLocation, featureGroupCallback)));
          fitToSelectedLayers();
        }

        function featureGroupCallback(featureGroup) {

          // (called whenever servicesToLayers creates a layer)
          featureGroup.on('click', clickEventHandler);
          featureGroup.on('mousemove', mouseMoveEventHandler);
          featureGroup.on('mouseover', function (e) {
            $scope.highlightService(e.target.service);
          });
          featureGroup.on('mouseout', function (e) {
            $scope.unhighlightService(e.target.service);
          });
          $scope.servicesLayerMap[uniqueId(featureGroup.service)] = featureGroup;
        }

        function serviceLayer(service) {
          return $scope.servicesLayerMap[uniqueId(service)];
        }

        function uniqueId(service) {
          return service.key.providerId+'-'+service.id;
        }

        function clickEventHandler(e) {
          //console.log('Shape clicked: ', e.latlng);
          $scope.moveFilterLocation(e.latlng);
          $scope.$apply();
        }

        $scope.$on('leafletDirectiveMap.click', function (event, args) {
          //console.log("Map clicked: ", event, args);
          $scope.moveFilterLocation(args.leafletEvent.latlng);
        });

        function mouseMoveEventHandler(e) {
          // update mouse location
          $scope.mouseLocation = e.latlng;
          $scope.$apply();

          // show distance in meters to filterMarker
          $scope.distance = e.latlng.distanceTo($scope.filterLocation);
        }

        function fitToSelectedLayers() {
          if ($scope.services.length)
            fitToLayer($scope.servicesLayer);
        }

        function fitToLayer(layer) {
          leafletData.getMap(SEARCHMAP_ID).then(function (map) {
            if (layer) {
              //console.log('fit map to services', layer.getBounds());
              map.fitBounds(layer.getBounds(), {paddingBottomRight: [$scope.selectedService ? $scope.element.offsetWidth : 0, 0]});
            }
          });
        }

        $scope.toggleSelectService = function (service) {
          if (service === $scope.selectedService) {
            $scope.unselectService();
          } else {
            $scope.selectService(service);
          }
        };

        $scope.unselectService = function () {
          if ($scope.selectedService) {

            // unselect prevous service layer
            if (serviceLayer($scope.selectedService))
              serviceLayer($scope.selectedService).unselect();

            $scope.selectedService = null;
            fitToSelectedLayers();
          }
        };

        $scope.selectService = function (service) {
          // unselect prevous service
          if ($scope.selectedService && serviceLayer($scope.selectedService))
            serviceLayer($scope.selectedService).unselect();

          if (service !== $scope.selectedService) {
            $scope.selectedService = service;
            serviceLayer(service).select();
            fitToLayer(serviceLayer(service));
            console.log("Selected service", service);
            console.log("Selected service", JSON.stringify(service,null,"    "));
          }
        };

        $scope.highlightService = function (service) {
          $scope.highlightedService = service;
          serviceLayer(service).highlight();
        };

        $scope.unhighlightService = function (service) {
          $scope.highlightedService = null;
          serviceLayer(service).unhighlight();
        };

        if ($scope.filter.location) {
           $scope.moveFilterLocation($scope.filter.location);
        }

        filterAndShowServices();

        $scope.$watch('filter', function (newValue, oldValue) {
          filterChanged();
        }, true);

        leafletData.getMap(SEARCHMAP_ID).then(function (map) {
          map.addLayer($scope.servicesLayer);
          map.on('mousemove', mouseMoveEventHandler);
        });

      }])

    // Search Filter Object
    // that holds the various filters supplied by controls in eg. the sidebar and used to filter services
    .service('searchServiceFilterModel', function (OperationalServiceService, SpecificationService, OrganizationService) {

      this.data = {
        operationalServices: OperationalServiceService.query(),
        technicalSpecifications: null,
        organizations: OrganizationService.query(),
        transportTypes: {
          mms: 'MMS',
          rest: 'REST',
          soap: 'SOAP',
          www: 'WWW',
          tcp: 'TCP',
          udp: 'UDP',
          aisasm: 'AISASM',
          tel: 'TEL',
          vhf: 'VHF',
          dgnss: 'DGNSS',
          other: 'OTHER'
        },
        result: []
      };

      this.filter = {
        anyText: null,
        location: null,
        operationalService: null,
        provider: null,
        technicalSpecification: null,
        transportType: null
      };

      this.clean = function () {
        delete this.filter.anyText;
        delete this.filter.location;
        delete this.filter.operationalService;
        delete this.filter.provider;
        delete this.filter.technicalSpecification;
        delete this.filter.transportType;
      };

    });

;
<|endoftext|>


<commit_before>import {
  validateFormat,
  validateLength
} from 'ember-changeset-validations/validators';
import validatePhone from 'pruefung-client/validators/phone';

export default {
  email: validateFormat({
    allowBlank: true,
    type: 'email'
  }),
  firstName: validateLength({
    allowNone: false,
    min: 3
  }),
  fieldOfDuties: validateLength({
    allowNone: false,
    min: 3
  }),
  lastName: validateLength({
    allowNone: false,
    min: 3
  }),
  telephone: validatePhone({
    allowBlank: true
  })
};
<commit_msg>remove file which has been added accidentally in 632c36eb127641732e704f4d7127fe833ce72b9f<commit_after><|endoftext|>


<commit_before>var generator = require('../generator');

exports['accepts single operand'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: { name: 'gareth' }
  });
  test.equals('name:gareth', actual);
  test.done();
};

exports['accepts multiple operands in array'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [{ name: 'gareth' }, { name: 'milan' }]
  });
  test.equals('name:gareth OR name:milan', actual);
  test.done();
};

exports['accepts multiple operands in object'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: [{ name: 'gareth', job: 'geek' }]
  });
  test.equals('name:gareth AND job:geek', actual);
  test.done();
};

exports['defaults to conjunction'] = function(test) {
  var actual = generator.convert({
    $operands: [{ name: 'gareth' }, { job: 'geek' }]
  });
  test.equals('name:gareth AND job:geek', actual);
  test.done();
};

exports['evaluates nested query: x AND (y OR z)'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'or',
        $operands: [
          { job: 'geek' },
          { job: 'musician' }
        ]
      }
    ]
  });
  test.equals('name:gareth AND (job:geek OR job:musician)', actual);
  test.done();
};

exports['evaluates nested query: x OR (y AND z)'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [
      { job: 'geek' },
      {
        $operator: 'and',
        $operands: [
          { job: 'musician' },
          { name: 'gareth' }
        ]
      }
    ]
  });
  test.equals('job:geek OR (job:musician AND name:gareth)', actual);
  test.done();
};

exports['evaluates multiple nested query: w OR (x AND (y OR z))'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'and',
        $operands: [
          { language: 'javascript' },
          {
            $operator: 'or',
            $operands: [
              { job: 'geek' },
              { job: 'musician' }
            ]
          }
        ]
      }
    ]
  });
  test.equals('name:gareth OR (language:javascript AND (job:geek OR job:musician))', actual);
  test.done();
};

exports['search all fields'] = function(test) {
  var actual = generator.convert({
    $operands: [ 'new' ]
  });
  test.equals('new', actual);
  test.done();
};

exports['search all fields with query'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: 'gareth' },
      'new'
    ]
  });
  test.equals('name:gareth AND new', actual);
  test.done();
};

exports['or shorthand'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: ['gareth','milan'] }
    ]
  });
  test.equals('name:(gareth OR milan)', actual);
  test.done();
};

exports['or shorthand with one value'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: ['gareth'] }
    ]
  });
  test.equals('name:gareth', actual);
  test.done();
};

exports['or shorthand with no values'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: [] },
      { job: 'geek' }
    ]
  });
  test.equals('job:geek', actual);
  test.done();
};

exports['includes field types'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        name: 'gareth',
        job: 'geek',
        age: 55,
        seconds: 123456789,
        height: 5.5,
        position: -5.123456789,
        male: true,
        dob: new Date(123456789)
      }]
    }, { schema: {
      name: 'string',
      age: 'int',
      seconds: 'long',
      height: 'float',
      position: 'double',
      male: 'boolean',
      dob: 'date'
    }}
  );

  var expected = 'name:gareth AND job:geek AND age<int>:55 AND ' +
                 'seconds<long>:123456789 AND height<float>:5.5 AND ' +
                 'position<double>:-5.123456789 AND male:true AND ' +
                 'dob<date>:"1970-01-02T10:17:36.789Z"';
  test.equals(expected, actual);
  test.done();
};

exports['handles falsy values'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        name: 'jenny',
        age: 0,
        male: false
      }]
    }, { schema: {
      age: 'int',
      male: 'boolean'
    }}
  );

  var expected = 'name:jenny AND age<int>:0 AND male:false';
  test.equals(expected, actual);
  test.done();
};

exports['handles date formats'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        dob: new Date(123456789),
        dod: '2014-05-31T11:00:00.000Z'
      }]
    }, { schema: {
      dod: 'date',
      dob: 'date'
    }}
  );

  var expected = 'dob<date>:"1970-01-02T10:17:36.789Z" AND ' +
                 'dod<date>:"2014-05-31T11:00:00.000Z"';
  test.equals(expected, actual);
  test.done();
};

exports['value ranges'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        name: { $from: 'gareth', $to: 'milan' },
        age: { $from: 55, $to: 63 },
        seconds: { $from: 0, $to: 123456789 },
        height: { $from: -3.5, $to: 5.5 },
        position: { $from: -10.123555555, $to: -5.123456789 },
        dob: { $from: new Date(123450000), $to: new Date(123456789) }
      }]
    }, { schema: {
      name: 'string',
      age: 'int',
      seconds: 'long',
      height: 'float',
      position: 'double',
      dob: 'date'
    }}
  );

  var expected = 'name:[gareth TO milan] AND age<int>:[55 TO 63] AND ' +
                 'seconds<long>:[0 TO 123456789] AND height<float>:[-3.5 TO 5.5] AND ' +
                 'position<double>:[-10.123555555 TO -5.123456789] AND ' +
                 'dob<date>:["1970-01-02T10:17:30.000Z" TO "1970-01-02T10:17:36.789Z"]';
  test.equals(expected, actual);
  test.done();
};

exports['escapes key'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: operands = {
      'KEY: + - && || ! ( ) { } [ ] ^ " ~ * ? : \\': 'VALUE'
    }
  });
  var expected = 'KEY\\: \\+ \\- \\&\\& \\|\\| \\! \\( \\) \\{ \\} \\[ \\] \\^ \\" \\~ \\* \\? \\: \\\\:VALUE';
  test.equals(expected, actual);
  test.done();
};

exports['handle null values'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: [
      { name: 'gareth' },
      { job: null },
      { shoesize: undefined }
    ]
  });
  test.equals('name:gareth', actual);
  test.done();
};

exports['not operator'] = function(test) {
  var actual = generator.convert({
    $operator: 'not',
    $operands: 'gareth'
  });
  test.equals('NOT gareth', actual);
  test.done();
};

exports['not operator with field'] = function(test) {
  var actual = generator.convert({
    $operator: 'not',
    $operands: { name: 'gareth' }
  });
  test.equals('NOT name:gareth', actual);
  test.done();
};

exports['not operator with and query'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'not',
        $operands: [
          { job: 'geek' }
        ]
      }
    ]
  });
  test.equals('name:gareth AND NOT job:geek', actual);
  test.done();
};

exports['not operator with or query'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'not',
        $operands: [
          { job: 'geek' }
        ]
      }
    ]
  });
  test.equals('name:gareth OR NOT job:geek', actual);
  test.done();
};

exports['search all fields with wildcards'] = function(test) {
  var actual = generator.convert({
    $operands: [ 'som?thing * else' ]
  });
  test.equals('som?thing * else', actual);
  test.done();
};

exports['using not operator with typed field'] = function(test) {
  var actual = generator.convert(
  {
    $operator: 'not',
    $operands: { age: 0 }
  }, {
    schema: { age: 'int' }
  });
  test.equals('NOT age<int>:0', actual);
  test.done();
};

exports['wraps named fields'] = function(test) {
  var actual = generator.convert({
    $operands: { id: 'abc-123xyz' }
  });
  test.equals('id:"abc-123xyz"', actual);
  test.done();
};

exports['escapes quotes in named fields'] = function(test) {
  var actual = generator.convert({
    $operands: { id: 'abc-123+"xyz"' }
  });
  test.equals('id:"abc-123+\\"xyz\\""', actual);
  test.done();
};<commit_msg>Escape quotes and wrap named strings with special characters.<commit_after>var generator = require('../generator');

exports['accepts single operand'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: { name: 'gareth' }
  });
  test.equals('name:gareth', actual);
  test.done();
};

exports['accepts multiple operands in array'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [{ name: 'gareth' }, { name: 'milan' }]
  });
  test.equals('name:gareth OR name:milan', actual);
  test.done();
};

exports['accepts multiple operands in object'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: [{ name: 'gareth', job: 'geek' }]
  });
  test.equals('name:gareth AND job:geek', actual);
  test.done();
};

exports['defaults to conjunction'] = function(test) {
  var actual = generator.convert({
    $operands: [{ name: 'gareth' }, { job: 'geek' }]
  });
  test.equals('name:gareth AND job:geek', actual);
  test.done();
};

exports['evaluates nested query: x AND (y OR z)'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'or',
        $operands: [
          { job: 'geek' },
          { job: 'musician' }
        ]
      }
    ]
  });
  test.equals('name:gareth AND (job:geek OR job:musician)', actual);
  test.done();
};

exports['evaluates nested query: x OR (y AND z)'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [
      { job: 'geek' },
      {
        $operator: 'and',
        $operands: [
          { job: 'musician' },
          { name: 'gareth' }
        ]
      }
    ]
  });
  test.equals('job:geek OR (job:musician AND name:gareth)', actual);
  test.done();
};

exports['evaluates multiple nested query: w OR (x AND (y OR z))'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'and',
        $operands: [
          { language: 'javascript' },
          {
            $operator: 'or',
            $operands: [
              { job: 'geek' },
              { job: 'musician' }
            ]
          }
        ]
      }
    ]
  });
  test.equals('name:gareth OR (language:javascript AND (job:geek OR job:musician))', actual);
  test.done();
};

exports['search all fields'] = function(test) {
  var actual = generator.convert({
    $operands: [ 'new' ]
  });
  test.equals('new', actual);
  test.done();
};

exports['search all fields with query'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: 'gareth' },
      'new'
    ]
  });
  test.equals('name:gareth AND new', actual);
  test.done();
};

exports['or shorthand'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: ['gareth','milan'] }
    ]
  });
  test.equals('name:(gareth OR milan)', actual);
  test.done();
};

exports['or shorthand with one value'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: ['gareth'] }
    ]
  });
  test.equals('name:gareth', actual);
  test.done();
};

exports['or shorthand with no values'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: [] },
      { job: 'geek' }
    ]
  });
  test.equals('job:geek', actual);
  test.done();
};

exports['includes field types'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        name: 'gareth',
        job: 'geek',
        age: 55,
        seconds: 123456789,
        height: 5.5,
        position: -5.123456789,
        male: true,
        dob: new Date(123456789)
      }]
    }, { schema: {
      name: 'string',
      age: 'int',
      seconds: 'long',
      height: 'float',
      position: 'double',
      male: 'boolean',
      dob: 'date'
    }}
  );

  var expected = 'name:gareth AND job:geek AND age<int>:55 AND ' +
                 'seconds<long>:123456789 AND height<float>:5.5 AND ' +
                 'position<double>:-5.123456789 AND male:true AND ' +
                 'dob<date>:"1970-01-02T10:17:36.789Z"';
  test.equals(expected, actual);
  test.done();
};

exports['handles falsy values'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        name: 'jenny',
        age: 0,
        male: false
      }]
    }, { schema: {
      age: 'int',
      male: 'boolean'
    }}
  );

  var expected = 'name:jenny AND age<int>:0 AND male:false';
  test.equals(expected, actual);
  test.done();
};

exports['handles date formats'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        dob: new Date(123456789),
        dod: '2014-05-31T11:00:00.000Z'
      }]
    }, { schema: {
      dod: 'date',
      dob: 'date'
    }}
  );

  var expected = 'dob<date>:"1970-01-02T10:17:36.789Z" AND ' +
                 'dod<date>:"2014-05-31T11:00:00.000Z"';
  test.equals(expected, actual);
  test.done();
};

exports['value ranges'] = function(test) {
  var actual = generator.convert(
    {
      $operands: [{
        name: { $from: 'gareth', $to: 'milan' },
        age: { $from: 55, $to: 63 },
        seconds: { $from: 0, $to: 123456789 },
        height: { $from: -3.5, $to: 5.5 },
        position: { $from: -10.123555555, $to: -5.123456789 },
        dob: { $from: new Date(123450000), $to: new Date(123456789) }
      }]
    }, { schema: {
      name: 'string',
      age: 'int',
      seconds: 'long',
      height: 'float',
      position: 'double',
      dob: 'date'
    }}
  );

  var expected = 'name:[gareth TO milan] AND age<int>:[55 TO 63] AND ' +
                 'seconds<long>:[0 TO 123456789] AND height<float>:[-3.5 TO 5.5] AND ' +
                 'position<double>:[-10.123555555 TO -5.123456789] AND ' +
                 'dob<date>:["1970-01-02T10:17:30.000Z" TO "1970-01-02T10:17:36.789Z"]';
  test.equals(expected, actual);
  test.done();
};

exports['escapes key'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: operands = {
      'KEY: + - && || ! ( ) { } [ ] ^ " ~ * ? : \\': 'VALUE'
    }
  });
  var expected = 'KEY\\: \\+ \\- \\&\\& \\|\\| \\! \\( \\) \\{ \\} \\[ \\] \\^ \\" \\~ \\* \\? \\: \\\\:VALUE';
  test.equals(expected, actual);
  test.done();
};

exports['handle null values'] = function(test) {
  var actual = generator.convert({
    $operator: 'and',
    $operands: [
      { name: 'gareth' },
      { job: null },
      { shoesize: undefined }
    ]
  });
  test.equals('name:gareth', actual);
  test.done();
};

exports['not operator'] = function(test) {
  var actual = generator.convert({
    $operator: 'not',
    $operands: 'gareth'
  });
  test.equals('NOT gareth', actual);
  test.done();
};

exports['not operator with field'] = function(test) {
  var actual = generator.convert({
    $operator: 'not',
    $operands: { name: 'gareth' }
  });
  test.equals('NOT name:gareth', actual);
  test.done();
};

exports['not operator with and query'] = function(test) {
  var actual = generator.convert({
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'not',
        $operands: [
          { job: 'geek' }
        ]
      }
    ]
  });
  test.equals('name:gareth AND NOT job:geek', actual);
  test.done();
};

exports['not operator with or query'] = function(test) {
  var actual = generator.convert({
    $operator: 'or',
    $operands: [
      { name: 'gareth' },
      {
        $operator: 'not',
        $operands: [
          { job: 'geek' }
        ]
      }
    ]
  });
  test.equals('name:gareth OR NOT job:geek', actual);
  test.done();
};

exports['search all fields with wildcards'] = function(test) {
  var actual = generator.convert({
    $operands: [ 'som?thing * else' ]
  });
  test.equals('som?thing * else', actual);
  test.done();
};

exports['using not operator with typed field'] = function(test) {
  var actual = generator.convert(
  {
    $operator: 'not',
    $operands: { age: 0 }
  }, {
    schema: { age: 'int' }
  });
  test.equals('NOT age<int>:0', actual);
  test.done();
};

exports['wraps named fields'] = function(test) {
  var actual = generator.convert({
    $operands: { id: 'abc-123+xyz' }
  });
  test.equals('id:"abc-123+xyz"', actual);
  test.done();
};

exports['escapes quotes in named fields'] = function(test) {
  var actual = generator.convert({
    $operands: { id: 'abc-123+"xyz"' }
  });
  test.equals('id:"abc-123+\\"xyz\\""', actual);
  test.done();
};<|endoftext|>


<commit_before>//
// HLF Extensions Core Tests
// =========================
// Offloads testing larger core components to other test modules.
//
// [Page](../../../tests/core.unit.html)
//
(function() {
  'use strict';
  if (window.guard && !guard.isNavigatorSupported) { return; }

  require.config({
    baseUrl: '../',
    paths: {
      hlf: 'src/js',
      test: 'tests/js',
    }
  });

  define(['hlf/core', 'test/base'], function(HLF, base) {
    const { module, test } = QUnit;

    // ---

    module('other');

    test('exports', function(assert) {
      assert.ok(HLF, 'Namespace should exist.');
      assert.ok(HLF.buildExtension, 'Method should exist.');
    });

    // ---

    module('extension core', {
      beforeEach(assert) {
        Object.assign(this, {
          assertInstanceMethods(instance, ...methodNames) {
            methodNames.forEach(methodName => assert.ok(
              typeof instance[methodName] === 'function',
              `Instance has generated API addition ${methodName}.`
            ));
          },
          buildTestExtension(extensionClass, options) {
            options = Object.assign({}, options);
            HLF.buildExtension(extensionClass, options);
            this.SomeExtension = extensionClass;
          },
          createSomeChildElement() {
            let someElement = document.createElement('div');
            someElement.classList.add('foo');
            this.someElement.appendChild(someElement);
            return someElement;
          },
          createTestExtensionClass(additions, defaults) {
            const { methods, onNew, staticMethods } = additions || {};
            class SomeExtension {
              constructor(element, options, contextElement) {
                if (onNew) { onNew.apply(this, arguments); }
              }
            }
            Object.assign(SomeExtension.prototype, methods);
            Object.assign(SomeExtension, staticMethods, {
              debug: false,
              defaults: Object.assign({}, defaults),
              toPrefix() { return 'se'; },
            });
            return SomeExtension;
          },
          createTestExtension({ classAdditions, createOptions, defaults } = {}) {
            createOptions = Object.assign({}, {
              name: 'someExtension',
              namespace: {
                debug: false,
                toString() { return 'se'; },
                defaults: Object.assign({}, defaults),
              },
              apiClass: this.createTestExtensionClass(classAdditions, defaults),
            }, createOptions);
            Object.assign(this, {
              extension: HLF.createExtension(createOptions),
              namespace: createOptions.namespace,
            });
          },
          someData: { key: 'value' },
          someElement: document.createElement('div'),
          someExtension: null,
        });
        document.getElementById('qunit-fixture').appendChild(this.someElement);
      },
      afterEach() {
        if (typeof this.someExtension === 'function') {
          this.someExtension('remove');
        }
        if (this.extension) {
          this.extension._deleteInstances();
        }
      },
    });

    test('initializers', function(assert) {
      this.buildTestExtension(this.createTestExtensionClass({
        methods: { init() { this._didInit = true; } },
        staticMethods: { init() { this._didInit = true; } },
      }));
      this.someExtension = this.SomeExtension.extend(this.someElement);
      const { someExtension, SomeExtension } = this;
      assert.ok(someExtension._didInit,
        'Instance had initializer called.');
      assert.ok(SomeExtension._didInit,
        'Extension class had initializer called.');
      assert.strictEqual(someExtension.element, this.someElement,
        'Extension stores the element as property.');
      assert.strictEqual(someExtension.rootElement, someExtension.element,
        'Extension sets the element as root element.');
      assert.ok(someExtension.element.classList.contains('js-se'),
        'Extension gives the element the main class.');
    });

    test('de-initializers', function(assert) {
      let didDeinit = false;
      let didReceiveEvent = false;
      this.buildTestExtension(this.createTestExtensionClass({
        onNew() {
          this.eventListeners = {};
          this.eventListeners[this.eventName('someevent')] = this._onSomeEvent;
          this.resizeDelay = 100;
        },
        methods: {
          deinit() { didDeinit = true; },
          _onSomeEvent(event) { didReceiveEvent = true; },
          _onWindowResize(event) { didReceiveEvent = true; },
        },
      }), { autoListen: true });
      this.someExtension = this.SomeExtension.extend(this.someElement);
      this.someExtension.remove();
      assert.ok(didDeinit,
        'Extension performs "remove" action, calls custom deinit, if any.');
      this.someElement.dispatchEvent(new CustomEvent('sesomeevent'));
      window.dispatchEvent(new Event('resize'));
      assert.notOk(didReceiveEvent,
        'Extension automatically removes listeners from autoListen.');
      this.someExtension = null;
    });

    test('compacted options', function(assert) {
      const defaultSelectors = { someElement: '.foo' };
      const optionSelectors = { someOtherElement: '.bar' };
      this.buildTestExtension(this.createTestExtensionClass(
        null, { classNames: {}, selectors: defaultSelectors }
      ), { compactOptions: true });
      this.someExtension = this.SomeExtension.extend(this.someElement, { selectors: optionSelectors });
      assert.notOk(this.someExtension.options,
        'Extension no longer stores the final options as property.');
      assert.deepEqual(this.someExtension.classNames, this.SomeExtension.defaults.classNames,
        'Extension stores the classNames option as property.');
      assert.deepEqual(this.someExtension.selectors, Object.assign({}, defaultSelectors, optionSelectors),
        'Extension stores the selectors option as property.');
      const configureSelectors = { someOtherElement: '.baz' };
      this.someExtension.configure({ selectors: configureSelectors });
      assert.deepEqual(this.someExtension.selectors,
        Object.assign({}, defaultSelectors, optionSelectors, configureSelectors),
        'Extension performs "configure" action, compacts new options.');
      this.someExtension.configure({ selectors: 'default' });
      assert.deepEqual(this.someExtension.selectors, defaultSelectors,
        'Extension performs "configure" action, restores default options.');
    });

    test('custom options', function(assert) {
      this.buildTestExtension(this.createTestExtensionClass(), { compactOptions: true });
      const options = { someOption: 'bar' };
      this.someElement.setAttribute('data-se', JSON.stringify(options));
      this.someExtension = this.SomeExtension.extend(this.someElement);
      assert.equal(this.someExtension.someOption, options.someOption,
        'Extension allows custom options via element data attribute.');
    });

    test('naming methods', function(assert) {
      const methodNames = ['attrName', 'className', 'eventName', 'varName'];
      this.createTestExtension();
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      this.assertInstanceMethods(instance, ...methodNames);
      methodNames.forEach(methodName => assert.ok(
        typeof this.namespace[methodName] === 'function',
        `Namespace has generated API addition ${methodName}.`
      ));
      assert.equal(instance.attrName('some-attribute'), 'data-se-some-attribute',
        'attrName namespaces attribute names correctly.');
      assert.equal(instance.className('some-class'), 'js-se-some-class',
        'className namespaces class names correctly.');
      assert.equal(instance.eventName('someevent'), 'sesomeevent',
        'eventName namespaces event names correctly.');
      assert.equal(instance.varName('some-var'), '--se-some-var',
        'varName namespaces CSS var names correctly.');
    });

    test('timeout methods', function(assert) {
      this.createTestExtension();
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      let done = assert.async();
      const duration = 50;

      let elementTimeoutCompleted = false;
      instance.setElementTimeout(this.someElement, 'some-timeout', duration,
        () => { elementTimeoutCompleted = true; });
      assert.ok(this.someElement.hasAttribute('data-se-some-timeout'),
        'setElementTimeout stores timeout as element data attribute per name.');

      let timeoutCompleted = false;
      instance.setTimeout('_someTimeout', duration,
        () => { timeoutCompleted = true; });
      assert.ok(instance._someTimeout,
        'setTimeout stores timeout as property value per name.');

      let elementTimeoutCleared = true;
      instance.setElementTimeout(this.someElement, 'some-timeout-to-clear', duration,
        () => { elementTimeoutCleared = false; });
      instance.setElementTimeout(this.someElement, 'some-timeout-to-clear', null);
      assert.notOk(this.someElement.hasAttribute('data-se-some-timeout-to-clear'),
        'setElementTimeout clears timeout if value is null.');

      let timeoutCleared = true;
      instance.setTimeout('_someTimeoutToClear', duration,
        () => { timeoutCleared = false; });
      instance.setTimeout('_someTimeoutToClear', null);
      assert.notOk(instance._someTimeoutToClear,
        'setTimeout clears timeout if value is null.');

      setTimeout(() => {

        assert.ok(elementTimeoutCompleted, 'setElementTimeout calls callback.');
        assert.notOk(this.someElement.hasAttribute('data-se-some-timeout'),
          'setElementTimeout removes element data attribute upon completion.');

        assert.ok(timeoutCompleted, 'setTimeout calls callback.');
        assert.notOk(instance._someTimeout,
          'setTimeout removes property value upon completion.');

        assert.ok(elementTimeoutCleared,
          'setElementTimeout clears timeout if value is null.');
        assert.ok(timeoutCleared,
          'setTimeout clears timeout if value is null.');

        done();
      }, duration + 50);
    });

    test('css methods', function(assert) {
      this.createTestExtension({
        createOptions: { baseMethodGroups: ['css'] },
      });
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      let childElement = this.createSomeChildElement();

      this.someElement.style.setProperty('--se-some-size', '1px');
      assert.equal(instance.cssVariable('some-size'), '1px',
        'cssVariable returns variable value on root element by default.');
      childElement.style.setProperty('--se-some-size', '2px');
      assert.equal(instance.cssVariable('some-size', childElement), '2px',
        'cssVariable returns variable value on given element if any.');

      this.someElement.style.setProperty('transition-duration', '0.1s');
      assert.equal(instance.cssDuration('transition-duration'), 100,
        'cssDuration returns converted duration on root element by default.');
      childElement.style.setProperty('transition-duration', '0.2s');
      assert.equal(instance.cssDuration('transition-duration', childElement), 200,
        'cssDuration returns converted duration on given element if any.');
    });

    test('as shared instance', function(assert) {
      this.createSomeChildElement();
      this.createTestExtension();
      let subject = (contextElement) => (contextElement.querySelectorAll('.foo'));
      this.someExtension = this.extension(subject, this.someElement);
      let instance = this.someExtension();
      assert.deepEqual(instance.elements, Array.from(subject(this.someElement)),
        'Extension stores the elements as property.');
      assert.deepEqual(instance.options.querySelector(this.someElement), subject(this.someElement),
        'Extension stores custom query-selector subject as option.');
      assert.strictEqual(instance.contextElement, this.someElement,
        'Extension stores the context element as property.');
      assert.strictEqual(instance.rootElement, instance.contextElement,
        'Extension sets the context element as root element.');
      assert.ok(instance.contextElement.classList.contains('js-se'),
        'Extension gives the context element the main class.');
      assert.equal(instance.id,
        parseInt(this.someElement.getAttribute('data-se-instance-id')),
        'Extension stores singleton with context element.');
    });

    test('compactOptions option', function(assert) {
      this.createTestExtension({
        createOptions: { compactOptions: true },
        defaults: { someOption: 'foo' },
      });
      this.someExtension = this.extension(this.someElement, {
        someOtherOption: 'bar',
      });
      let instance = this.someExtension();
      assert.equal(instance.someOption, 'foo',
        'Instance has default options merged in as properties.');
      assert.equal(instance.someOtherOption, 'bar',
        'Instance has custom options merged in as properties.');
    });

    test('autoBind option', function(assert) {
      this.createTestExtension({
        classAdditions: { methods: {
          _onSomeEvent() {
            this.someContext = this;
          }
        }},
        createOptions: { autoBind: true },
      });
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      let { _onSomeEvent } = instance;
      _onSomeEvent();
      assert.strictEqual(instance.someContext, instance,
        'Instance has auto-bound methods.');
    });

    test('autoListen option', function(assert) {
      this.createTestExtension({
        classAdditions: {
          onNew() {
            this.eventListeners = {};
            this.eventListeners[this.eventName('someevent')] = this._onSomeEvent;
            this.resizeDelay = 100;
            this._resizeCount = 0;
          },
          methods: {
            _onSomeEvent(event) {
              this._someEventDetail = event.detail;
            },
            _onWindowResize(event) {
              this._resizeCount += 1;
            },
          },
        },
        createOptions: { autoListen: true },
      });
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      this.assertInstanceMethods(instance,
        'addEventListeners', 'removeEventListeners', 'toggleEventListeners',
        'createCustomEvent', 'dispatchCustomEvent');
      instance.dispatchCustomEvent('someevent', this.someData);
      assert.equal(instance._someEventDetail, this.someData,
        'Instance has auto-added auto-bound listeners based on eventListeners.');
      window.dispatchEvent(new Event('resize'));
      window.dispatchEvent(new Event('resize'));
      assert.equal(instance._resizeCount, 1,
        'Instance has auto-bound and throttled _onWindowResize listener.');
    });

    test('autoSelect option', function(assert) {
      this.createTestExtension({
        createOptions: { autoSelect: true },
        defaults: { selectors: { someElement: '.foo', someElements: '.foo' } },
      });
      this.createSomeChildElement();
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      assert.ok(instance.someElement instanceof HTMLElement,
        'Instance has auto-selected sub element based on selectors option.');
      assert.ok(instance.someElements instanceof NodeList,
        'Instance has auto-selected sub elements based on selectors option.');
      this.assertInstanceMethods(instance,
        'selectByClass', 'selectAllByClass', 'selectToProperties');
    });

    // ---

    QUnit.start();
  });

}());
<commit_msg>Core revision: pass 'naming methods' test.<commit_after>//
// HLF Extensions Core Tests
// =========================
// Offloads testing larger core components to other test modules.
//
// [Page](../../../tests/core.unit.html)
//
(function() {
  'use strict';
  if (window.guard && !guard.isNavigatorSupported) { return; }

  require.config({
    baseUrl: '../',
    paths: {
      hlf: 'src/js',
      test: 'tests/js',
    }
  });

  define(['hlf/core', 'test/base'], function(HLF, base) {
    const { module, test } = QUnit;

    // ---

    module('other');

    test('exports', function(assert) {
      assert.ok(HLF, 'Namespace should exist.');
      assert.ok(HLF.buildExtension, 'Method should exist.');
    });

    // ---

    module('extension core', {
      beforeEach(assert) {
        Object.assign(this, {
          assertInstanceMethods(instance, ...methodNames) {
            methodNames.forEach(methodName => assert.ok(
              typeof instance[methodName] === 'function',
              `Instance has generated API addition ${methodName}.`
            ));
          },
          buildTestExtension(extensionClass, options) {
            options = Object.assign({}, options);
            HLF.buildExtension(extensionClass, options);
            this.SomeExtension = extensionClass;
          },
          createSomeChildElement() {
            let someElement = document.createElement('div');
            someElement.classList.add('foo');
            this.someElement.appendChild(someElement);
            return someElement;
          },
          createTestExtensionClass(additions, defaults) {
            const { methods, onNew, staticMethods } = additions || {};
            class SomeExtension {
              constructor(element, options, contextElement) {
                if (onNew) { onNew.apply(this, arguments); }
              }
            }
            Object.assign(SomeExtension.prototype, methods);
            Object.assign(SomeExtension, staticMethods, {
              debug: false,
              defaults: Object.assign({}, defaults),
              toPrefix() { return 'se'; },
            });
            return SomeExtension;
          },
          createTestExtension({ classAdditions, createOptions, defaults } = {}) {
            createOptions = Object.assign({}, {
              name: 'someExtension',
              namespace: {
                debug: false,
                toString() { return 'se'; },
                defaults: Object.assign({}, defaults),
              },
              apiClass: this.createTestExtensionClass(classAdditions, defaults),
            }, createOptions);
            Object.assign(this, {
              extension: HLF.createExtension(createOptions),
              namespace: createOptions.namespace,
            });
          },
          someData: { key: 'value' },
          someElement: document.createElement('div'),
          someExtension: null,
        });
        document.getElementById('qunit-fixture').appendChild(this.someElement);
      },
      afterEach() {
        if (typeof this.someExtension === 'function') {
          this.someExtension('remove');
        }
        if (this.extension) {
          this.extension._deleteInstances();
        }
      },
    });

    test('initializers', function(assert) {
      this.buildTestExtension(this.createTestExtensionClass({
        methods: { init() { this._didInit = true; } },
        staticMethods: { init() { this._didInit = true; } },
      }));
      this.someExtension = this.SomeExtension.extend(this.someElement);
      const { someExtension, SomeExtension } = this;
      assert.ok(someExtension._didInit,
        'Instance had initializer called.');
      assert.ok(SomeExtension._didInit,
        'Extension class had initializer called.');
      assert.strictEqual(someExtension.element, this.someElement,
        'Extension stores the element as property.');
      assert.strictEqual(someExtension.rootElement, someExtension.element,
        'Extension sets the element as root element.');
      assert.ok(someExtension.element.classList.contains('js-se'),
        'Extension gives the element the main class.');
    });

    test('de-initializers', function(assert) {
      let didDeinit = false;
      let didReceiveEvent = false;
      this.buildTestExtension(this.createTestExtensionClass({
        onNew() {
          this.eventListeners = {};
          this.eventListeners[this.eventName('someevent')] = this._onSomeEvent;
          this.resizeDelay = 100;
        },
        methods: {
          deinit() { didDeinit = true; },
          _onSomeEvent(event) { didReceiveEvent = true; },
          _onWindowResize(event) { didReceiveEvent = true; },
        },
      }), { autoListen: true });
      this.someExtension = this.SomeExtension.extend(this.someElement);
      this.someExtension.remove();
      assert.ok(didDeinit,
        'Extension performs "remove" action, calls custom deinit, if any.');
      this.someElement.dispatchEvent(new CustomEvent('sesomeevent'));
      window.dispatchEvent(new Event('resize'));
      assert.notOk(didReceiveEvent,
        'Extension automatically removes listeners from autoListen.');
      this.someExtension = null;
    });

    test('compacted options', function(assert) {
      const defaultSelectors = { someElement: '.foo' };
      const optionSelectors = { someOtherElement: '.bar' };
      this.buildTestExtension(this.createTestExtensionClass(
        null, { classNames: {}, selectors: defaultSelectors }
      ), { compactOptions: true });
      this.someExtension = this.SomeExtension.extend(this.someElement, { selectors: optionSelectors });
      assert.notOk(this.someExtension.options,
        'Extension no longer stores the final options as property.');
      assert.deepEqual(this.someExtension.classNames, this.SomeExtension.defaults.classNames,
        'Extension stores the classNames option as property.');
      assert.deepEqual(this.someExtension.selectors, Object.assign({}, defaultSelectors, optionSelectors),
        'Extension stores the selectors option as property.');
      const configureSelectors = { someOtherElement: '.baz' };
      this.someExtension.configure({ selectors: configureSelectors });
      assert.deepEqual(this.someExtension.selectors,
        Object.assign({}, defaultSelectors, optionSelectors, configureSelectors),
        'Extension performs "configure" action, compacts new options.');
      this.someExtension.configure({ selectors: 'default' });
      assert.deepEqual(this.someExtension.selectors, defaultSelectors,
        'Extension performs "configure" action, restores default options.');
    });

    test('custom options', function(assert) {
      this.buildTestExtension(this.createTestExtensionClass(), { compactOptions: true });
      const options = { someOption: 'bar' };
      this.someElement.setAttribute('data-se', JSON.stringify(options));
      this.someExtension = this.SomeExtension.extend(this.someElement);
      assert.equal(this.someExtension.someOption, options.someOption,
        'Extension allows custom options via element data attribute.');
    });

    test('naming methods', function(assert) {
      const methodNames = ['attrName', 'className', 'eventName', 'varName'];
      this.buildTestExtension(this.createTestExtensionClass());
      this.someExtension = this.SomeExtension.extend(this.someElement);
      this.assertInstanceMethods(this.someExtension, ...methodNames);
      methodNames.forEach(methodName => assert.ok(
        typeof this.SomeExtension[methodName] === 'function',
        `Namespace has generated API addition ${methodName}.`
      ));
      assert.equal(this.someExtension.attrName('some-attribute'), 'data-se-some-attribute',
        'attrName namespaces attribute names correctly.');
      assert.equal(this.someExtension.className('some-class'), 'js-se-some-class',
        'className namespaces class names correctly.');
      assert.equal(this.someExtension.eventName('someevent'), 'sesomeevent',
        'eventName namespaces event names correctly.');
      assert.equal(this.someExtension.varName('some-var'), '--se-some-var',
        'varName namespaces CSS var names correctly.');
    });

    test('timeout methods', function(assert) {
      this.createTestExtension();
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      let done = assert.async();
      const duration = 50;

      let elementTimeoutCompleted = false;
      instance.setElementTimeout(this.someElement, 'some-timeout', duration,
        () => { elementTimeoutCompleted = true; });
      assert.ok(this.someElement.hasAttribute('data-se-some-timeout'),
        'setElementTimeout stores timeout as element data attribute per name.');

      let timeoutCompleted = false;
      instance.setTimeout('_someTimeout', duration,
        () => { timeoutCompleted = true; });
      assert.ok(instance._someTimeout,
        'setTimeout stores timeout as property value per name.');

      let elementTimeoutCleared = true;
      instance.setElementTimeout(this.someElement, 'some-timeout-to-clear', duration,
        () => { elementTimeoutCleared = false; });
      instance.setElementTimeout(this.someElement, 'some-timeout-to-clear', null);
      assert.notOk(this.someElement.hasAttribute('data-se-some-timeout-to-clear'),
        'setElementTimeout clears timeout if value is null.');

      let timeoutCleared = true;
      instance.setTimeout('_someTimeoutToClear', duration,
        () => { timeoutCleared = false; });
      instance.setTimeout('_someTimeoutToClear', null);
      assert.notOk(instance._someTimeoutToClear,
        'setTimeout clears timeout if value is null.');

      setTimeout(() => {

        assert.ok(elementTimeoutCompleted, 'setElementTimeout calls callback.');
        assert.notOk(this.someElement.hasAttribute('data-se-some-timeout'),
          'setElementTimeout removes element data attribute upon completion.');

        assert.ok(timeoutCompleted, 'setTimeout calls callback.');
        assert.notOk(instance._someTimeout,
          'setTimeout removes property value upon completion.');

        assert.ok(elementTimeoutCleared,
          'setElementTimeout clears timeout if value is null.');
        assert.ok(timeoutCleared,
          'setTimeout clears timeout if value is null.');

        done();
      }, duration + 50);
    });

    test('css methods', function(assert) {
      this.createTestExtension({
        createOptions: { baseMethodGroups: ['css'] },
      });
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      let childElement = this.createSomeChildElement();

      this.someElement.style.setProperty('--se-some-size', '1px');
      assert.equal(instance.cssVariable('some-size'), '1px',
        'cssVariable returns variable value on root element by default.');
      childElement.style.setProperty('--se-some-size', '2px');
      assert.equal(instance.cssVariable('some-size', childElement), '2px',
        'cssVariable returns variable value on given element if any.');

      this.someElement.style.setProperty('transition-duration', '0.1s');
      assert.equal(instance.cssDuration('transition-duration'), 100,
        'cssDuration returns converted duration on root element by default.');
      childElement.style.setProperty('transition-duration', '0.2s');
      assert.equal(instance.cssDuration('transition-duration', childElement), 200,
        'cssDuration returns converted duration on given element if any.');
    });

    test('as shared instance', function(assert) {
      this.createSomeChildElement();
      this.createTestExtension();
      let subject = (contextElement) => (contextElement.querySelectorAll('.foo'));
      this.someExtension = this.extension(subject, this.someElement);
      let instance = this.someExtension();
      assert.deepEqual(instance.elements, Array.from(subject(this.someElement)),
        'Extension stores the elements as property.');
      assert.deepEqual(instance.options.querySelector(this.someElement), subject(this.someElement),
        'Extension stores custom query-selector subject as option.');
      assert.strictEqual(instance.contextElement, this.someElement,
        'Extension stores the context element as property.');
      assert.strictEqual(instance.rootElement, instance.contextElement,
        'Extension sets the context element as root element.');
      assert.ok(instance.contextElement.classList.contains('js-se'),
        'Extension gives the context element the main class.');
      assert.equal(instance.id,
        parseInt(this.someElement.getAttribute('data-se-instance-id')),
        'Extension stores singleton with context element.');
    });

    test('compactOptions option', function(assert) {
      this.createTestExtension({
        createOptions: { compactOptions: true },
        defaults: { someOption: 'foo' },
      });
      this.someExtension = this.extension(this.someElement, {
        someOtherOption: 'bar',
      });
      let instance = this.someExtension();
      assert.equal(instance.someOption, 'foo',
        'Instance has default options merged in as properties.');
      assert.equal(instance.someOtherOption, 'bar',
        'Instance has custom options merged in as properties.');
    });

    test('autoBind option', function(assert) {
      this.createTestExtension({
        classAdditions: { methods: {
          _onSomeEvent() {
            this.someContext = this;
          }
        }},
        createOptions: { autoBind: true },
      });
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      let { _onSomeEvent } = instance;
      _onSomeEvent();
      assert.strictEqual(instance.someContext, instance,
        'Instance has auto-bound methods.');
    });

    test('autoListen option', function(assert) {
      this.createTestExtension({
        classAdditions: {
          onNew() {
            this.eventListeners = {};
            this.eventListeners[this.eventName('someevent')] = this._onSomeEvent;
            this.resizeDelay = 100;
            this._resizeCount = 0;
          },
          methods: {
            _onSomeEvent(event) {
              this._someEventDetail = event.detail;
            },
            _onWindowResize(event) {
              this._resizeCount += 1;
            },
          },
        },
        createOptions: { autoListen: true },
      });
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      this.assertInstanceMethods(instance,
        'addEventListeners', 'removeEventListeners', 'toggleEventListeners',
        'createCustomEvent', 'dispatchCustomEvent');
      instance.dispatchCustomEvent('someevent', this.someData);
      assert.equal(instance._someEventDetail, this.someData,
        'Instance has auto-added auto-bound listeners based on eventListeners.');
      window.dispatchEvent(new Event('resize'));
      window.dispatchEvent(new Event('resize'));
      assert.equal(instance._resizeCount, 1,
        'Instance has auto-bound and throttled _onWindowResize listener.');
    });

    test('autoSelect option', function(assert) {
      this.createTestExtension({
        createOptions: { autoSelect: true },
        defaults: { selectors: { someElement: '.foo', someElements: '.foo' } },
      });
      this.createSomeChildElement();
      this.someExtension = this.extension(this.someElement);
      let instance = this.someExtension();
      assert.ok(instance.someElement instanceof HTMLElement,
        'Instance has auto-selected sub element based on selectors option.');
      assert.ok(instance.someElements instanceof NodeList,
        'Instance has auto-selected sub elements based on selectors option.');
      this.assertInstanceMethods(instance,
        'selectByClass', 'selectAllByClass', 'selectToProperties');
    });

    // ---

    QUnit.start();
  });

}());
<|endoftext|>


<commit_before>var URLHelper = require('./url-helper');
var _ = require('underscore');

function Dashboard (dashboard) {
  this._dashboard = dashboard;
}

Dashboard.prototype = {

  /**
   * @return {View} used in the dashboard
   */
  getView: function () {
    return this._dashboard.dashboardView;
  },

  /**
   * @return {Map} the map used in the dashboard
   */
  getMap: function () {
    return this._dashboard.vis;
  },

  /**
   * @return {Array} of widgets in the dashboard
   */
  getWidgets: function () {
    return this._dashboard.widgets.getList();
  },

  getDashboardURL: function () {
    return URLHelper.getURLFromState(this.getState());
  },

  getState: function () {
    var state = {};
    var mapState = this.getMapState(); // TODO
    if (!_.isEmpty(mapState)) state.map = mapState;
    /*
    * TODO: Disabled widget states until issues are fixed.
    * See https://github.com/CartoDB/deep-insights.js/issues/416
    */
    // var widgetsState = this._dashboard.widgets._widgetsCollection.getStates();
    // if (!_.isEmpty(widgetsState)) state.widgets = widgetsState;
    return state;
  },

  getMapState: function () {
    var initialState = this._dashboard.dashboardView.getInitialMapState();
    var currentCenter = this._dashboard.vis.map.get('center');
    var currentZoom = this._dashboard.vis.map.getZoom();
    var upperLat = initialState.center[0] * 0.01;
    var upperLon = initialState.center[1] * 0.01;
    var inside = (Math.abs(initialState.center[0] - currentCenter[0]) < upperLat) && (Math.abs(initialState.center[1] - currentCenter[1]) < upperLon);
    if (inside && _.isEqual(currentZoom, initialState.zoom)) {
      return {};
    } else {
      return {
        center: currentCenter,
        zoom: currentZoom
      };
    }
  },

  setState: function (state) {
    // todo: set map state
    this._dashboard.widgets.setWidgetsState(state.widgets);
    this._dashboard.vis.map.setView(state.map.center, state.map.zoom);
  },

  onStateChanged: function (callback) {
    this._dashboard.widgets._widgetsCollection.bind('change', function () {
      callback(this.getState(), this.getDashboardURL());
    }, this);
    this._dashboard.vis.map.bind('change', function () {
      callback(this.getState(), this.getDashboardURL());
    }, this);
  },

  /**
   * @param {Integer} id - widget id
   * @return a widget object
   */
  getWidget: function (id) {
    return this._dashboard.widgets.get(id);
  },

  /**
   * Create a category widget.
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {CategoryWidget} The new widget
   */
  createCategoryWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createCategoryModel(widgetAttrs, layer);
  },

  /**
   * Create a histogram widget
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {HistogramWidget} The new widget
   */
  createHistogramWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createHistogramModel(widgetAttrs, layer);
  },

  /**
   * Create a formula widget
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {FormulaWidget} The new widget
   */
  createFormulaWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createFormulaModel(widgetAttrs, layer);
  },

  /**
   * Create a timesier es widget
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {TimeSeriesWidget} The new widget
   */
  createTimeSeriesWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createTimeSeriesModel(widgetAttrs, layer);
  }

};

module.exports = Dashboard;
<commit_msg>disable more code<commit_after>var URLHelper = require('./url-helper');
var _ = require('underscore');

function Dashboard (dashboard) {
  this._dashboard = dashboard;
}

Dashboard.prototype = {

  /**
   * @return {View} used in the dashboard
   */
  getView: function () {
    return this._dashboard.dashboardView;
  },

  /**
   * @return {Map} the map used in the dashboard
   */
  getMap: function () {
    return this._dashboard.vis;
  },

  /**
   * @return {Array} of widgets in the dashboard
   */
  getWidgets: function () {
    return this._dashboard.widgets.getList();
  },

  getDashboardURL: function () {
    return URLHelper.getURLFromState(this.getState());
  },

  getState: function () {
    var state = {};
    var mapState = this.getMapState(); // TODO
    if (!_.isEmpty(mapState)) state.map = mapState;
    /*
     * TODO: Disabled widget states until issues are fixed.
     * See https://github.com/CartoDB/deep-insights.js/issues/416
     */
    // var widgetsState = this._dashboard.widgets._widgetsCollection.getStates();
    // if (!_.isEmpty(widgetsState)) state.widgets = widgetsState;
    return state;
  },

  getMapState: function () {
    var initialState = this._dashboard.dashboardView.getInitialMapState();
    var currentCenter = this._dashboard.vis.map.get('center');
    var currentZoom = this._dashboard.vis.map.getZoom();
    var upperLat = initialState.center[0] * 0.01;
    var upperLon = initialState.center[1] * 0.01;
    var inside = (Math.abs(initialState.center[0] - currentCenter[0]) < upperLat) && (Math.abs(initialState.center[1] - currentCenter[1]) < upperLon);
    if (inside && _.isEqual(currentZoom, initialState.zoom)) {
      return {};
    } else {
      return {
        center: currentCenter,
        zoom: currentZoom
      };
    }
  },

  setState: function (state) {
    // todo: set map state
    this._dashboard.widgets.setWidgetsState(state.widgets);
    this._dashboard.vis.map.setView(state.map.center, state.map.zoom);
  },

  onStateChanged: function (callback) {
    /*
     * TODO: Disabled widget states until issues are fixed.
     * See https://github.com/CartoDB/deep-insights.js/issues/416
     */
    // this._dashboard.widgets._widgetsCollection.bind('change', function () {
    //   callback(this.getState(), this.getDashboardURL());
    // }, this);

    this._dashboard.vis.map.bind('change', function () {
      callback(this.getState(), this.getDashboardURL());
    }, this);
  },

  /**
   * @param {Integer} id - widget id
   * @return a widget object
   */
  getWidget: function (id) {
    return this._dashboard.widgets.get(id);
  },

  /**
   * Create a category widget.
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {CategoryWidget} The new widget
   */
  createCategoryWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createCategoryModel(widgetAttrs, layer);
  },

  /**
   * Create a histogram widget
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {HistogramWidget} The new widget
   */
  createHistogramWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createHistogramModel(widgetAttrs, layer);
  },

  /**
   * Create a formula widget
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {FormulaWidget} The new widget
   */
  createFormulaWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createFormulaModel(widgetAttrs, layer);
  },

  /**
   * Create a timesier es widget
   * @param {Object} widgetAttrs - attributes for the new widget
   * @param {string} widgetAttrs.id - id (required)
   * @param {string} widgetAttrs.title - title (required)
   * @param {number} widgetAttrs.order - index of the widget (optional)
   * @param ...
   * @return {TimeSeriesWidget} The new widget
   */
  createTimeSeriesWidget: function (widgetAttrs, layer) {
    return this._dashboard.widgets.createTimeSeriesModel(widgetAttrs, layer);
  }

};

module.exports = Dashboard;
<|endoftext|>


<commit_before>import React from 'react'
import classnames from 'classnames'

import './style.scss'

function price (item) {
  if (item.price) {
    return (

Price

{item.price}€

    )
  }
}

function image (item) {
  const imageURL = `/assets/img/provider/${item.provider}.jpeg`
  return (

  )
}

function date (item) {
  const locale = navigator.language

  const opts = {
    month: 'short',
    day: 'numeric',
    year: 'numeric'
  }

  const timestamp = item.updatedAt || item.createdAt
  return (new Date(timestamp)).toLocaleString(locale, opts)
}

function Hit ({item, toggle, get}) {
  return (

        {image(item)}

            {date(item)}

          {price(item)}

  )
}

export default Hit
<commit_msg>shortenURL → shortenUrl<commit_after>import React from 'react'
import classnames from 'classnames'

import './style.scss'

function price (item) {
  if (item.price) {
    return (

Price

{item.price}€

    )
  }
}

function image (item) {
  const imageURL = `/assets/img/provider/${item.provider}.jpeg`
  return (

  )
}

function date (item) {
  const locale = navigator.language

  const opts = {
    month: 'short',
    day: 'numeric',
    year: 'numeric'
  }

  const timestamp = item.updatedAt || item.createdAt
  return (new Date(timestamp)).toLocaleString(locale, opts)
}

function Hit ({item, toggle, get}) {
  return (

        {image(item)}

            {date(item)}

          {price(item)}

  )
}

export default Hit
<|endoftext|>