<commit_before>/**
 */

Template.locations.created = function () {
    //
};

Template.locations.helpers({
    //
});

/**
 * Code executed once the page is loaded and rendered
 */
Template.locations.rendered = function () {

    /**
     * Set the map projection to a Southern California focus
     *
     * This will need to be reprojected if the background map raster changes
     */
    var projection = d3.geo.mercator()
        .scale(13168)
        .center([-119.082, 34.656])
        .precision(0.1);

    /**
     * Dev map features
     *
     * Useful for dev map registrion. Normally disabled
     */
    //devMapFeatures(d3, projection);

    /**
     * Initiate the SVG object for drawing all the location markers
     */
    // TODO: read this in from the LESS
    var width = 1920;
    var height = 1080;
    var svg = d3.select(".container").append("svg")
        .attr("class", 'svg-canvas')
        .attr("width", width)
        .attr("height", height);

    /**
     * Load the meteor collection for Locations and Images
     */
    Deps.autorun(function () {

        //
        // Not sure why but we need to wait a few milliseconds before
        // asking for the images object from MiniMongo. otherwise we'll Get
        // the previous page's collection, which includes all the images for
        // that location, resulting in tens of images animating into place
        //
        window.setTimeout(function() {
            renderLocations();
        }, 200);

        function renderLocations() {

            /**
             * Draw each location
             */
            var locations = Locations.find().fetch();
            _.each(locations, function(location, i) {
                drawLocation(svg, projection, location, i);
            });

            /**
             * Draw each image
             *
             * TODO:
             * Actually I think that this will be our primary collection. I might
             * move this into a seperate collection at some point.
             *
             */
            var images = Images.find().fetch();
            console.log('images', images);
            images = _.shuffle(images);
            console.log('shuffled images', images);
            _.each(images, function(image, i) {
                drawImage(svg, projection, image, i);
            });
        }
    });

    function drawLocation(svg, projection, location, i) {
        /**
        * Use D3's projection manipulation to turn the long, lat coordinates into
        * tralation measurements.
        * Transform is a basic SVG attribute, and translate is a type of
        * transformation.
        */
        position = projection([location.longitude,location.latitude]);

        /**
         * Draw a white background first
         *
         * This serves as the matte for a shadow and also gives us a old
         * skoool white border around the photo.
         */
        //svg.append('rect')
        //.attr("transform", function() {
        //return "translate(" + projection([location.longitude,location.latitude]) + ")";
        //})
        //.attr("width", 300)
        //.attr("height", 250)
        //.attr('stroke', 'white')
        //.attr('stroke-width', '10')
        //.attr('class', 'location-matte');

        //clipId = 'special-' + i;
        //svg.append('clipPath')
        //.attr("id", clipId)
        /**
         * Clip with a circle that bounces in from its center
         */
        ////.append('circle')
        ////.attr("transform", function() {
        ////return "translate(" + projection([location.longitude,location.latitude]) + ")";
        ////})
        ////.attr("r", .01)
        ////.transition()
        ////.attr("r", 90)
        ////.duration(300)
        ////.delay(i * 200) // Stagger the markers animating in
        ////.transition()
        ////.attr("r", 40)
        ////.duration(100)
        ////.transition()
        ////.attr("r", 80)
        ////.duration(80)

        /**
         * Clip the image with a rectangle
         */
        //.append('rect')
        //.attr("transform", function() {
        //return "translate(" + projection([location.longitude,location.latitude]) + ")";
        //})
        //.attr("width", 300)
        //.attr("height", 250)

        //.attr('clip-path', clipIdUrl)
        //.attr("transform", function() {
        //return "translate(" + projection([location.longitude,location.latitude]) + ")";
        //})
        //.attr("r", 40);

        //clipIdUrl = 'url(#' + clipId + ')';
        //svg.append('image')
        //.attr("xlink:href", "/images/house.jpg")
        //.attr("width", "600")
        //.attr("height", "400")
        //.attr("x", (position[0]-100))
        //.attr("y", (position[1]-100))
        //.attr("clip-path", clipIdUrl);

        /**
         * Reference dot to show where the location lat long is pointing
         *
         * This should be turned off in the final version.
         */
        svg.append('circle')
            .attr("r", 10)
            .attr("transform", function() {
                    return "translate(" + projection([location.longitude,location.latitude]) + ")";
                    })
        .attr("class", 'location-ref-marker');

        /**
         * Append an image at a specific location
         *
         * This is for reference, now that we're appending images
         * per marker.
         */
            //svg.append("svg:image")
            //.attr("xlink:href", "/images/house.jpg")
            //.attr("x", position[0])
            //.attr("y", position[1])
            //.attr("width", "200")
            //.attr("height", "182")
            //.transition()
            //.attr("width", "400")
            //.attr("height", "282")
            //.duration(800);
    }

    function drawImage(svg, projection, image, i) {

        position = projection([image.longitude, image.latitude]);

        // Old skool border width
        var imageBorder = 5;

        // Group for all the picture elements
        var pictureGroup = svg.append("g")
            .attr('class', 'picture-group');

        var filter = pictureGroup.append("defs")
        .append("filter")
            .attr("id", "blur")
        .append("feGaussianBlur")
            .attr("stdDeviation", 5);

        // Drop shadow rectangle
        pictureGroup.append('rect')
            .attr("width", "0")
            .attr("height", "0")
            .attr("opacity", ".1")
            .attr("x", (position[0]))
            .attr("y", (position[1]))
            .style('fill', '#000')
            .transition()
            .delay(i * 50) // Stagger the markers animating in
            // Simulate scaling form the center of the image
            .attr("x", (position[0]) - (image.thumbWidth / 2))
            .attr("y", (position[1]) - (image.thumbHeight / 2))
            .attr("width", image.thumbWidth + (imageBorder * 2))
            .attr("height", image.thumbHeight + (imageBorder * 2))
            .attr("opacity", "1")
            .attr("filter", "url(#blur)")
            .duration(500);

        // White border rectangle
        pictureGroup.append('rect')
            .attr("width", "0")
            .attr("height", "0")
            .attr("opacity", ".1")
            .attr("x", (position[0]))
            .attr("y", (position[1]))
            .attr('class', 'location-matte')
            .transition()
            .delay(i * 50) // Stagger the markers animating in
            // Simulate scaling form the center of the image
            .attr("x", (position[0]) - (image.thumbWidth / 2))
            .attr("y", (position[1]) - (image.thumbHeight / 2))
            .attr("width", image.thumbWidth + (imageBorder * 2))
            .attr("height", image.thumbHeight + (imageBorder * 2))
            .attr("opacity", "1")
            .duration(500);

        // Image
        pictureGroup.append('image')
            .attr("xlink:href", "/images/thumbnails/" + image._id + ".jpg")
            .attr("data-id", image._id)
            .attr("data-location", image.generalLocationDs)
            .attr("width", "0")
            .attr("height", "0")
            .attr("opacity", ".1")
            .attr("x", (position[0] + imageBorder))
            .attr("y", (position[1] + imageBorder))
            .transition()
            .delay(i * 50) // Stagger the markers animating in
            // Simulate scaling form the center of the image
            .attr("x", (position[0] + imageBorder) - (image.thumbWidth / 2))
            .attr("y", (position[1] + imageBorder) - (image.thumbHeight / 2))
            .attr("width", image.thumbWidth)
            .attr("height", image.thumbHeight)
            .attr("opacity", "1")
            .attr('location', image.generalLocationDs)
            .duration(500);
    }

    function devMapFeatures(d3, projection) {
        /**
         * Test map data to position the map projection
         *
         * These don't need to be shown in the final map, but keep them here.
         * If the background raster map changes, we will need this data to
         * reproject the D3 map so that the location markers are correct.
         */

        var path = d3.geo.path()
        .projection(projection);

        d3.json("/data/salton.json", function(error, salton) {
        svg.append("path")
            .datum(topojson.feature(salton, salton.objects.salton))
            .attr("d", path)
            .attr("class", 'water');
        });

        d3.json("/data/i15.json", function(error, i15) {
        svg.append("path")
        .datum(topojson.feature(i15, i15.objects.i15))
            .attr("d", path)
            .attr("class", 'road');
        });

        /**
         * Solid state polygon.
         * Only really useful for positioning the wiggly part of the Colorado
         * River in Mexico.
         */
        d3.json("/data/states.json", function(error, states) {
            svg.append("path")
            .datum(topojson.feature(states, states.objects.states))
            .attr("d", path)
            .attr("class", 'states');
        });

        /**
         * Cities markers
         */
        d3.json("/data/cities.json", function(error, cities) {
            svg.append("path")
            .datum(topojson.feature(cities, cities.objects.cities))
            .attr("d", path)
            .attr("class", 'cities');
        });

    }

};

Template.locations.events({
    /**
     * Image click
     */
    'click image':function(event, template){

        var animateContentOut = function() {
            var imagePositions = d3.selectAll('image').attr('x');
            d3.selectAll("image").each( function(d, i){

                x = Number(d3.select(this).attr("x"));
                y = Number(d3.select(this).attr("y"));

                width = Number(d3.select(this).attr("width"));
                height = Number(d3.select(this).attr("height"));

                centerX = parseInt(x + (width / 2));
                centerY = parseInt(y + (height / 2));

                var pictureGroup = d3.select(this.parentNode)
                    .transition()
                    .delay(i * 75) // Stagger the markers animating out
                    .attr("transform", function (){
                        transform = 'translate(' + centerX + ',' + centerY + ')scale(0)';
                        return transform;
                    })
                    .duration(500);

            });
        };
        animateContentOut();

        window.setTimeout(function() {
            goDestination();
        }, 500);

        function goDestination() {
            // Get the clicked location string from the COM data-location attribute
            var imageLocation = $(event.currentTarget).data("location");
            var clickedImage  = $(event.currentTarget).data("id");
            console.log('clickedImage - ', clickedImage);
            // Query Mongo for a location with a matching title
            var clickedLocation = Locations.findOne( {title: imageLocation });
            // Navigate to the Location with the matching _id
            Router.go(
                'location',
                {link: clickedLocation.link},
                {query: {image: clickedImage}}
            );
        }

    }
});

<commit_msg>Fixing quote inconsistency<commit_after>/**
 */

Template.locations.created = function () {
    //
};

Template.locations.helpers({
    //
});

/**
 * Code executed once the page is loaded and rendered
 */
Template.locations.rendered = function () {

    /**
     * Set the map projection to a Southern California focus
     *
     * This will need to be reprojected if the background map raster changes
     */
    var projection = d3.geo.mercator()
        .scale(13168)
        .center([-119.082, 34.656])
        .precision(0.1);

    /**
     * Dev map features
     *
     * Useful for dev map registrion. Normally disabled
     */
    //devMapFeatures(d3, projection);

    /**
     * Initiate the SVG object for drawing all the location markers
     */
    // TODO: read this in from the LESS
    var width = 1920;
    var height = 1080;
    var svg = d3.select('.container').append('svg')
        .attr('class', 'svg-canvas')
        .attr('width', width)
        .attr('height', height);

    /**
     * Load the meteor collection for Locations and Images
     */
    Deps.autorun(function () {

        //
        // Not sure why but we need to wait a few milliseconds before
        // asking for the images object from MiniMongo. otherwise we'll Get
        // the previous page's collection, which includes all the images for
        // that location, resulting in tens of images animating into place
        //
        window.setTimeout(function() {
            renderLocations();
        }, 200);

        function renderLocations() {

            /**
             * Draw each location
             */
            var locations = Locations.find().fetch();
            _.each(locations, function(location, i) {
                drawLocation(svg, projection, location, i);
            });

            /**
             * Draw each image
             *
             * TODO:
             * Actually I think that this will be our primary collection. I might
             * move this into a seperate collection at some point.
             *
             */
            var images = Images.find().fetch();
            console.log('images', images);
            images = _.shuffle(images);
            console.log('shuffled images', images);
            _.each(images, function(image, i) {
                drawImage(svg, projection, image, i);
            });
        }
    });

    function drawLocation(svg, projection, location, i) {
        /**
        * Use D3's projection manipulation to turn the long, lat coordinates into
        * tralation measurements.
        * Transform is a basic SVG attribute, and translate is a type of
        * transformation.
        */
        position = projection([location.longitude,location.latitude]);

        /**
         * Draw a white background first
         *
         * This serves as the matte for a shadow and also gives us a old
         * skoool white border around the photo.
         */
        //svg.append('rect')
        //.attr('transform', function() {
        //return 'translate(' + projection([location.longitude,location.latitude]) + ')';
        //})
        //.attr('width', 300)
        //.attr('height', 250)
        //.attr('stroke', 'white')
        //.attr('stroke-width', '10')
        //.attr('class', 'location-matte');

        //clipId = 'special-' + i;
        //svg.append('clipPath')
        //.attr('id', clipId)
        /**
         * Clip with a circle that bounces in from its center
         */
        ////.append('circle')
        ////.attr('transform', function() {
        ////return 'translate(' + projection([location.longitude,location.latitude]) + ')';
        ////})
        ////.attr('r', .01)
        ////.transition()
        ////.attr('r', 90)
        ////.duration(300)
        ////.delay(i * 200) // Stagger the markers animating in
        ////.transition()
        ////.attr('r', 40)
        ////.duration(100)
        ////.transition()
        ////.attr('r', 80)
        ////.duration(80)

        /**
         * Clip the image with a rectangle
         */
        //.append('rect')
        //.attr('transform', function() {
        //return 'translate(' + projection([location.longitude,location.latitude]) + ')';
        //})
        //.attr('width', 300)
        //.attr('height', 250)

        //.attr('clip-path', clipIdUrl)
        //.attr('transform', function() {
        //return 'translate(' + projection([location.longitude,location.latitude]) + ')';
        //})
        //.attr('r', 40);

        //clipIdUrl = 'url(#' + clipId + ')';
        //svg.append('image')
        //.attr('xlink:href', '/images/house.jpg')
        //.attr('width', '600')
        //.attr('height', '400')
        //.attr('x', (position[0]-100))
        //.attr('y', (position[1]-100))
        //.attr('clip-path', clipIdUrl);

        /**
         * Reference dot to show where the location lat long is pointing
         *
         * This should be turned off in the final version.
         */
        svg.append('circle')
            .attr('r', 10)
            .attr('transform', function() {
                    return 'translate(' + projection([location.longitude,location.latitude]) + ')';
                    })
        .attr('class', 'location-ref-marker');

        /**
         * Append an image at a specific location
         *
         * This is for reference, now that we're appending images
         * per marker.
         */
            //svg.append('svg:image')
            //.attr('xlink:href', '/images/house.jpg')
            //.attr('x', position[0])
            //.attr('y', position[1])
            //.attr('width', '200')
            //.attr('height', '182')
            //.transition()
            //.attr('width', '400')
            //.attr('height', '282')
            //.duration(800);
    }

    function drawImage(svg, projection, image, i) {

        position = projection([image.longitude, image.latitude]);

        // Old skool border width
        var imageBorder = 5;

        // Group for all the picture elements
        var pictureGroup = svg.append('g')
            .attr('class', 'picture-group');

        var filter = pictureGroup.append('defs')
        .append('filter')
            .attr('id', 'blur')
        .append('feGaussianBlur')
            .attr('stdDeviation', 5);

        // Drop shadow rectangle
        pictureGroup.append('rect')
            .attr('width', '0')
            .attr('height', '0')
            .attr('opacity', '.1')
            .attr('x', (position[0]))
            .attr('y', (position[1]))
            .style('fill', '#000')
            .transition()
            .delay(i * 50) // Stagger the markers animating in
            // Simulate scaling form the center of the image
            .attr('x', (position[0]) - (image.thumbWidth / 2))
            .attr('y', (position[1]) - (image.thumbHeight / 2))
            .attr('width', image.thumbWidth + (imageBorder * 2))
            .attr('height', image.thumbHeight + (imageBorder * 2))
            .attr('opacity', '1')
            .attr('filter', 'url(#blur)')
            .duration(500);

        // White border rectangle
        pictureGroup.append('rect')
            .attr('width', '0')
            .attr('height', '0')
            .attr('opacity', '.1')
            .attr('x', (position[0]))
            .attr('y', (position[1]))
            .attr('class', 'location-matte')
            .transition()
            .delay(i * 50) // Stagger the markers animating in
            // Simulate scaling form the center of the image
            .attr('x', (position[0]) - (image.thumbWidth / 2))
            .attr('y', (position[1]) - (image.thumbHeight / 2))
            .attr('width', image.thumbWidth + (imageBorder * 2))
            .attr('height', image.thumbHeight + (imageBorder * 2))
            .attr('opacity', '1')
            .duration(500);

        // Image
        pictureGroup.append('image')
            .attr('xlink:href', '/images/thumbnails/' + image._id + '.jpg')
            .attr('data-id', image._id)
            .attr('data-location', image.generalLocationDs)
            .attr('width', '0')
            .attr('height', '0')
            .attr('opacity', '.1')
            .attr('x', (position[0] + imageBorder))
            .attr('y', (position[1] + imageBorder))
            .transition()
            .delay(i * 50) // Stagger the markers animating in
            // Simulate scaling form the center of the image
            .attr('x', (position[0] + imageBorder) - (image.thumbWidth / 2))
            .attr('y', (position[1] + imageBorder) - (image.thumbHeight / 2))
            .attr('width', image.thumbWidth)
            .attr('height', image.thumbHeight)
            .attr('opacity', '1')
            .attr('location', image.generalLocationDs)
            .duration(500);
    }

    function devMapFeatures(d3, projection) {
        /**
         * Test map data to position the map projection
         *
         * These don't need to be shown in the final map, but keep them here.
         * If the background raster map changes, we will need this data to
         * reproject the D3 map so that the location markers are correct.
         */

        var path = d3.geo.path()
        .projection(projection);

        d3.json('/data/salton.json', function(error, salton) {
        svg.append('path')
            .datum(topojson.feature(salton, salton.objects.salton))
            .attr('d', path)
            .attr('class', 'water');
        });

        d3.json('/data/i15.json', function(error, i15) {
        svg.append('path')
        .datum(topojson.feature(i15, i15.objects.i15))
            .attr('d', path)
            .attr('class', 'road');
        });

        /**
         * Solid state polygon.
         * Only really useful for positioning the wiggly part of the Colorado
         * River in Mexico.
         */
        d3.json('/data/states.json', function(error, states) {
            svg.append('path')
            .datum(topojson.feature(states, states.objects.states))
            .attr('d', path)
            .attr('class', 'states');
        });

        /**
         * Cities markers
         */
        d3.json('/data/cities.json', function(error, cities) {
            svg.append('path')
            .datum(topojson.feature(cities, cities.objects.cities))
            .attr('d', path)
            .attr('class', 'cities');
        });

    }

};

Template.locations.events({
    /**
     * Image click
     */
    'click image':function(event, template){

        var animateContentOut = function() {
            var imagePositions = d3.selectAll('image').attr('x');
            d3.selectAll('image').each( function(d, i){

                x = Number(d3.select(this).attr('x'));
                y = Number(d3.select(this).attr('y'));

                width = Number(d3.select(this).attr('width'));
                height = Number(d3.select(this).attr('height'));

                centerX = parseInt(x + (width / 2));
                centerY = parseInt(y + (height / 2));

                var pictureGroup = d3.select(this.parentNode)
                    .transition()
                    .delay(i * 75) // Stagger the markers animating out
                    .attr('transform', function (){
                        transform = 'translate(' + centerX + ',' + centerY + ')scale(0)';
                        return transform;
                    })
                    .duration(500);

            });
        };
        animateContentOut();

        window.setTimeout(function() {
            goDestination();
        }, 500);

        function goDestination() {
            // Get the clicked location string from the COM data-location attribute
            var imageLocation = $(event.currentTarget).data('location');
            var clickedImage  = $(event.currentTarget).data('id');
            console.log('clickedImage - ', clickedImage);
            // Query Mongo for a location with a matching title
            var clickedLocation = Locations.findOne( {title: imageLocation });
            // Navigate to the Location with the matching _id
            Router.go(
                'location',
                {link: clickedLocation.link},
                {query: {image: clickedImage}}
            );
        }

    }
});

<|endoftext|>


<commit_before>'use strict';

const createContext = require('../../../../../../test/helpers/create-context');
const apiTokenController = require('../api-token');

describe('API Token Controller', () => {
  describe('Create API Token', () => {
    const body = {
      name: 'api-token_tests-name',
      description: 'api-token_tests-description',
      type: 'read-only',
    };

    test('Fails if API Token already exist', async () => {
      const exists = jest.fn(() => true);
      const badRequest = jest.fn();
      const ctx = createContext({ body }, { badRequest });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              exists,
            },
          },
        },
      };

      await apiTokenController.create(ctx);

      expect(exists).toHaveBeenCalledWith({ name: body.name });
      expect(badRequest).toHaveBeenCalledWith('Name already taken');
    });

    test('Create API Token Successfully', async () => {
      const create = jest.fn().mockResolvedValue(body);
      const exists = jest.fn(() => false);
      const badRequest = jest.fn();
      const created = jest.fn();
      const ctx = createContext({ body }, { badRequest, created });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              exists,
              create,
            },
          },
        },
      };

      await apiTokenController.create(ctx);

      expect(exists).toHaveBeenCalledWith({ name: body.name });
      expect(badRequest).not.toHaveBeenCalled();
      expect(create).toHaveBeenCalledWith(body);
      expect(created).toHaveBeenCalled();
    });
  });

  describe('List API tokens', () => {
    const tokens = [
      {
        id: 1,
        name: 'api-token_tests-name',
        description: 'api-token_tests-description',
        type: 'read-only',
      },
      {
        id: 2,
        name: 'api-token_tests-name-2',
        description: 'api-token_tests-description-2',
        type: 'full-access',
      },
    ];

    test('List API tokens successfully', async () => {
      const list = jest.fn().mockResolvedValue(tokens);
      const send = jest.fn();
      const ctx = createContext({}, { send });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              list,
            },
          },
        },
      };

      await apiTokenController.list(ctx);

      expect(list).toHaveBeenCalled();
      expect(send).toHaveBeenCalledWith({ data: tokens });
    });
  });

  describe('Delete an API token', () => {
    const token = {
      id: 1,
      name: 'api-token_tests-name',
      description: 'api-token_tests-description',
      type: 'read-only',
    };

    test('Deletes an API token successfully', async () => {
      const revoke = jest.fn().mockResolvedValue(token);
      const deleted = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { deleted });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              revoke,
            },
          },
        },
      };

      await apiTokenController.revoke(ctx);

      expect(revoke).toHaveBeenCalledWith(token.id);
      expect(deleted).toHaveBeenCalledWith({ data: token });
    });

    test('Does not return an error if the ressource does not exists', async () => {
      const revoke = jest.fn().mockResolvedValue(null);
      const deleted = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { deleted });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              revoke,
            },
          },
        },
      };

      await apiTokenController.revoke(ctx);

      expect(revoke).toHaveBeenCalledWith(token.id);
      expect(deleted).toHaveBeenCalledWith({ data: null });
    });
  });

  describe('Retrieve an API token', () => {
    const token = {
      id: 1,
      name: 'api-token_tests-name',
      description: 'api-token_tests-description',
      type: 'read-only',
    };

    test('Retrieve an API token successfully', async () => {
      const getById = jest.fn().mockResolvedValue(token);
      const send = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { send });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              getById,
            },
          },
        },
      };

      await apiTokenController.get(ctx);

      expect(getById).toHaveBeenCalledWith(token.id);
      expect(send).toHaveBeenCalledWith({ data: token });
    });

    test('Fails if the API token does not exist', async () => {
      const getById = jest.fn().mockResolvedValue(null);
      const notFound = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { notFound });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              getById,
            },
          },
        },
      };

      await apiTokenController.get(ctx);

      expect(getById).toHaveBeenCalledWith(token.id);
      expect(notFound).toHaveBeenCalledWith('API Token not found');
    });
  });
});
<commit_msg>store the hashed accessKey in the database<commit_after>'use strict';

const createContext = require('../../../../../../test/helpers/create-context');
const apiTokenController = require('../api-token');

describe('API Token Controller', () => {
  describe('Create API Token', () => {
    const body = {
      name: 'api-token_tests-name',
      description: 'api-token_tests-description',
      type: 'read-only',
    };

    test('Fails if API Token already exists', async () => {
      const exists = jest.fn(() => true);
      const badRequest = jest.fn();
      const ctx = createContext({ body }, { badRequest });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              exists,
            },
          },
        },
      };

      await apiTokenController.create(ctx);

      expect(exists).toHaveBeenCalledWith({ name: body.name });
      expect(badRequest).toHaveBeenCalledWith('Name already taken');
    });

    test('Create API Token Successfully', async () => {
      const create = jest.fn().mockResolvedValue(body);
      const exists = jest.fn(() => false);
      const badRequest = jest.fn();
      const created = jest.fn();
      const ctx = createContext({ body }, { badRequest, created });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              exists,
              create,
            },
          },
        },
      };

      await apiTokenController.create(ctx);

      expect(exists).toHaveBeenCalledWith({ name: body.name });
      expect(badRequest).not.toHaveBeenCalled();
      expect(create).toHaveBeenCalledWith(body);
      expect(created).toHaveBeenCalled();
    });
  });

  describe('List API tokens', () => {
    const tokens = [
      {
        id: 1,
        name: 'api-token_tests-name',
        description: 'api-token_tests-description',
        type: 'read-only',
      },
      {
        id: 2,
        name: 'api-token_tests-name-2',
        description: 'api-token_tests-description-2',
        type: 'full-access',
      },
    ];

    test('List API tokens successfully', async () => {
      const list = jest.fn().mockResolvedValue(tokens);
      const send = jest.fn();
      const ctx = createContext({}, { send });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              list,
            },
          },
        },
      };

      await apiTokenController.list(ctx);

      expect(list).toHaveBeenCalled();
      expect(send).toHaveBeenCalledWith({ data: tokens });
    });
  });

  describe('Delete an API token', () => {
    const token = {
      id: 1,
      name: 'api-token_tests-name',
      description: 'api-token_tests-description',
      type: 'read-only',
    };

    test('Deletes an API token successfully', async () => {
      const revoke = jest.fn().mockResolvedValue(token);
      const deleted = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { deleted });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              revoke,
            },
          },
        },
      };

      await apiTokenController.revoke(ctx);

      expect(revoke).toHaveBeenCalledWith(token.id);
      expect(deleted).toHaveBeenCalledWith({ data: token });
    });

    test('Does not return an error if the ressource does not exists', async () => {
      const revoke = jest.fn().mockResolvedValue(null);
      const deleted = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { deleted });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              revoke,
            },
          },
        },
      };

      await apiTokenController.revoke(ctx);

      expect(revoke).toHaveBeenCalledWith(token.id);
      expect(deleted).toHaveBeenCalledWith({ data: null });
    });
  });

  describe('Retrieve an API token', () => {
    const token = {
      id: 1,
      name: 'api-token_tests-name',
      description: 'api-token_tests-description',
      type: 'read-only',
    };

    test('Retrieve an API token successfully', async () => {
      const getById = jest.fn().mockResolvedValue(token);
      const send = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { send });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              getById,
            },
          },
        },
      };

      await apiTokenController.get(ctx);

      expect(getById).toHaveBeenCalledWith(token.id);
      expect(send).toHaveBeenCalledWith({ data: token });
    });

    test('Fails if the API token does not exist', async () => {
      const getById = jest.fn().mockResolvedValue(null);
      const notFound = jest.fn();
      const ctx = createContext({ params: { id: token.id } }, { notFound });

      global.strapi = {
        admin: {
          services: {
            'api-token': {
              getById,
            },
          },
        },
      };

      await apiTokenController.get(ctx);

      expect(getById).toHaveBeenCalledWith(token.id);
      expect(notFound).toHaveBeenCalledWith('API Token not found');
    });
  });
});
<|endoftext|>


<commit_before>import { isFunction, uniqueId, remove, every, isArray, some, find, isNumber } from 'lodash'

let handlerBindings = []
const bindingKey = '__veui_outside__'

document.addEventListener('click', e => {
  handlerBindings.forEach(item => {
    item[bindingKey] && item[bindingKey].handler(e)
  })
}, true)

document.addEventListener('mousemove', e => {
  handlerBindings.forEach(item => {
    item[bindingKey] && item[bindingKey].handler(e)
  })
}, true)

function isContain (container, contained) {
  return container === contained || container.contains(contained)
}

function getElementsByRefs (refs, context) {
  const elements = []
  refs.forEach((ref) => {
    if (context.$refs[ref]) {
      let element = context.$refs[ref]
      if (element.$el) {
        elements.push(element.$el)
      } else if (isArray(element)) {
        elements.splice(elements.length - 1, 0, ...element)
      } else if (element) {
        elements.push(element)
      }
    }
  })
  return elements
}

function empty () {}

function generate (el, { whiteList, handler, trigger, delay }) {
  const hoverState = {
    state: 'ready',
    prevEvent: null,
    timer: null
  }
  function hover (e, whiteList, handler, trigger, delay) {
    if (delay === 0) {
      if (every(whiteList, element => !isContain(element, e.target))) {
        handler(e)
      }
    } else {
      if (hoverState.state === 'ready' && some(whiteList, element => isContain(element, e.target))) {
        // 如果鼠标第一次在whiteList里面动，就改一下状态
        hoverState.state = 'in'
      } else if (hoverState.state === 'in' && every(whiteList, element => !isContain(element, e.target))) {
        // 鼠标从whiteList里面移出去了，就设置一下超时
        hoverState.state = 'out'

        hoverState.timer = setTimeout(() => {
          // 超时没移回，就要触发handler了
          if (hoverState.state === 'out' && every(whiteList, element => !isContain(element, e.target))) {
            // 此处用最后一次记录的event对象
            handler(hoverState.prevEvent)
            // 重置状态
            hoverState.state = 'ready'
            hoverState.prevEvent = null
            hoverState.timer = null
          }
        }, delay)
      } else if (hoverState.state === 'out') {
        // 鼠标在外面了，就要随时检查鼠标是不是移回whiteList了
        if (some(whiteList, element => isContain(element, e.target))) {
          // 鼠标移了回来，重置一下状态
          hoverState.state = 'in'
          clearTimeout(hoverState.timer)
          hoverState.timer = null
        }
      }

      hoverState.prevEvent = e
    }
  }

  return function (e) {
    // click 模式，直接判断元素包含情况
    if (e.type === trigger && every(whiteList, element => !isContain(element, e.target))) {
      handler(e)
    }

    if (e.type === 'mousemove' && trigger === 'hover') {
      hover(e, whiteList, handler, trigger, delay)
    }
  }
}

function parseParams (el, arg, modifiers, value, context) {
  let whiteList
  let handler
  let trigger
  // delay表示如果鼠标移动到whiteList元素之外多少秒之后，才会触发handler；
  let delay

  const refs = arg ? arg.split(',') : []
  whiteList = [el, ...getElementsByRefs(refs, context)]

  handler = isFunction(value) ? value : empty

  trigger = modifiers.hover ? 'hover' : 'click'

  delay = find(Object.keys(modifiers), key => isNumber(parseInt(key, 10)) && modifiers[key])
  delay = delay ? parseInt(delay, 10) : 0

  return {
    whiteList,
    handler,
    trigger,
    delay
  }
}

export default {
  bind (el, { value, arg, modifiers }, vnode) {
    el[bindingKey] = {
      id: uniqueId(),
      handler: generate(el, parseParams(el, arg, modifiers, value, vnode.context))
    }
    handlerBindings.push(el)
  },
  update (el, { value, arg, modifiers }, vnode) {
    if (isFunction(value)) {
      el[bindingKey].handler = generate(el, parseParams(el, arg, modifiers, value, vnode.context))
    }
  },
  unbind (el) {
    remove(handlerBindings, item => item[bindingKey].id === el[bindingKey].id)
  }
}
<commit_msg>outside支持传object (#48)<commit_after>import { isFunction, uniqueId, remove, every, isArray, some, find, isNumber } from 'lodash'

let handlerBindings = []
const bindingKey = '__veui_outside__'

document.addEventListener('click', e => {
  handlerBindings.forEach(item => {
    item[bindingKey] && item[bindingKey].handler(e)
  })
}, true)

document.addEventListener('mousemove', e => {
  handlerBindings.forEach(item => {
    item[bindingKey] && item[bindingKey].handler(e)
  })
}, true)

function isContain (container, contained) {
  return container === contained || container.contains(contained)
}

function getElementsByRefs (refs, context) {
  const elements = []
  refs.forEach((ref) => {
    if (context.$refs[ref]) {
      let element = context.$refs[ref]
      if (element.$el) {
        elements.push(element.$el)
      } else if (isArray(element)) {
        elements.splice(elements.length - 1, 0, ...element)
      } else if (element) {
        elements.push(element)
      }
    }
  })
  return elements
}

function empty () {}

function generate (el, { whiteList, handler, trigger, delay }) {
  const hoverState = {
    state: 'ready',
    prevEvent: null,
    timer: null
  }
  function hover (e, whiteList, handler, trigger, delay) {
    if (delay === 0) {
      if (every(whiteList, element => !isContain(element, e.target))) {
        handler(e)
      }
    } else {
      if (hoverState.state === 'ready' && some(whiteList, element => isContain(element, e.target))) {
        // 如果鼠标第一次在whiteList里面动，就改一下状态
        hoverState.state = 'in'
      } else if (hoverState.state === 'in' && every(whiteList, element => !isContain(element, e.target))) {
        // 鼠标从whiteList里面移出去了，就设置一下超时
        hoverState.state = 'out'

        hoverState.timer = setTimeout(() => {
          // 超时没移回，就要触发handler了
          if (hoverState.state === 'out' && every(whiteList, element => !isContain(element, e.target))) {
            // 此处用最后一次记录的event对象
            handler(hoverState.prevEvent)
            // 重置状态
            hoverState.state = 'ready'
            hoverState.prevEvent = null
            hoverState.timer = null
          }
        }, delay)
      } else if (hoverState.state === 'out') {
        // 鼠标在外面了，就要随时检查鼠标是不是移回whiteList了
        if (some(whiteList, element => isContain(element, e.target))) {
          // 鼠标移了回来，重置一下状态
          hoverState.state = 'in'
          clearTimeout(hoverState.timer)
          hoverState.timer = null
        }
      }

      hoverState.prevEvent = e
    }
  }

  return function (e) {
    // click 模式，直接判断元素包含情况
    if (e.type === trigger && every(whiteList, element => !isContain(element, e.target))) {
      handler(e)
    }

    if (e.type === 'mousemove' && trigger === 'hover') {
      hover(e, whiteList, handler, trigger, delay)
    }
  }
}

function parseParams (el, arg, modifiers, value, context) {
  let whiteList
  let handler
  let trigger
  // delay表示如果鼠标移动到whiteList元素之外多少秒之后，才会触发handler；
  let delay

  // 如果value是Function的话，其余参数就尽量从modifier、arg里面去解析
  // 否则从value里面去解析
  if (isFunction(value)) {
    handler = value

    const refs = arg ? arg.split(',') : []
    whiteList = [el, ...getElementsByRefs(refs, context)]

    trigger = modifiers.hover ? 'hover' : 'click'

    delay = find(Object.keys(modifiers), key => isNumber(parseInt(key, 10)) && modifiers[key])
    delay = delay ? parseInt(delay, 10) : 0
  } else {
    const normalizedValue = value || {}
    handler = isFunction(normalizedValue.handler) ? normalizedValue.handler : empty

    const refs = isArray(normalizedValue.refs) ? normalizedValue.refs : String(normalizedValue).refs.split(',')
    whiteList = [el, ...getElementsByRefs(refs, context)]

    trigger = normalizedValue.trigger === 'hover' ? 'hover' : 'click'

    delay = parseInt(normalizedValue.delay, 10)
    if (isNaN(delay)) {
      delay = 0
    }
  }

  return {
    whiteList,
    handler,
    trigger,
    delay
  }
}

export default {
  bind (el, { value, arg, modifiers }, vnode) {
    el[bindingKey] = {
      id: uniqueId(),
      handler: generate(el, parseParams(el, arg, modifiers, value, vnode.context))
    }
    handlerBindings.push(el)
  },
  update (el, { value, arg, modifiers }, vnode) {
    if (isFunction(value)) {
      el[bindingKey].handler = generate(el, parseParams(el, arg, modifiers, value, vnode.context))
    }
  },
  unbind (el) {
    remove(handlerBindings, item => item[bindingKey].id === el[bindingKey].id)
  }
}
<|endoftext|>


<commit_before>import { cartoRequest } from 'utils/request';

const QUERY_OLD =
  "WITH pc as (SELECT * from ptw_config_table where published = TRUE), pg as ( select grid_id, st_symdifference(st_makevalid(ST_Buffer(st_centroid(the_geom_webmercator),5500, 'quad_segs=24')), st_makevalid(ST_Buffer(st_centroid(the_geom_webmercator),5100, 'quad_segs=24'))) as the_geom_webmercator from ptw_grid_may2018_score_gt_0 where grid_id in (select grid_id from pc)), ptw AS (SELECT pc.cartodb_id, pc.grid_id, pc.glad_count, pc.name, pc.LINK, pc.image, pc.image_source, pc.overlap_pa, pc.overlap_ifl, pc.description, 'ptw_grid_may2018_score_gt_0'::text AS tablename, 'ptw_top_10'::text AS LAYER, TRUE AS actions, TRUE AS analize, TRUE AS readmore, FALSE AS analysis FROM ptw_config_table pc WHERE pc.published = TRUE) SELECT ptw.*, FALSE AS marker, pg.the_geom_webmercator, st_asgeojson(ST_transform(ST_Envelope(pg.the_geom_webmercator),4326)) bbox FROM ptw INNER JOIN pg ON ptw.grid_id = pg.grid_id UNION ALL SELECT ptw.*, TRUE AS marker, St_centroid(pg.the_geom_webmercator) AS the_geom_webmercator,st_asgeojson(ST_transform(ST_Envelope(pg.the_geom_webmercator), 4326)) bbox FROM ptw INNER JOIN pg ON ptw.grid_id = pg.grid_id";
const QUERY =
  "WITH mb AS (WITH pc AS (SELECT * FROM ptw_config_table WHERE published = TRUE), pg AS ( SELECT grid_id, the_geom_webmercator FROM ptw_grid_may2018_score_gt_0 WHERE grid_id IN (SELECT grid_id FROM pc)), ptw AS ( SELECT pc.cartodb_id, pc.grid_id, pc.glad_count, pc.name, pc.LINK, pc.image, pc.image_source, pc.description, 'ptw_grid_may2018_score_gt_0' :: text AS tablename FROM ptw_config_table pc WHERE pc.published = TRUE) SELECT ptw.*, ST_buffer(St_centroid(pg.the_geom_webmercator), 5500, 'quad_segs=24') AS the_geom_webmercator, St_centroid(pg.the_geom_webmercator) AS point, St_asgeojson(St_transform(St_envelope(pg.the_geom_webmercator), 4326) ) bbox, 'mongabay' :: text AS TYPE FROM ptw inner join pg ON ptw.grid_id = pg.grid_id), soy AS (SELECT score, cartodb_id, grid_id, glad_count, description, name, NULL AS LINK, NULL AS image, NULL AS image_source, NULL AS text, ST_buffer(St_centroid(the_geom_webmercator), 5500, 'quad_segs=24') AS the_geom_webmercator, St_centroid(the_geom_webmercator) AS point, St_asgeojson(St_transform(St_envelope(the_geom_webmercator), 4326)) bbox, 'soy' :: text AS TYPE FROM soy_ptw_top_10 ORDER BY score DESC LIMIT 10 ), palm AS (SELECT score, cartodb_id, grid_id, glad_count, description, name, NULL AS LINK, NULL AS image, NULL AS image_source, NULL AS text, ST_buffer(St_centroid(the_geom_webmercator), 5500, 'quad_segs=24') AS the_geom_webmercator, St_centroid(the_geom_webmercator) AS point, St_asgeojson(St_transform(St_envelope(the_geom_webmercator), 4326)) bbox, 'palm' :: text AS TYPE FROM palm_ptw_top_10 ORDER BY score DESC LIMIT 10) SELECT * FROM mb UNION ALL SELECT cartodb_id, grid_id, glad_count, description, name, link, image, image_source, text, the_geom_webmercator, point, bbox, type FROM palm UNION ALL SELECT cartodb_id, grid_id, glad_count, description, name, link, image, image_source, text, the_geom_webmercator, point, bbox, type FROM soy";
const REQUEST_URL = `/sql?q=${
  process.env.FEATURE_ENV === 'staging' ? QUERY : QUERY_OLD
}`;

export const getPTWProvider = () => cartoRequest.get(REQUEST_URL);

export default {
  getPTWProvider
};
<commit_msg>update PTW commodities query<commit_after>import { cartoRequest } from 'utils/request';

const QUERY_OLD =
  "WITH pc as (SELECT * from ptw_config_table where published = TRUE), pg as ( select grid_id, st_symdifference(st_makevalid(ST_Buffer(st_centroid(the_geom_webmercator),5500, 'quad_segs=24')), st_makevalid(ST_Buffer(st_centroid(the_geom_webmercator),5100, 'quad_segs=24'))) as the_geom_webmercator from ptw_grid_may2018_score_gt_0 where grid_id in (select grid_id from pc)), ptw AS (SELECT pc.cartodb_id, pc.grid_id, pc.glad_count, pc.name, pc.LINK, pc.image, pc.image_source, pc.overlap_pa, pc.overlap_ifl, pc.description, 'ptw_grid_may2018_score_gt_0'::text AS tablename, 'ptw_top_10'::text AS LAYER, TRUE AS actions, TRUE AS analize, TRUE AS readmore, FALSE AS analysis FROM ptw_config_table pc WHERE pc.published = TRUE) SELECT ptw.*, FALSE AS marker, pg.the_geom_webmercator, st_asgeojson(ST_transform(ST_Envelope(pg.the_geom_webmercator),4326)) bbox FROM ptw INNER JOIN pg ON ptw.grid_id = pg.grid_id UNION ALL SELECT ptw.*, TRUE AS marker, St_centroid(pg.the_geom_webmercator) AS the_geom_webmercator,st_asgeojson(ST_transform(ST_Envelope(pg.the_geom_webmercator), 4326)) bbox FROM ptw INNER JOIN pg ON ptw.grid_id = pg.grid_id";
const QUERY =
  "with s AS (SELECT * FROM ptw_soy_grid), stt AS (SELECT * FROM soy_ptw_top_10), p AS (SELECT * FROM ptw_palm_grid), ptt AS (SELECT * FROM palm_ptw_top_10), pc AS (SELECT * FROM ptw_config_table WHERE published = TRUE), pg AS ( SELECT grid_id, the_geom_webmercator FROM ptw_grid_may2018_score_gt_0 WHERE grid_id IN (SELECT grid_id FROM pc)), ptw AS ( SELECT pc.cartodb_id, pc.grid_id, pc.description, pc.glad_count, pc.name, pc.LINK, pc.image, pc.image_source, 'ptw_grid_may2018_score_gt_0' :: text AS tablename FROM ptw_config_table pc WHERE pc.published = TRUE), mb AS (SELECT ptw.*, ST_buffer(St_centroid(pg.the_geom_webmercator), 5500, 'quad_segs=24') AS the_geom_webmercator, St_centroid(pg.the_geom_webmercator) AS point, St_asgeojson(St_transform(St_envelope(pg.the_geom_webmercator), 4326) ) bbox, 'mongabay' :: text AS TYPE FROM ptw inner join pg ON ptw.grid_id = pg.grid_id), soy AS (SELECT s.cartodb_id, s.grid_id, stt.description, stt.glad_count, stt.name, NULL AS LINK, NULL AS image, NULL AS image_source, 'soy_ptw_top_10' :: text AS tablename,        ST_buffer(St_centroid(s.the_geom_webmercator), 5500, 'quad_segs=24') AS the_geom_webmercator, St_centroid(s.the_geom_webmercator) AS point,  St_asgeojson(St_transform(St_envelope(s.the_geom_webmercator), 4326)) AS bbox, 'soy' :: text AS TYPE FROM s inner join stt ON s.grid_id = stt.grid_id), palm AS (SELECT p.cartodb_id, p.grid_id, ptt.description, ptt.glad_count, ptt.name, NULL AS LINK, NULL AS image, NULL AS image_source, 'palm_ptw_top_10' :: text AS tablename, ST_buffer(St_centroid(p.the_geom_webmercator), 5500, 'quad_segs=24') AS the_geom_webmercator, St_centroid(p.the_geom_webmercator) AS point, St_asgeojson(St_transform(St_envelope(p.the_geom_webmercator), 4326)) AS bbox, 'palm' :: text AS TYPE FROM p inner join ptt ON p.grid_id = ptt.grid_id) SELECT * FROM mb UNION ALL SELECT * FROM soy UNION ALL SELECT * FROM palm";
const REQUEST_URL = `/sql?q=${
  process.env.FEATURE_ENV === 'staging' ? QUERY : QUERY_OLD
}`;

export const getPTWProvider = () => cartoRequest.get(REQUEST_URL);

export default {
  getPTWProvider
};
<|endoftext|>


<commit_before>/**
 * @file
 * Functionality related to installing Kalabox.
 */

// Requires
var fs = require('fs'),
    url = require('url'),
    http = require('http'),
    https = require('https'),
    exec = require('child_process').exec,
    spawn = require('child_process').spawn,
    logger = require('../../logger');

// State variables:
var vboxVersion,
    vagrantVersion;

/**
 * Downloads a file from the Internet.
 *
 * @param  string file_url
 *   URL to download from.
 * @param  string destination
 *   Directory on the file system to save file to (ending with "/").
 * @param  function callback
 *   Callback to pass progress to.
 */
exports.downloadFile = function(file_url, destination, callback) {
  // Get URL details from the full URL.
  var parsedUrl = url.parse(file_url);
  var options = {
    host : parsedUrl.host,
    port : 80,
    path : parsedUrl.pathname
  };
  var file_name = parsedUrl.pathname.split('/').pop();

  // Determine if we're downloading over http or https.
  var httpInterface = http;
  if (parsedUrl.protocol == 'https:') {
    httpInterface = https;
    options.port = 443;
  }

  // Create stream and start the download.
  var file = fs.createWriteStream(destination + file_name);
  httpInterface.get(options, function(res) {
    var filesize = res.headers['content-length'];
    var downloaded = 0;
    var done = 0;
    res.on('data', function(data) {
      file.write(data, function() {
        downloaded = downloaded + data.length;
        done = (downloaded / filesize) * 100;
        callback(null, done);
      });
    }).on('end', function(data) {
      file.end(function() {
        console.log(file_name + ' downloaded to ' + destination);
        callback(null, 100);
      });
    });
  }).on('error', function(error) {
    callback(error, 100);
  });
};

/**
 * Checks if VirtualBox is installed.
 *
 * @param  function callback
 *   Callback to pass an output array with version if installed, or false if not.
 */
exports.checkVBox = function(callback) {
  if (vboxVersion) {
    return vboxVersion;
  }
  // e.g. "4.2.8r83876"
  exec('VBoxManage -v', function(error, stdout, stderr) {
    if (error !== null) {
      callback(false);
    }
    else {
      vboxVersion = stdout;
      callback(stdout);
    }
  });
};

/**
 * Checks if Vagrant is installed.
 *
 * @param  function callback
 *   Callback to pass an output array with version if installed, or false if not.
 */
exports.checkVagrant = function(callback) {
  if (vagrantVersion) {
    return vagrantVersion;
  }
  // e.g. "Vagrant version 1.0.3"
  exec('vagrant --version', function(error, stdout, stderr) {
    if (error !== null) {
      callback(false);
    }
    else {
      vagrantVersion = stdout;
      callback(stdout);
    }
  });
};

/**
 * Check if we have access to the internet
 * @param  function callback
 *   Callback to pass an output true if Google resolves, or false if not.
 */
exports.checkInternet = function(callback) {
  require('dns').resolve('www.google.com', function(err) {
    if (err !== null) {
      callback(false);
    }
    else {
      callback(true);
    }
  });
};

/**
 * Compares two version strings.
 *
 * Based on: http://jsfiddle.net/Xv9WL/16/
 * and http://jsfiddle.net/ripper234/Xv9WL/28/
 *
 * @param  string v1
 *   First version string.
 * @param  string v2
 *   Second version string.
 * @return int
 *   -1 if v1 < v2; 0 if v1 == v2; 1 if v1 > v2
 */
exports.compareVersions = function(v1, v2) {
  var v1parts = v1.split('.');
  var v2parts = v2.split('.');

  for (var i = 0; i < v1parts.length; ++i) {
    if (v2parts.length == i) {
      return 1;
    }
    if (v1parts[i] == v2parts[i]) {
      continue;
    }
    else if (v1parts[i] > v2parts[i]) {
      return 1;
    }
    else {
      return -1;
    }
  }

  if (v1parts.length != v2parts.length) {
    return -1;
  }

  return 0;
};
<commit_msg>Update install-utils.js to make sure we don't redownload vagrant and virtualbox DMGs if they're already downloaded.<commit_after>/**
 * @file
 * Functionality related to installing Kalabox.
 */

// Requires
var fs = require('fs'),
    url = require('url'),
    http = require('http'),
    https = require('https'),
    exec = require('child_process').exec,
    spawn = require('child_process').spawn,
    flow = require('nue').flow,
    as = require('nue').as,
    logger = require('../../logger');

// State variables:
var vboxVersion,
    vagrantVersion;

/**
 * Downloads a file from the Internet.
 *
 * @param  string file_url
 *   URL to download from.
 * @param  string destination
 *   Directory on the file system to save file to (ending with "/").
 * @param  function callback
 *   Callback to pass progress to.
 */
exports.downloadFile = flow('writeFile')(
  // Check if there is already a file at the destination location
  function writeFile0(file_url, destination, callback) {
    // Get URL details from the full URL.
    var parsedUrl = url.parse(file_url);
    this.data.options = {
      host : parsedUrl.host,
      port : 80,
      path : parsedUrl.pathname
    };
    this.data.file_name = parsedUrl.pathname.split('/').pop();

    // Determine if we're downloading over http or https.
    this.data.httpInterface = http;
    if (parsedUrl.protocol == 'https:') {
      this.data.httpInterface = https;
      this.data.options.port = 443;
    }
    this.data.destination = destination;
    this.data.callback = callback;

    fs.exists(this.data.destination + this.data.file_name, this.async(as(0)));
  },
  // Create stream and start the download.
  function writeFile1(exists){
    if(exists){
      console.log('File already downloaded');
      this.end();
      return;
    }

    this.data.file = fs.createWriteStream(this.data.destination + this.data.file_name);
    this.data.httpInterface.get(this.data.options, this.async(as(0))).on('error', function(error) {
      this.data.callback(error, 100);
    });
  },
  // Report on the downloading progress
  function writeFile2(res) {
    var filesize = res.headers['content-length'];
    var downloaded = 0;
    var done = 0;
    var that = this;
    res.on('data', function(data) {
      that.data.file.write(data, function() {
        downloaded = downloaded + data.length;
        done = (downloaded / filesize) * 100;
        that.data.callback(null, done);
      });
    }).on('end', this.async(as(0)));
  },
  function writeFile3(data) {
    this.data.file.end(this.async());
  },
  function writeFileEnd() {
    if (this.err) {
      this.data.callback({message: this.err.message});
      this.err = null;
      return;
    }

    console.log(this.data.file_name + ' downloaded to ' + this.data.destination);
    this.data.callback(null, 100);
  }
);

/**
 * Checks if VirtualBox is installed.
 *
 * @param  function callback
 *   Callback to pass an output array with version if installed, or false if not.
 */
exports.checkVBox = function(callback) {
  if (vboxVersion) {
    return vboxVersion;
  }
  // e.g. "4.2.8r83876"
  exec('VBoxManage -v', function(error, stdout, stderr) {
    if (error !== null) {
      callback(false);
    }
    else {
      vboxVersion = stdout;
      callback(stdout);
    }
  });
};

/**
 * Checks if Vagrant is installed.
 *
 * @param  function callback
 *   Callback to pass an output array with version if installed, or false if not.
 */
exports.checkVagrant = function(callback) {
  if (vagrantVersion) {
    return vagrantVersion;
  }
  // e.g. "Vagrant version 1.0.3"
  exec('vagrant --version', function(error, stdout, stderr) {
    if (error !== null) {
      callback(false);
    }
    else {
      vagrantVersion = stdout;
      callback(stdout);
    }
  });
};

/**
 * Check if we have access to the internet
 * @param  function callback
 *   Callback to pass an output true if Google resolves, or false if not.
 */
exports.checkInternet = function(callback) {
  require('dns').resolve('www.google.com', function(err) {
    if (err !== null) {
      callback(false);
    }
    else {
      callback(true);
    }
  });
};

/**
 * Compares two version strings.
 *
 * Based on: http://jsfiddle.net/Xv9WL/16/
 * and http://jsfiddle.net/ripper234/Xv9WL/28/
 *
 * @param  string v1
 *   First version string.
 * @param  string v2
 *   Second version string.
 * @return int
 *   -1 if v1 < v2; 0 if v1 == v2; 1 if v1 > v2
 */
exports.compareVersions = function(v1, v2) {
  var v1parts = v1.split('.');
  var v2parts = v2.split('.');

  for (var i = 0; i < v1parts.length; ++i) {
    if (v2parts.length == i) {
      return 1;
    }
    if (v1parts[i] == v2parts[i]) {
      continue;
    }
    else if (v1parts[i] > v2parts[i]) {
      return 1;
    }
    else {
      return -1;
    }
  }

  if (v1parts.length != v2parts.length) {
    return -1;
  }

  return 0;
};
<|endoftext|>


<commit_before>/** *
 * Jobs of this module
 * - Maintain the list of components in the Redux store. So monitor new components
 *   and add/remove components.
 * - Watch components for query changes and extract these and update the store.
 * - Ensure all page queries are run as part of bootstrap and report back when
 *   this is done
 * - Whenever a query changes, re-run all pages that rely on this query.
 ***/

const _ = require(`lodash`)
const chokidar = require(`chokidar`)
const path = require(`path`)
const slash = require(`slash`)

const { store, emitter } = require(`../../redux/`)
const { boundActionCreators } = require(`../../redux/actions`)
const queryCompiler = require(`./query-compiler`).default
const report = require(`gatsby-cli/lib/reporter`)
const {
  queueQueryForPathname,
  runQueuedActions: runQueuedQueries,
} = require(`./page-query-runner`)
const debug = require(`debug`)(`gatsby:query-watcher`)

const getQueriesSnapshot = () => {
  const state = store.getState()

  const snapshot = {
    components: new Map(state.components),
    staticQueryComponents: new Map(state.staticQueryComponents),
  }

  return snapshot
}

const handleComponentsWithRemovedQueries = (
  { components, staticQueryComponents },
  queries
) => {
  // If a component previously with a query now doesn't — update the
  // store.
  components.forEach(c => {
    if (c.query !== `` && !queries.has(c.componentPath)) {
      debug(`Page query was removed from ${c.componentPath}`)
      boundActionCreators.replaceComponentQuery({
        query: ``,
        componentPath: c.componentPath,
      })
      queueQueriesForPageComponent(c.componentPath)
    }
  })

  // If a component had static query and it doesn't have it
  // anymore - update the store
  staticQueryComponents.forEach(c => {
    if (c.query !== `` && !queries.has(c.componentPath)) {
      debug(`Static query was removed from ${c.componentPath}`)
      store.dispatch({
        type: `REMOVE_STATIC_QUERY`,
        payload: c.jsonName,
      })
      boundActionCreators.deleteComponentsDependencies([c.jsonName])
    }
  })
}

const handleQuery = (
  { components, staticQueryComponents },
  query,
  component
) => {
  // If this is a static query
  // Add action / reducer + watch staticquery files
  if (query.isStaticQuery) {
    const isNewQuery = !staticQueryComponents.has(query.jsonName)
    if (
      isNewQuery ||
      staticQueryComponents.get(query.jsonName).query !== query.text
    ) {
      boundActionCreators.replaceStaticQuery({
        name: query.name,
        componentPath: query.path,
        id: query.jsonName,
        jsonName: query.jsonName,
        query: query.text,
        hash: query.hash,
      })

      debug(
        `Static query in ${component} ${
          isNewQuery ? `was added` : `has changed`
        }.`
      )

      boundActionCreators.deleteComponentsDependencies([query.jsonName])
      queueQueryForPathname(query.jsonName)
    }
    return true

    // If this is page query
  } else if (components.has(component)) {
    if (components.get(component).query !== query.text) {
      boundActionCreators.replaceComponentQuery({
        query: query.text,
        componentPath: component,
      })

      debug(
        `Page query in ${component} ${
          components.get(component).query.length === 0
            ? `was added`
            : `has changed`
        }.`
      )
      queueQueriesForPageComponent(component)
    }
    return true
  }

  return false
}

const updateStateAndRunQueries = isFirstRun => {
  const snapshot = getQueriesSnapshot()
  return queryCompiler().then(queries => {
    handleComponentsWithRemovedQueries(snapshot, queries)

    let queriesWillNotRun = false
    queries.forEach((query, component) => {
      const queryWillRun = handleQuery(snapshot, query, component)

      if (queryWillRun) {
        watchComponent(component)
      } else if (isFirstRun) {
        report.warn(
          `The GraphQL query in the non-page component "${component}" will not be run.`
        )
        queriesWillNotRun = true
      }
    })

    if (queriesWillNotRun) {
      report.log(report.stripIndent`
        Exported queries are only executed for Page components. Instead of an exported
        query, either co-locate a GraphQL fragment and compose that fragment into the
        query (or other fragment) of the top-level page that renders this component, or
        use a <StaticQuery> in this component. For more info on fragments and
        composition, see http://graphql.org/learn/queries/#fragments and for more
        information on <StaticQuery>, see https://next.gatsbyjs.org/docs/static-query
      `)
    }
    runQueuedQueries()
  })
}

/**
 * Removes components templates that aren't used by any page from redux store.
 */
const clearInactiveComponents = () => {
  const { components, pages } = store.getState()

  const activeTemplates = new Set()
  pages.forEach(page => {
    // Set will guarantee uniqeness of entires
    activeTemplates.add(slash(page.component))
  })

  components.forEach(component => {
    if (!activeTemplates.has(component.componentPath)) {
      debug(
        `${
          component.componentPath
        } component was removed because it isn't used by any page`
      )
      store.dispatch({
        type: `REMOVE_TEMPLATE_COMPONENT`,
        payload: component,
      })
    }
  })
}

exports.extractQueries = () => {
  // Remove template components that point to not existing page templates.
  // We need to do this, because components data is cached and there might
  // be changes applied when development server isn't running. This is needed
  // only in initial run, because during development state will be adjusted.
  clearInactiveComponents()

  return updateStateAndRunQueries(true).then(() => {
    // During development start watching files to recompile & run
    // queries on the fly.
    if (process.env.NODE_ENV !== `production`) {
      watch(store.getState().program.directory)
    }
  })
}

const queueQueriesForPageComponent = componentPath => {
  const pages = getPagesForComponent(componentPath)
  // Remove page data dependencies before re-running queries because
  // the changing of the query could have changed the data dependencies.
  // Re-running the queries will add back data dependencies.
  boundActionCreators.deleteComponentsDependencies(
    pages.map(p => p.path || p.id)
  )
  pages.forEach(page => queueQueryForPathname(page.path))
}

const getPagesForComponent = componentPath => {
  const state = store.getState()
  return [...state.pages.values()].filter(
    p => p.componentPath === componentPath
  )
}

const filesToWatch = new Set()
let watcher
const watchComponent = componentPath => {
  // We don't start watching until mid-way through the bootstrap so ignore
  // new components being added until then. This doesn't affect anything as
  // when extractQueries is called from bootstrap, we make sure that all
  // components are being watched.
  if (
    process.env.NODE_ENV !== `production` &&
    !filesToWatch.has(componentPath)
  ) {
    filesToWatch.add(componentPath)
    if (watcher) {
      watcher.add(componentPath)
    }
  }
}

exports.watchComponent = watchComponent

const watch = rootDir => {
  if (watcher) return
  const debounceCompile = _.debounce(() => {
    updateStateAndRunQueries()
  }, 100)

  watcher = chokidar
    .watch(slash(path.join(rootDir, `/src/**/*.{js,jsx,ts,tsx}`)))
    .on(`change`, path => {
      debounceCompile()
    })
  filesToWatch.forEach(filePath => watcher.add(filePath))
}

if (process.env.gatsby_executing_command === `develop`) {
  let bootstrapFinished = false
  emitter.on(`BOOTSTRAP_FINISHED`, () => {
    bootstrapFinished = true
  })
  emitter.on(`DELETE_PAGE`, action => {
    if (bootstrapFinished) {
      const componentPath = slash(action.payload.component)
      const { pages } = store.getState()
      let otherPageWithTemplateExists = false
      for (let page of pages.values()) {
        if (slash(page.component) === componentPath) {
          otherPageWithTemplateExists = true
          break
        }
      }
      if (!otherPageWithTemplateExists) {
        store.dispatch({
          type: `REMOVE_TEMPLATE_COMPONENT`,
          payload: {
            componentPath,
          },
        })
      }
    }
  })
}
<commit_msg>compare both query text and hash when checking if we need to rerun StaticQuery (#7846)<commit_after>/** *
 * Jobs of this module
 * - Maintain the list of components in the Redux store. So monitor new components
 *   and add/remove components.
 * - Watch components for query changes and extract these and update the store.
 * - Ensure all page queries are run as part of bootstrap and report back when
 *   this is done
 * - Whenever a query changes, re-run all pages that rely on this query.
 ***/

const _ = require(`lodash`)
const chokidar = require(`chokidar`)
const path = require(`path`)
const slash = require(`slash`)

const { store, emitter } = require(`../../redux/`)
const { boundActionCreators } = require(`../../redux/actions`)
const queryCompiler = require(`./query-compiler`).default
const report = require(`gatsby-cli/lib/reporter`)
const {
  queueQueryForPathname,
  runQueuedActions: runQueuedQueries,
} = require(`./page-query-runner`)
const debug = require(`debug`)(`gatsby:query-watcher`)

const getQueriesSnapshot = () => {
  const state = store.getState()

  const snapshot = {
    components: new Map(state.components),
    staticQueryComponents: new Map(state.staticQueryComponents),
  }

  return snapshot
}

const handleComponentsWithRemovedQueries = (
  { components, staticQueryComponents },
  queries
) => {
  // If a component previously with a query now doesn't — update the
  // store.
  components.forEach(c => {
    if (c.query !== `` && !queries.has(c.componentPath)) {
      debug(`Page query was removed from ${c.componentPath}`)
      boundActionCreators.replaceComponentQuery({
        query: ``,
        componentPath: c.componentPath,
      })
      queueQueriesForPageComponent(c.componentPath)
    }
  })

  // If a component had static query and it doesn't have it
  // anymore - update the store
  staticQueryComponents.forEach(c => {
    if (c.query !== `` && !queries.has(c.componentPath)) {
      debug(`Static query was removed from ${c.componentPath}`)
      store.dispatch({
        type: `REMOVE_STATIC_QUERY`,
        payload: c.jsonName,
      })
      boundActionCreators.deleteComponentsDependencies([c.jsonName])
    }
  })
}

const handleQuery = (
  { components, staticQueryComponents },
  query,
  component
) => {
  // If this is a static query
  // Add action / reducer + watch staticquery files
  if (query.isStaticQuery) {
    const oldQuery = staticQueryComponents.get(query.jsonName)
    const isNewQuery = !oldQuery

    // Compare query text because text is compiled query with any attached
    // fragments and we want to rerun queries if fragments are edited.
    // Compare hash because hash is used for identyfing query and
    // passing data to component in development. Hash can change if user will
    // format query text, but it doesn't mean that compiled text will change.
    if (
      isNewQuery ||
      oldQuery.hash !== query.hash ||
      oldQuery.text !== query.text
    ) {
      boundActionCreators.replaceStaticQuery({
        name: query.name,
        componentPath: query.path,
        id: query.jsonName,
        jsonName: query.jsonName,
        query: query.text,
        hash: query.hash,
      })

      debug(
        `Static query in ${component} ${
          isNewQuery ? `was added` : `has changed`
        }.`
      )

      boundActionCreators.deleteComponentsDependencies([query.jsonName])
      queueQueryForPathname(query.jsonName)
    }
    return true

    // If this is page query
  } else if (components.has(component)) {
    if (components.get(component).query !== query.text) {
      boundActionCreators.replaceComponentQuery({
        query: query.text,
        componentPath: component,
      })

      debug(
        `Page query in ${component} ${
          components.get(component).query.length === 0
            ? `was added`
            : `has changed`
        }.`
      )
      queueQueriesForPageComponent(component)
    }
    return true
  }

  return false
}

const updateStateAndRunQueries = isFirstRun => {
  const snapshot = getQueriesSnapshot()
  return queryCompiler().then(queries => {
    handleComponentsWithRemovedQueries(snapshot, queries)

    let queriesWillNotRun = false
    queries.forEach((query, component) => {
      const queryWillRun = handleQuery(snapshot, query, component)

      if (queryWillRun) {
        watchComponent(component)
      } else if (isFirstRun) {
        report.warn(
          `The GraphQL query in the non-page component "${component}" will not be run.`
        )
        queriesWillNotRun = true
      }
    })

    if (queriesWillNotRun) {
      report.log(report.stripIndent`
        Exported queries are only executed for Page components. Instead of an exported
        query, either co-locate a GraphQL fragment and compose that fragment into the
        query (or other fragment) of the top-level page that renders this component, or
        use a <StaticQuery> in this component. For more info on fragments and
        composition, see http://graphql.org/learn/queries/#fragments and for more
        information on <StaticQuery>, see https://next.gatsbyjs.org/docs/static-query
      `)
    }
    runQueuedQueries()
  })
}

/**
 * Removes components templates that aren't used by any page from redux store.
 */
const clearInactiveComponents = () => {
  const { components, pages } = store.getState()

  const activeTemplates = new Set()
  pages.forEach(page => {
    // Set will guarantee uniqeness of entires
    activeTemplates.add(slash(page.component))
  })

  components.forEach(component => {
    if (!activeTemplates.has(component.componentPath)) {
      debug(
        `${
          component.componentPath
        } component was removed because it isn't used by any page`
      )
      store.dispatch({
        type: `REMOVE_TEMPLATE_COMPONENT`,
        payload: component,
      })
    }
  })
}

exports.extractQueries = () => {
  // Remove template components that point to not existing page templates.
  // We need to do this, because components data is cached and there might
  // be changes applied when development server isn't running. This is needed
  // only in initial run, because during development state will be adjusted.
  clearInactiveComponents()

  return updateStateAndRunQueries(true).then(() => {
    // During development start watching files to recompile & run
    // queries on the fly.
    if (process.env.NODE_ENV !== `production`) {
      watch(store.getState().program.directory)
    }
  })
}

const queueQueriesForPageComponent = componentPath => {
  const pages = getPagesForComponent(componentPath)
  // Remove page data dependencies before re-running queries because
  // the changing of the query could have changed the data dependencies.
  // Re-running the queries will add back data dependencies.
  boundActionCreators.deleteComponentsDependencies(
    pages.map(p => p.path || p.id)
  )
  pages.forEach(page => queueQueryForPathname(page.path))
}

const getPagesForComponent = componentPath => {
  const state = store.getState()
  return [...state.pages.values()].filter(
    p => p.componentPath === componentPath
  )
}

const filesToWatch = new Set()
let watcher
const watchComponent = componentPath => {
  // We don't start watching until mid-way through the bootstrap so ignore
  // new components being added until then. This doesn't affect anything as
  // when extractQueries is called from bootstrap, we make sure that all
  // components are being watched.
  if (
    process.env.NODE_ENV !== `production` &&
    !filesToWatch.has(componentPath)
  ) {
    filesToWatch.add(componentPath)
    if (watcher) {
      watcher.add(componentPath)
    }
  }
}

exports.watchComponent = watchComponent

const watch = rootDir => {
  if (watcher) return
  const debounceCompile = _.debounce(() => {
    updateStateAndRunQueries()
  }, 100)

  watcher = chokidar
    .watch(slash(path.join(rootDir, `/src/**/*.{js,jsx,ts,tsx}`)))
    .on(`change`, path => {
      debounceCompile()
    })
  filesToWatch.forEach(filePath => watcher.add(filePath))
}

if (process.env.gatsby_executing_command === `develop`) {
  let bootstrapFinished = false
  emitter.on(`BOOTSTRAP_FINISHED`, () => {
    bootstrapFinished = true
  })
  emitter.on(`DELETE_PAGE`, action => {
    if (bootstrapFinished) {
      const componentPath = slash(action.payload.component)
      const { pages } = store.getState()
      let otherPageWithTemplateExists = false
      for (let page of pages.values()) {
        if (slash(page.component) === componentPath) {
          otherPageWithTemplateExists = true
          break
        }
      }
      if (!otherPageWithTemplateExists) {
        store.dispatch({
          type: `REMOVE_TEMPLATE_COMPONENT`,
          payload: {
            componentPath,
          },
        })
      }
    }
  })
}
<|endoftext|>


<commit_before>const Tooltip = require('tether-tooltip');

const positions = [
  'top',
  'left',
  'right',
  'bottom',
  'top-left',
  'left-top',
  'left-middle',
  'left-bottom',
  'bottom-left',
  'bottom-center',
  'bottom-right',
  'right-bottom',
  'right-middle',
  'right-top',
  'top-right',
  'top-center',
];

function getPosition(el, modifiers) {
  let position = false;
  for (const pos of positions) {
    if (modifiers[pos]) {
      position = pos;
      break;
    }
  }

  if (!position) {
    if (el.dataset.placement) {
      position = el.dataset.placement;
    } else {
      position = 'top center';
    }
  }

  position = position.replace('-', ' ');

  if (position.indexOf(' ') < 0) {
    if (position === 'top' || position === 'bottom') {
      position = `${position} center`;
    } else {
      position = `${position} middle`;
    }
  }

  return position;
}

function createTooltip (el, value, modifiers) {
  const position = getPosition(el, modifiers);

  let classes = (/^(top|bottom|left|right)(?:$| )/).exec(position);
  console.log(position);
  classes = classes ? classes[1] : '';

  el._tooltip = new Tooltip({
    target: el,
    position,
    content: value,
    classes,
  });

  const arrow = document.createElement('DIV');
  arrow.className = 'tooltip-arrow';

  el._tooltip.drop.drop.insertBefore(arrow, el._tooltip.drop.content);
  el._tooltip.drop.content.className = 'tooltip-inner';
}

function destroyTooltip (el) {
  if (el._tooltip) {
    el._tooltip.destroy();
    Reflect.deleteProperty(el, '_tooltip');
  }
}

export default {
  name: 'tooltip',
  bind (el, {value, modifiers}) {
    destroyTooltip(el);
    if (value) {
      createTooltip(el, value, modifiers);
    }
  },
  update (el, {value, modifiers}) {
    if (!value) {
      destroyTooltip(el);
    } else if (el && el._tooltip) {
      const position = getPosition(el, modifiers);
      if (el._tooltip.options.position == position) {
        el._tooltip.drop.content.innerHTML = value;
      } else {
        destroyTooltip(el);
        createTooltip(el, value, modifiers);
      }
    } else {
      createTooltip(el, value, modifiers);
    }
  },
  unbind (el) {
    destroyTooltip(el);
  },
};
<commit_msg>Removed debugging statement<commit_after>const Tooltip = require('tether-tooltip');

const positions = [
  'top',
  'left',
  'right',
  'bottom',
  'top-left',
  'left-top',
  'left-middle',
  'left-bottom',
  'bottom-left',
  'bottom-center',
  'bottom-right',
  'right-bottom',
  'right-middle',
  'right-top',
  'top-right',
  'top-center',
];

function getPosition(el, modifiers) {
  let position = false;
  for (const pos of positions) {
    if (modifiers[pos]) {
      position = pos;
      break;
    }
  }

  if (!position) {
    if (el.dataset.placement) {
      position = el.dataset.placement;
    } else {
      position = 'top center';
    }
  }

  position = position.replace('-', ' ');

  if (position.indexOf(' ') < 0) {
    if (position === 'top' || position === 'bottom') {
      position = `${position} center`;
    } else {
      position = `${position} middle`;
    }
  }

  return position;
}

function createTooltip (el, value, modifiers) {
  const position = getPosition(el, modifiers);

  let classes = (/^(top|bottom|left|right)(?:$| )/).exec(position);
  classes = classes ? classes[1] : '';

  el._tooltip = new Tooltip({
    target: el,
    position,
    content: value,
    classes,
  });

  const arrow = document.createElement('DIV');
  arrow.className = 'tooltip-arrow';

  el._tooltip.drop.drop.insertBefore(arrow, el._tooltip.drop.content);
  el._tooltip.drop.content.className = 'tooltip-inner';
}

function destroyTooltip (el) {
  if (el._tooltip) {
    el._tooltip.destroy();
    Reflect.deleteProperty(el, '_tooltip');
  }
}

export default {
  name: 'tooltip',
  bind (el, {value, modifiers}) {
    destroyTooltip(el);
    if (value) {
      createTooltip(el, value, modifiers);
    }
  },
  update (el, {value, modifiers}) {
    if (!value) {
      destroyTooltip(el);
    } else if (el && el._tooltip) {
      const position = getPosition(el, modifiers);
      if (el._tooltip.options.position == position) {
        el._tooltip.drop.content.innerHTML = value;
      } else {
        destroyTooltip(el);
        createTooltip(el, value, modifiers);
      }
    } else {
      createTooltip(el, value, modifiers);
    }
  },
  unbind (el) {
    destroyTooltip(el);
  },
};
<|endoftext|>


<commit_before>'use strict';

app.factory('TwitterUserFactory', ['$rootScope', function($rootScope) {

	var users = {};
	var url = '/1.1/followers/ids.json'

	users.getFollowers = function() {
		$rootScope.twitterOAuthResult.get(url).success(function(response) {
			debugger;
		});
	};

	return users;

}])<commit_msg>set scope.followers to the returned response array<commit_after>'use strict';

app.factory('TwitterUserFactory', ['$rootScope', '$scope', function($rootScope, $scope) {

	var users = {};
	var url = '/1.1/followers/list.json'

	users.getFollowers = function() {
		$rootScope.twitterOAuthResult.get(url).success(function(response) {
			$scope.followers = response.users;
		});
	};

	return users;

}])<|endoftext|>


<commit_before>(function(µ,SMOD,GMOD,HMOD,SC){

	var TableConfig=GMOD("gui.TableConfig")

	SC=SC({
		Node:"NodePatch"
	});

	var TREE=µ.gui.TreeTableConfig=µ.Class(TableConfig,{

		//init see {@link TableConfig}

		setOptions:function(options)
		{
			this.mega(options);
			if(options&&"childrenGetter" in options) this.options.childrenGetter=options.childrenGetter;
			return this;
		},
		getRows:function(data,callback)
		{
			return data.map(root=>SC.Node.traverse(root,(node,parent,parentResult,entry)=>
			{
				var row=this.getRow(node,entry.depth);

				if(callback)callback.call(row,row,node,this);

				var result={};
				if(!parentResult)
				{
					result.fragment=document.createDocumentFragment();
					result.fragment.appendChild(row);
				}
				else
				{
					parentResult.addChildRow(row);
				}

				result.addChildRow=function(childRow)
				{
					row.treeChildren.push(childRow);

					if(row.treeChildren.length==1)
					{//first time
						row.classList.add("collapsed");
					}
				};

				return result;
			},this.options.childrenGetter).fragment);
		},
		getRow:function(data,depth=0)
		{
			var row=document.createElement(this.options.body.rowTag);

			this.fillRow(data,row);

			var firstCell=row.firstElementChild;
			var firstCellChild=firstCell.firstChild;

			var indent=document.createElement("span");
			indent.classList.add("indent");
			indent.innerHTML=''.repeat(depth);
			firstCell.insertBefore(indent,firstCellChild);

			var toggler=document.createElement("span");
			toggler.classList.add("toggler");
			firstCell.insertBefore(toggler,firstCellChild);

			toggler.addEventListener("click",function(event)
			{
				row.expand(null,event.ctrlKey);
				event.stopPropagation();
				event.preventDefault();
			},false);

			row.treeChildren=[];

			var insertHelper=document.createDocumentFragment();
			row.expand=function(state,all)
			{
				if(this.treeChildren.length>0&&(state==null||state===!this.classList.contains("expanded")))
				{
					if(row.classList.toggle("collapsed",!row.classList.toggle("expanded")))
					{
						for(var child of row.treeChildren) child.remove();
					}
					else
					{
						for(var child of row.treeChildren) insertHelper.appendChild(child);
						row.parentNode.insertBefore(insertHelper,row.nextSibling);
					}

					if(all)
					{
						for(var child of row.treeChildren) child.expand(state,true);
					}
				}
			};

			return row;
		},
		getTable:function(data,headerCallback,rowCallback)
		{
			var table=this.mega(data,headerCallback,rowCallback);
			table.classList.add("Tree");
			return table;
		}
	});
	SMOD("gui.TreeTableConfig",TREE);

})(Morgas,Morgas.setModule,Morgas.getModule,Morgas.hasModule,Morgas.shortcut);
<commit_msg>fix expanding<commit_after>(function(µ,SMOD,GMOD,HMOD,SC){

	var TableConfig=GMOD("gui.TableConfig")

	SC=SC({
		Node:"NodePatch"
	});

	var TREE=µ.gui.TreeTableConfig=µ.Class(TableConfig,{

		//init see {@link TableConfig}

		setOptions:function(options)
		{
			this.mega(options);
			if(options&&"childrenGetter" in options) this.options.childrenGetter=options.childrenGetter;
			return this;
		},
		getRows:function(data,callback)
		{
			return data.map(root=>SC.Node.traverse(root,(node,parent,parentResult,entry)=>
			{
				var row=this.getRow(node,entry.depth);

				if(callback)callback.call(row,row,node,this);

				var result={};
				if(!parentResult)
				{
					result.fragment=document.createDocumentFragment();
					result.fragment.appendChild(row);
				}
				else
				{
					parentResult.addChildRow(row);
				}

				result.addChildRow=function(childRow)
				{
					row.treeChildren.push(childRow);

					if(row.treeChildren.length==1)
					{//first time
						row.classList.add("collapsed");
					}
				};

				return result;
			},this.options.childrenGetter).fragment);
		},
		getRow:function(data,depth=0)
		{
			var row=document.createElement(this.options.body.rowTag);

			this.fillRow(data,row);

			var firstCell=row.firstElementChild;
			var firstCellChild=firstCell.firstChild;

			var indent=document.createElement("span");
			indent.classList.add("indent");
			indent.innerHTML=''.repeat(depth);
			firstCell.insertBefore(indent,firstCellChild);

			var toggler=document.createElement("span");
			toggler.classList.add("toggler");
			firstCell.insertBefore(toggler,firstCellChild);

			toggler.addEventListener("click",function(event)
			{
				row.expand(null,event.ctrlKey);
				event.stopPropagation();
				event.preventDefault();
			},false);

			row.treeChildren=[];

			var insertHelper=document.createDocumentFragment();
			row.isExpanded=row.classList.contains.bind(row.classList,"expanded");
			row.expand=function(state,all=false)
			{
				if(this.treeChildren.length>0)
				{
					if(state==null||state!==row.isExpanded())
					{
						if(state=row.classList.toggle("collapsed",!row.classList.toggle("expanded")))
						{
							let todo=row.treeChildren.slice();
							let child;
							while(child=todo.shift())
							{
								child.remove();
								if(child.treeChildren.length>0&&all) child.classList.toggle("collapsed",!child.classList.toggle("expanded",false))
								todo.unshift(...child.treeChildren);
							}
						}
						else
						{
							let todo=row.treeChildren.slice();
							let child;
							while(child=todo.shift())
							{
								insertHelper.appendChild(child);
								if(child.treeChildren.length>0&&(child.isExpanded()||child.classList.toggle("expanded",!child.classList.toggle("collapsed",!all))))
								{
									todo.unshift(...child.treeChildren);
								}
							}
							row.parentNode.insertBefore(insertHelper,row.nextSibling);
						}
					}
					else if(all)
					{
						for(var child of row.treeChildren)
						{
							child.expand(state,true);
						}
					}
				}
			};

			return row;
		},
		getTable:function(data,headerCallback,rowCallback)
		{
			var table=this.mega(data,headerCallback,rowCallback);
			table.classList.add("Tree");
			return table;
		}
	});
	SMOD("gui.TreeTableConfig",TREE);

})(Morgas,Morgas.setModule,Morgas.getModule,Morgas.hasModule,Morgas.shortcut);
<|endoftext|>


<commit_before>/*
 * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
 */

L.LatLngBounds = L.Class.extend({
	initialize: function(southWest, northEast) {	// (LatLng, LatLng) or (LatLng[])
		if (!southWest) return;
		var latlngs = (southWest instanceof Array ? southWest : [southWest, northEast]);
		for (var i = 0, len = latlngs.length; i < len; i++) {
			this.extend(latlngs[i]);
		}
	},

	// extend the bounds to contain the given point
	extend: function(/*LatLng*/ latlng) {
		if (!this._southWest && !this._northEast) {
			this._southWest = new L.LatLng(latlng.lat, latlng.lng);
			this._northEast = new L.LatLng(latlng.lat, latlng.lng);
		} else {
			this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
			this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
			this._northEast.lat = Math.max(latlng.lat, this._northEast.lat);
			this._northEast.lng = Math.max(latlng.lng, this._northEast.lng);
		}
	},

	getCenter: function() /*-> LatLng*/ {
		return new L.LatLng(
				(this._southWest.lat + this._northEast.lat) / 2,
				(this._southWest.lng + this._northEast.lng) / 2);
	},

	getSouthWest: function() { return this._southWest; },

	getNorthEast: function() { return this._northEast; },

	getNorthWest: function() {
		return new L.LatLng(this._northEast.lat, this._southWest.lng);
	},

	getSouthEast: function() {
		return new L.LatLng(this._southWest.lat, this._northEast.lng);
	},

	contains: function(/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ {
		var sw = this._southWest,
			ne = this._northEast,
			sw2, ne2;

		if (obj instanceof L.LatLngBounds) {
			sw2 = obj.getSouthWest();
			ne2 = obj.getNorthEast();
		} else {
			sw2 = ne2 = obj;
		}

		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
				(sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
	},

	toBBoxString: function() {
		return this._southWest.lng + "," + this._southWest.lat + "," + this._northEast.lng + "," + this._northEast.lat;
	}
});

//TODO International date line?<commit_msg>cleanup LatLngBounds toBBoxString<commit_after>/*
 * L.LatLngBounds represents a rectangular area on the map in geographical coordinates.
 */

L.LatLngBounds = L.Class.extend({
	initialize: function(southWest, northEast) {	// (LatLng, LatLng) or (LatLng[])
		if (!southWest) return;
		var latlngs = (southWest instanceof Array ? southWest : [southWest, northEast]);
		for (var i = 0, len = latlngs.length; i < len; i++) {
			this.extend(latlngs[i]);
		}
	},

	// extend the bounds to contain the given point
	extend: function(/*LatLng*/ latlng) {
		if (!this._southWest && !this._northEast) {
			this._southWest = new L.LatLng(latlng.lat, latlng.lng);
			this._northEast = new L.LatLng(latlng.lat, latlng.lng);
		} else {
			this._southWest.lat = Math.min(latlng.lat, this._southWest.lat);
			this._southWest.lng = Math.min(latlng.lng, this._southWest.lng);
			this._northEast.lat = Math.max(latlng.lat, this._northEast.lat);
			this._northEast.lng = Math.max(latlng.lng, this._northEast.lng);
		}
	},

	getCenter: function() /*-> LatLng*/ {
		return new L.LatLng(
				(this._southWest.lat + this._northEast.lat) / 2,
				(this._southWest.lng + this._northEast.lng) / 2);
	},

	getSouthWest: function() { return this._southWest; },

	getNorthEast: function() { return this._northEast; },

	getNorthWest: function() {
		return new L.LatLng(this._northEast.lat, this._southWest.lng);
	},

	getSouthEast: function() {
		return new L.LatLng(this._southWest.lat, this._northEast.lng);
	},

	contains: function(/*LatLngBounds or LatLng*/ obj) /*-> Boolean*/ {
		var sw = this._southWest,
			ne = this._northEast,
			sw2, ne2;

		if (obj instanceof L.LatLngBounds) {
			sw2 = obj.getSouthWest();
			ne2 = obj.getNorthEast();
		} else {
			sw2 = ne2 = obj;
		}

		return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
				(sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
	},

	toBBoxString: function() {
		var sw = this._southWest,
			ne = this._northEast;
		return [sw.lng, sw.lat, ne.lng, ne.lat].join(',');
	}
});

//TODO International date line?<|endoftext|>