<commit_before>var cat = {};

cat.jsplumb_ready = false;

// TODO remove when done debugging
function toggle_debug_log() {
    $('#debug-log').toggleClass('hide');
}

jsPlumb.bind('ready', function() {
    jsPlumb.Defaults.Container = $('#field');

    cat.jsplumb_ready = true;
    $(document).trigger('jsplumb-ready');

    /* some combination of this might work -- laggy/buggy though
     * ALSO: how to do it on mobile?
    $('#sensors').sortable();
    $('#actuators').sortable();
    jsPlumb.draggable($('.sensor'), {containment: '#sensors'});
    jsPlumb.draggable($('.actuator'), {containment: '#actuators'});
    */
});

cat.server_url = 'ws://localhost:8001';
// use this one when you are on the Galileo
// cat.server_url = 'ws://cat/';

cat.app = angular.module('ConnectAnything', []);

cat.app.filter('sensors', function() {
    return function(pins) {
        return _.filter(pins, function(pin) {
            return pin.is_input && pin.is_visible;
        });
    };
});

cat.app.filter('actuators', function() {
    return function(pins) {
        return _.filter(pins, function(pin) {
            return !pin.is_input && pin.is_visible;
        });
    };
});

cat.is_safe_to_render_connections = function() {
    // connections can only draw themselves AFTER their pins have drawn
    // and AFTER jsPlumb has had a chance to initialize itself

    // TODO I think I could use a promise here and it would be nicer.

    var $document = $(document);
    var visible_pins, rendered_pins;
    var all_pins_rendered = false;

    function check() {
        if (all_pins_rendered && cat.jsplumb_ready) {
            return true;
        } else {
            return false;
        }
    }

    function maybe_trigger() {
        if (check()) {
            $('.connection').trigger('render-connection');
        }
    }

    $document.on('reset-pins', function(e, pins) {
        visible_pins = _.pluck(_.filter(pins, function(pin) {
            return pin.is_visible;
        }), 'id');
        console.log('visible pins', visible_pins);
        rendered_pins = {};
        all_pins_rendered = false;
    });

    $document.on('rendered-pin', function(e, pin) {
        // TODO why does this callback happen whenever a connection is rendered, with the ID of the connection?
        if (visible_pins.indexOf(pin) < 0) {
            console.log('this pin is weird:', pin);
            return;
        }
        rendered_pins[pin] = true;
        if (_.keys(rendered_pins).length === visible_pins.length) {
            all_pins_rendered = true;
            maybe_trigger();
        }
    });

    $document.on('jsplumb-ready', function(e) {
        maybe_trigger();
    });

    return check;
}();

cat.app.controller('PinsCtrl', ['$scope', function($scope, server) {

    var $document = $(document);

    // TODO take this out when done debugging
    window.$scope = $scope;
    var pin_order = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5'];
    var start_data = cat.get_fake_initial_data();
    $scope.pins = start_data.pins;
    $scope.connections = start_data.connections;
    $document.trigger('reset-pins', $scope.pins);

    // http://clintberry.com/2013/angular-js-websocket-service/
    var ws = new WebSocket(cat.server_url);
    // use this when on the Galileo
    //var ws = new WebSocket(cat.server_url, 'hardware-state-protocol');
    ws.onopen = function() {
        console.log('socket opened');
    };
    var $debug_log = $('#debug-log');
    ws.onmessage = function(message) {
        console.log('websocket message', message.data);
        //TODO remove when done debugging
        $debug_log.html(message.data);
        var pin_states = message.data.split(',');
        $scope.$apply(function(){
            for (var i = 0; i < pin_states.length; i++) {
                var pin = $scope.pins[pin_order[i]];
                // for the time being on the sensor pin values from the server are meaningful
                if (pin.is_input) {
                    pin.value = parseFloat(pin_states[i])*100;
                }
            }
        });
        // you would need to trigger this IFF you were causing pins to be redrawn
        //$document.trigger('reset-pins', $scope.pins);
    };

    $scope.connect = function(sensor, actuator) {
        console.log('connect sensor', sensor, 'actuator', actuator);
        // TODO server
        $scope.connections.push({
            sensor: sensor,
            actuator: actuator,
        });
        $scope.pins[sensor].connected_to.push(actuator);
        $scope.pins[actuator].connected_to.push(sensor);
    };

    $scope.disconnect = function(sensor, actuator) {
        // TODO server
        $scope.connections = _.filter($scope.connections, function(c) {
            return !(c.sensor === sensor && c.actuator === actuator);
        });
        $scope.pins[sensor].connected_to = _.filter($scope.pins[sensor].connected_to, function(pin) {
            return !(pin === actuator);
        });
        $scope.pins[actuator].connected_to = _.filter($scope.pins[actuator].connected_to, function(pin) {
            return !(pin === sensor);
        });
    };
}]);

// shared between sensors and actuators
cat.pin_initializer = function($document, $scope, $el, attrs) {
    var that = {};
    that.$endpoint = $el.find('.endpoint');
    that.clickevent = 'mousedown';
    that.id = attrs.id;
    return that;
};

cat.app.directive('sensor', function($document) {
    function link($scope, $el, attrs) {
        var that = cat.pin_initializer($document, $scope, $el, attrs);

        that.$endpoint.on(that.clickevent, function(e) {
            var already_activated = $el.hasClass('activated');
            $('.pin').removeClass('activated');
            if (!already_activated) {
                $el.addClass('activated');
                $('.actuator').addClass('activated');
            }
        });

        $el.on('$destroy', function() {
            that.$endpoint.off(that.clickevent);
        });

        $(document).trigger('rendered-pin', attrs.id);
    }

    // TODO is there an unlink function i should write so that i can remove listeners when this gets deleted?

    return {
        link: link,
    }
});

cat.app.directive('actuator', function($document) {
    function link($scope, $el, attrs) {

        var that = cat.pin_initializer($document, $scope, $el, attrs);

        that.$endpoint.on(that.clickevent, function(e) {
            if (!$el.hasClass('activated')) {
                return;
            }
            var $sensor = $('.sensor.activated').first();
            var sensor = $sensor.attr('id'); // pin id
            if ($scope.pins[attrs.id].connected_to.indexOf(sensor) >= 0) {
                // already connected, so ask if they want to delete the connection
                $('#connect-' + sensor + '-' + attrs.id).trigger('mousedown');
            } else {
                $scope.$apply(function() {
                    $scope.connect(sensor, attrs.id);
                });
            }
            $('.pin').removeClass('activated');
        });

        $el.on('$destroy', function() {
            that.$endpoint.off(that.clickevent);
        });

        $(document).trigger('rendered-pin', attrs.id);
    }

    return {
        link: link,
    }
});

cat.app.directive('connection', function($document) {
    function link($scope, $el, attrs) {

        var $sensor, $actuator, connection, msg;
        $sensor = $actuator = connection = msg = null;

        function render() {
            $sensor = $('#'+attrs.sensor);
            $actuator = $('#'+attrs.actuator);
            console.log('rendering connection ', attrs.sensor, '-', attrs.actuator);
            msg = 'Do you want to delete the ' + $sensor.attr('id') + ' - ' + $actuator.attr('id') + ' connection?';
            connection = jsPlumb.connect({
                source: attrs.sensor,
                target: attrs.actuator,
                connector: ['Bezier', {curviness: 70}],
                cssClass: 'connection pins-'+attrs.sensor+'-'+attrs.actuator,
                endpoint: 'Blank',
                endpointClass: 'endpoint pins-'+attrs.sensor+'-'+attrs.actuator,
                anchors: ['Right', 'Left'],
                paintStyle: {
                    lineWidth: 15,
                    strokeStyle: 'rgb(232, 189, 0)',
                    outlineWidth: 2,
                    outlineColor: 'antiquewhite',
                },
                endpointStyle: {
                    fillStyle: '#a7b04b',
                },
                hoverPaintStyle: {
                    strokeStyle: 'rgb(250, 250, 60)',
                },
            });

            function remove_self(e) {
                if (confirm(msg)) {
                    connection.unbind('mousedown');
                    $scope.$apply(function() {
                        $scope.disconnect(attrs.sensor, attrs.actuator);
                    });
                }
            }

            connection.bind('mousedown', remove_self);
            $el.on('mousedown', remove_self);
        }

        if (cat.is_safe_to_render_connections()) {
            render();
        } else {
            $el.on('render-connection', function() {
                render();
            });
        }

        $el.on('$destroy', function() {
            if (connection !== null) {
                connection.unbind('mousedown');
                jsPlumb.detach(connection);
            }
        });
    }

    return {
        link: link,
    };
});

cat.get_fake_initial_data = function() {

    var pin_defaults = {
        'input': {
            'analog': [0, 1, 2, 3, 4, 5],
        },
        'output': {
            'analog': [3, 5, 6, 9, 10, 11],
            'digital': [0, 1, 2, 4, 7, 8, 12, 13],
        },
    };

    function pin_name(number, is_analog, is_input) {
        if (!is_analog) { // digital
            return number.toString();
        }
        // is_analog === true
        if (is_input) {
            return 'A' + number;
        } else {
            return '~' + number;
        }
    }

    function pin_id(number, is_analog, is_input) {
        if (is_input && is_analog) {
            return 'A' + number;
        } else {
            return number.toString();
        }
    }

    pins = {};
    _.each(pin_defaults, function(obj, IorO) { // input or output
        _.each(obj, function(nums, AorD) { // analog or digital
            _.each(nums, function(num) { // all pin numbers of this type
                var is_input = IorO === 'input';
                var is_analog = AorD === 'analog';
                var id = pin_id(num, is_analog, is_input);
                var name = pin_name(num, is_analog, is_input);
                pins[id] = {
                    'id': id,
                    'name': name,
                    'label': 'Label for ' + name,
                    'is_analog': is_analog,
                    'is_input': is_input,
                    'value': 0,
                    'is_visible': true,
                    'connected_to': [],
                };
            });
        });
    });

    // TODO merge in sync-connections branch so that you can start with some connections
    connections = [{sensor: 'A1', actuator: '1'},
                   {sensor: 'A1', actuator: '3'}];

    // TODO maintaining redundant state is bad
    _.each(connections, function(c) {
        pins[c.sensor].connected_to.push(c.actuator);
        pins[c.actuator].connected_to.push(c.sensor);
    });

    return {pins: pins, connections: connections};
};
<commit_msg>fixed typo in comment<commit_after>var cat = {};

cat.jsplumb_ready = false;

// TODO remove when done debugging
function toggle_debug_log() {
    $('#debug-log').toggleClass('hide');
}

jsPlumb.bind('ready', function() {
    jsPlumb.Defaults.Container = $('#field');

    cat.jsplumb_ready = true;
    $(document).trigger('jsplumb-ready');

    /* some combination of this might work -- laggy/buggy though
     * ALSO: how to do it on mobile?
    $('#sensors').sortable();
    $('#actuators').sortable();
    jsPlumb.draggable($('.sensor'), {containment: '#sensors'});
    jsPlumb.draggable($('.actuator'), {containment: '#actuators'});
    */
});

cat.server_url = 'ws://localhost:8001';
// use this one when you are on the Galileo
// cat.server_url = 'ws://cat/';

cat.app = angular.module('ConnectAnything', []);

cat.app.filter('sensors', function() {
    return function(pins) {
        return _.filter(pins, function(pin) {
            return pin.is_input && pin.is_visible;
        });
    };
});

cat.app.filter('actuators', function() {
    return function(pins) {
        return _.filter(pins, function(pin) {
            return !pin.is_input && pin.is_visible;
        });
    };
});

cat.is_safe_to_render_connections = function() {
    // connections can only draw themselves AFTER their pins have drawn
    // and AFTER jsPlumb has had a chance to initialize itself

    // TODO I think I could use a promise here and it would be nicer.

    var $document = $(document);
    var visible_pins, rendered_pins;
    var all_pins_rendered = false;

    function check() {
        if (all_pins_rendered && cat.jsplumb_ready) {
            return true;
        } else {
            return false;
        }
    }

    function maybe_trigger() {
        if (check()) {
            $('.connection').trigger('render-connection');
        }
    }

    $document.on('reset-pins', function(e, pins) {
        visible_pins = _.pluck(_.filter(pins, function(pin) {
            return pin.is_visible;
        }), 'id');
        console.log('visible pins', visible_pins);
        rendered_pins = {};
        all_pins_rendered = false;
    });

    $document.on('rendered-pin', function(e, pin) {
        // TODO why does this callback happen whenever a connection is rendered, with the ID of the connection?
        if (visible_pins.indexOf(pin) < 0) {
            console.log('this pin is weird:', pin);
            return;
        }
        rendered_pins[pin] = true;
        if (_.keys(rendered_pins).length === visible_pins.length) {
            all_pins_rendered = true;
            maybe_trigger();
        }
    });

    $document.on('jsplumb-ready', function(e) {
        maybe_trigger();
    });

    return check;
}();

cat.app.controller('PinsCtrl', ['$scope', function($scope, server) {

    var $document = $(document);

    // TODO take this out when done debugging
    window.$scope = $scope;
    var pin_order = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', 'A0', 'A1', 'A2', 'A3', 'A4', 'A5'];
    var start_data = cat.get_fake_initial_data();
    $scope.pins = start_data.pins;
    $scope.connections = start_data.connections;
    $document.trigger('reset-pins', $scope.pins);

    // http://clintberry.com/2013/angular-js-websocket-service/
    var ws = new WebSocket(cat.server_url);
    // use this when on the Galileo
    //var ws = new WebSocket(cat.server_url, 'hardware-state-protocol');
    ws.onopen = function() {
        console.log('socket opened');
    };
    var $debug_log = $('#debug-log');
    ws.onmessage = function(message) {
        console.log('websocket message', message.data);
        //TODO remove when done debugging
        $debug_log.html(message.data);
        var pin_states = message.data.split(',');
        $scope.$apply(function(){
            for (var i = 0; i < pin_states.length; i++) {
                var pin = $scope.pins[pin_order[i]];
                // for the time being only the sensor pin values from the server are meaningful
                if (pin.is_input) {
                    pin.value = parseFloat(pin_states[i])*100;
                }
            }
        });
        // you would need to trigger this IFF you were causing pins to be redrawn
        //$document.trigger('reset-pins', $scope.pins);
    };

    $scope.connect = function(sensor, actuator) {
        console.log('connect sensor', sensor, 'actuator', actuator);
        // TODO server
        $scope.connections.push({
            sensor: sensor,
            actuator: actuator,
        });
        $scope.pins[sensor].connected_to.push(actuator);
        $scope.pins[actuator].connected_to.push(sensor);
    };

    $scope.disconnect = function(sensor, actuator) {
        // TODO server
        $scope.connections = _.filter($scope.connections, function(c) {
            return !(c.sensor === sensor && c.actuator === actuator);
        });
        $scope.pins[sensor].connected_to = _.filter($scope.pins[sensor].connected_to, function(pin) {
            return !(pin === actuator);
        });
        $scope.pins[actuator].connected_to = _.filter($scope.pins[actuator].connected_to, function(pin) {
            return !(pin === sensor);
        });
    };
}]);

// shared between sensors and actuators
cat.pin_initializer = function($document, $scope, $el, attrs) {
    var that = {};
    that.$endpoint = $el.find('.endpoint');
    that.clickevent = 'mousedown';
    that.id = attrs.id;
    return that;
};

cat.app.directive('sensor', function($document) {
    function link($scope, $el, attrs) {
        var that = cat.pin_initializer($document, $scope, $el, attrs);

        that.$endpoint.on(that.clickevent, function(e) {
            var already_activated = $el.hasClass('activated');
            $('.pin').removeClass('activated');
            if (!already_activated) {
                $el.addClass('activated');
                $('.actuator').addClass('activated');
            }
        });

        $el.on('$destroy', function() {
            that.$endpoint.off(that.clickevent);
        });

        $(document).trigger('rendered-pin', attrs.id);
    }

    // TODO is there an unlink function i should write so that i can remove listeners when this gets deleted?

    return {
        link: link,
    }
});

cat.app.directive('actuator', function($document) {
    function link($scope, $el, attrs) {

        var that = cat.pin_initializer($document, $scope, $el, attrs);

        that.$endpoint.on(that.clickevent, function(e) {
            if (!$el.hasClass('activated')) {
                return;
            }
            var $sensor = $('.sensor.activated').first();
            var sensor = $sensor.attr('id'); // pin id
            if ($scope.pins[attrs.id].connected_to.indexOf(sensor) >= 0) {
                // already connected, so ask if they want to delete the connection
                $('#connect-' + sensor + '-' + attrs.id).trigger('mousedown');
            } else {
                $scope.$apply(function() {
                    $scope.connect(sensor, attrs.id);
                });
            }
            $('.pin').removeClass('activated');
        });

        $el.on('$destroy', function() {
            that.$endpoint.off(that.clickevent);
        });

        $(document).trigger('rendered-pin', attrs.id);
    }

    return {
        link: link,
    }
});

cat.app.directive('connection', function($document) {
    function link($scope, $el, attrs) {

        var $sensor, $actuator, connection, msg;
        $sensor = $actuator = connection = msg = null;

        function render() {
            $sensor = $('#'+attrs.sensor);
            $actuator = $('#'+attrs.actuator);
            console.log('rendering connection ', attrs.sensor, '-', attrs.actuator);
            msg = 'Do you want to delete the ' + $sensor.attr('id') + ' - ' + $actuator.attr('id') + ' connection?';
            connection = jsPlumb.connect({
                source: attrs.sensor,
                target: attrs.actuator,
                connector: ['Bezier', {curviness: 70}],
                cssClass: 'connection pins-'+attrs.sensor+'-'+attrs.actuator,
                endpoint: 'Blank',
                endpointClass: 'endpoint pins-'+attrs.sensor+'-'+attrs.actuator,
                anchors: ['Right', 'Left'],
                paintStyle: {
                    lineWidth: 15,
                    strokeStyle: 'rgb(232, 189, 0)',
                    outlineWidth: 2,
                    outlineColor: 'antiquewhite',
                },
                endpointStyle: {
                    fillStyle: '#a7b04b',
                },
                hoverPaintStyle: {
                    strokeStyle: 'rgb(250, 250, 60)',
                },
            });

            function remove_self(e) {
                if (confirm(msg)) {
                    connection.unbind('mousedown');
                    $scope.$apply(function() {
                        $scope.disconnect(attrs.sensor, attrs.actuator);
                    });
                }
            }

            connection.bind('mousedown', remove_self);
            $el.on('mousedown', remove_self);
        }

        if (cat.is_safe_to_render_connections()) {
            render();
        } else {
            $el.on('render-connection', function() {
                render();
            });
        }

        $el.on('$destroy', function() {
            if (connection !== null) {
                connection.unbind('mousedown');
                jsPlumb.detach(connection);
            }
        });
    }

    return {
        link: link,
    };
});

cat.get_fake_initial_data = function() {

    var pin_defaults = {
        'input': {
            'analog': [0, 1, 2, 3, 4, 5],
        },
        'output': {
            'analog': [3, 5, 6, 9, 10, 11],
            'digital': [0, 1, 2, 4, 7, 8, 12, 13],
        },
    };

    function pin_name(number, is_analog, is_input) {
        if (!is_analog) { // digital
            return number.toString();
        }
        // is_analog === true
        if (is_input) {
            return 'A' + number;
        } else {
            return '~' + number;
        }
    }

    function pin_id(number, is_analog, is_input) {
        if (is_input && is_analog) {
            return 'A' + number;
        } else {
            return number.toString();
        }
    }

    pins = {};
    _.each(pin_defaults, function(obj, IorO) { // input or output
        _.each(obj, function(nums, AorD) { // analog or digital
            _.each(nums, function(num) { // all pin numbers of this type
                var is_input = IorO === 'input';
                var is_analog = AorD === 'analog';
                var id = pin_id(num, is_analog, is_input);
                var name = pin_name(num, is_analog, is_input);
                pins[id] = {
                    'id': id,
                    'name': name,
                    'label': 'Label for ' + name,
                    'is_analog': is_analog,
                    'is_input': is_input,
                    'value': 0,
                    'is_visible': true,
                    'connected_to': [],
                };
            });
        });
    });

    // TODO merge in sync-connections branch so that you can start with some connections
    connections = [{sensor: 'A1', actuator: '1'},
                   {sensor: 'A1', actuator: '3'}];

    // TODO maintaining redundant state is bad
    _.each(connections, function(c) {
        pins[c.sensor].connected_to.push(c.actuator);
        pins[c.actuator].connected_to.push(c.sensor);
    });

    return {pins: pins, connections: connections};
};
<|endoftext|>


<commit_before>(function ($) {
    "use strict";
    var fn = {
        // Funcionalidades
        Iniciar: function () {
            fn.App();
        },
        App : function () {

          // $( "#lider" ).autocomplete({
          //   source: lideres
          // });
          // function split( val ) {
          //   return val.split( /,\s*/ );
          // }
          // function extractLast( term ) {
          //   return split( term ).pop();
          // }

          // $( "#participantes" )
          //   // don't navigate away from the field on tab when selecting an item
          //   .bind( "keydown", function( event ) {
          //     if ( event.keyCode === $.ui.keyCode.TAB &&
          //         $( this ).autocomplete( "instance" ).menu.active ) {
          //       event.preventDefault();
          //     }
          //   })
          //   .autocomplete({
          //     minLength: 0,
          //     source: function( request, response ) {
          //       // delegate back to autocomplete, but extract the last term
          //       response( $.ui.autocomplete.filter(
          //         participantes, extractLast( request.term ) ) );
          //     },
          //     focus: function() {
          //       // prevent value inserted on focus
          //       return false;
          //     },
          //     select: function( event, ui ) {
          //       var terms = split( this.value );
          //       // remove the current input
          //       terms.pop();
          //       // add the selected item
          //       terms.push( ui.item.value );
          //       // add placeholder to get the comma-and-space at the end
          //       terms.push( "" );
          //       this.value = terms.join( ", " );
          //       return false;
          //     }
          //   });
          function formatState (state) {
            // if (!state.id) { return state.text; }
            // console.log(state);
            // var states = state.element;
            // state.foreach(function(item){
            //   console.log(item);
            // });
            // console.log(state.element);
             // if (!state.id) { return state.text; }
            var $state = $(
              ' ' + state.text + ''
              // '' +  state.element.value.toLowerCase() + ''
            );
            return $state;
          };
          $("#lider").select2({
            maximumSelectionLength: 1,
            // placeholder: "Escolha alguém...",
            templateResult: formatState

          }
          );
          $("#participantes").select2(
            // placeholder: "Escolha alguém..."
          );

          }
      }
          // var REGEX_EMAIL = '([a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@' +
          //         '(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';

          // $('#lider').selectize({
          //     persist: false,
          //     maxItems: null,
          //     valueField: 'email',
          //     labelField: 'name',
          //     searchField: ['name', 'email'],
          //     options: [
          //         {email: 'brian@thirdroute.com', name: 'Brian Reavis'},
          //         {email: 'nikola@tesla.com', name: 'Nikola Tesla'},
          //         {email: 'someone@gmail.com'}
          //     ],
          //     render: {
          //         item: function(item, escape) {
          //             return '
' +
          //                 (item.name ? '' + escape(item.name) + '' : '') +
          //                 (item.email ? '' + escape(item.email) + '' : '') +
          //             '
';
          //         },
          //         option: function(item, escape) {
          //             var label = item.name || item.email;
          //             var caption = item.name ? item.email : null;
          //             return '
' +
          //                 '' + escape(label) + '' +
          //                 (caption ? '' + escape(caption) + '' : '') +
          //             '
';
          //         }
          //     },
          //     createFilter: function(input) {
          //         var match, regex;

          //         // email@address.com
          //         regex = new RegExp('^' + REGEX_EMAIL + '$', 'i');
          //         match = input.match(regex);
          //         if (match) return !this.options.hasOwnProperty(match[0]);

          //         // name <email@address.com>
          //         regex = new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i');
          //         match = input.match(regex);
          //         if (match) return !this.options.hasOwnProperty(match[2]);

          //         return false;
          //     },
          //     create: function(input) {
          //         if ((new RegExp('^' + REGEX_EMAIL + '$', 'i')).test(input)) {
          //             return {email: input};
          //         }
          //         var match = input.match(new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i'));
          //         if (match) {
          //             return {
          //                 email : match[2],
          //                 name  : $.trim(match[1])
          //             };
          //         }
          //         alert('Invalid email address.');
          //         return false;
          //     }
          // });
    //     }
    // }
    $(document).ready(function () {
        fn.Iniciar();
    });
})(jQuery);<commit_msg>Update<commit_after>(function ($) {
    "use strict";
    var fn = {
        // Funcionalidades
        Iniciar: function () {
            fn.App();
        },
        App : function () {

          $('#frmProjeto-Adicionar').submit(function(e){
            // e.preventDefault();
            // alert($('#participantes').val());
          });

          // $( "#lider" ).autocomplete({
          //   source: lideres
          // });
          // function split( val ) {
          //   return val.split( /,\s*/ );
          // }
          // function extractLast( term ) {
          //   return split( term ).pop();
          // }

          // $( "#participantes" )
          //   // don't navigate away from the field on tab when selecting an item
          //   .bind( "keydown", function( event ) {
          //     if ( event.keyCode === $.ui.keyCode.TAB &&
          //         $( this ).autocomplete( "instance" ).menu.active ) {
          //       event.preventDefault();
          //     }
          //   })
          //   .autocomplete({
          //     minLength: 0,
          //     source: function( request, response ) {
          //       // delegate back to autocomplete, but extract the last term
          //       response( $.ui.autocomplete.filter(
          //         participantes, extractLast( request.term ) ) );
          //     },
          //     focus: function() {
          //       // prevent value inserted on focus
          //       return false;
          //     },
          //     select: function( event, ui ) {
          //       var terms = split( this.value );
          //       // remove the current input
          //       terms.pop();
          //       // add the selected item
          //       terms.push( ui.item.value );
          //       // add placeholder to get the comma-and-space at the end
          //       terms.push( "" );
          //       this.value = terms.join( ", " );
          //       return false;
          //     }
          //   });
          function formatState (state) {
            // if (!state.id) { return state.text; }
            // console.log(state);
            // var states = state.element;
            // state.foreach(function(item){
            //   console.log(item);
            // });
            // console.log(state.element);
             // if (!state.id) { return state.text; }
            var $state = $(
              ' ' + state.text + ''
              // '' +  state.element.value.toLowerCase() + ''
            );
            return $state;
          };
          $("#lider").select2({
            maximumSelectionLength: 1,
            // placeholder: "Escolha alguém...",
            templateResult: formatState

          }
          );
          $("#participantes").select2(
            // placeholder: "Escolha alguém..."
          );

          }
      }
          // var REGEX_EMAIL = '([a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@' +
          //         '(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';

          // $('#lider').selectize({
          //     persist: false,
          //     maxItems: null,
          //     valueField: 'email',
          //     labelField: 'name',
          //     searchField: ['name', 'email'],
          //     options: [
          //         {email: 'brian@thirdroute.com', name: 'Brian Reavis'},
          //         {email: 'nikola@tesla.com', name: 'Nikola Tesla'},
          //         {email: 'someone@gmail.com'}
          //     ],
          //     render: {
          //         item: function(item, escape) {
          //             return '
' +
          //                 (item.name ? '' + escape(item.name) + '' : '') +
          //                 (item.email ? '' + escape(item.email) + '' : '') +
          //             '
';
          //         },
          //         option: function(item, escape) {
          //             var label = item.name || item.email;
          //             var caption = item.name ? item.email : null;
          //             return '
' +
          //                 '' + escape(label) + '' +
          //                 (caption ? '' + escape(caption) + '' : '') +
          //             '
';
          //         }
          //     },
          //     createFilter: function(input) {
          //         var match, regex;

          //         // email@address.com
          //         regex = new RegExp('^' + REGEX_EMAIL + '$', 'i');
          //         match = input.match(regex);
          //         if (match) return !this.options.hasOwnProperty(match[0]);

          //         // name <email@address.com>
          //         regex = new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i');
          //         match = input.match(regex);
          //         if (match) return !this.options.hasOwnProperty(match[2]);

          //         return false;
          //     },
          //     create: function(input) {
          //         if ((new RegExp('^' + REGEX_EMAIL + '$', 'i')).test(input)) {
          //             return {email: input};
          //         }
          //         var match = input.match(new RegExp('^([^<]*)\<' + REGEX_EMAIL + '\>$', 'i'));
          //         if (match) {
          //             return {
          //                 email : match[2],
          //                 name  : $.trim(match[1])
          //             };
          //         }
          //         alert('Invalid email address.');
          //         return false;
          //     }
          // });
    //     }
    // }
    $(document).ready(function () {
        fn.Iniciar();
    });
})(jQuery);<|endoftext|>


<commit_before>(function(){
	"use strict";
	function byteArrayToHex(arrayBuffer){
		const chunks = [];
		(new Uint32Array(arrayBuffer)).forEach(function(num){
			chunks.push(num.toString(16));
		});
		return chunks.map(function(chunk){
			return "0".repeat(8 - chunk.length) + chunk;
		}).join("");
	}

	const container = document.getElementById("tests");
	const iframe = document.getElementById("iframe");
	const svg = document.getElementById("svg");
	const noIframe = document.getElementById("noIframe");
	const template = document.querySelector(".test");
	template.parentElement.removeChild(template);

	function getElements(useIframe = true){
		const doc = useIframe? (useIframe === "svg"? svg.contentDocument: iframe.contentDocument): noIframe;

		return Array.from(doc.querySelectorAll(".testRect"));
	}

	function formatNumber(number){
		const str = number.toString();
		return "" + str.substring(0, str.length - 2) + "" +
			str.substring(str.length - 2);
	}

	const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"];
	async function performTest(output, callback, useIframe = true){
		const rects = (await Promise.all(getElements(useIframe).map(async function(element){
			return {
				name: element.dataset.name,
				data: await callback(element)
			};
		}))).filter(function(rect){
			return rect.data;
		});
		const data = new Float64Array(rects.length * properties.length);
		rects.forEach(function(rect, i){
			properties.forEach(function(property, j){
				data[i * properties.length + j] = rect.data[property];
			});
		});

		const hash = await crypto.subtle.digest("SHA-256", data);
		output.querySelector(".hash").textContent = byteArrayToHex(hash);

		const dataNode = output.querySelector(".data");
		dataNode.innerHTML = "

	" +
			rects.map(function(rect){
				return "" + rect.name + "	";
			}).join("") +
			"

hash	" +
			rects.map(function(rect){
				return "	";
			}).join("") +
			"
" +
			properties.map(function(property){
				return "
" + property + "	" + rects.map(function(rect){
					const value = rect.data[property];
					if ((typeof value) === "number"){
						return "" +
							formatNumber(rect.data[property]) +
							"	";
					}
					else {
						return "not available	";
					}
				}).join("") + "
";
			}).join("") +
			"
";
		rects.forEach(async function(rect){
			const data = new Float64Array(properties.length);
			properties.forEach(function(property, i){
				data[i] = rect.data[property];
			});

			const hash = await crypto.subtle.digest("SHA-256", data);
			dataNode.querySelector(
				".rectHash[data-name=\"" + rect.name + "\"]"
			).textContent = byteArrayToHex(hash);
		});
	}

	function createTest(title, callback, useIframe){
		const output = template.cloneNode(true);
		output.querySelector(".title").textContent = title +
			(useIframe? " (" + (useIframe === "svg"? "svg": "iframe") + ")": "");
		output.querySelector(".refresh").addEventListener("click", function(){
			performTest(output, callback, useIframe);
		});
		output.querySelector(".performance").addEventListener("click", function(){
			let count = 200;
			let totalCount = 0;
			let totalDuration = 0;
			return function(){
				let duration = 0;
				let i = 0;
				while (duration < 1000){
					const start = Date.now();
					for (; i < count; i += 1){
						const rects = getElements(useIframe).map(function(element){
							return {
								name: element.dataset.name,
								data: callback(element)
							};
						});
						const data = new Float64Array(rects.length * properties.length);
						rects.forEach(function(rect, i){
							properties.forEach(function(property, j){
								data[i * properties.length + j] = rect.data[property];
							});
						});
					}
					duration += Date.now() - start;
					if (duration < 1000){
						count += Math.ceil(
							count * (1000 - duration) / 1000
						);
					}
				}
				totalCount += count;
				totalDuration += duration;
				alert(
					(totalCount / (totalDuration / 1000)).toFixed(2) + " tests/s\n" +
					(totalDuration * 1000 / totalCount).toFixed(2) + " µs/test\n" +
					"(" + totalCount + " samples)"
				);
			};
		}());

		container.appendChild(output);
		performTest(output, callback, useIframe);
	}
	async function getIntersectionEntryValue(parentElement, element, property){
		if (!(element instanceof Element)){
			return null;
		}

		return new Promise(function(resolve){
			const timeout = window.setTimeout(function(){
				resolve(null);
			}, 1000);
			const observer = new IntersectionObserver(function(entries){
				window.clearTimeout(timeout);
				resolve(entries[0][property]);
			}, {
				root: parentElement,
				rootMargin: "1000px",
			});
			observer.observe(element);
		});
	}
	iframe.addEventListener("load", function(){
		[true, false].forEach(function(useIframe){
			createTest("Element.getClientRects", function(element){
				return element.getClientRects()[0];
			}, useIframe);
			createTest("Element.getBoundingClientRect", function(element){
				return element.getBoundingClientRect();
			}, useIframe);
			createTest("Element.getBoxQuads", function(element){
				const quad = element.getBoxQuads();
				return quad[0].getBounds();
			}, useIframe);
			createTest("IntersectionObserverEntry.intersectionRect", function(element){
				return getIntersectionEntryValue(element.parentElement, element, "intersectionRect");
			}, useIframe);
			createTest("IntersectionObserverEntry.boundingClientRect", function(element){
				return getIntersectionEntryValue(element.parentElement, element, "boundingClientRect");
			}, useIframe);
			createTest("IntersectionObserverEntry.rootBounds", function(element){
				return getIntersectionEntryValue(element, element.firstChild, "rootBounds");
			}, useIframe);
			createTest("Range.getClientRects", function(element){
				const range = document.createRange();
				range.selectNode(element);
				return range.getClientRects()[0];
			}, useIframe);
			createTest("Range.getBoundingClientRect", function(element){
				const range = document.createRange();
				range.selectNode(element);
				return range.getBoundingClientRect();
			}, useIframe);
		});
		createTest("SVGGraphicsElement.getBBox", function(element){
			return element.getBBox();
		}, "svg");
		createTest("SVGTextContentElement.getExtentOfChar", function(element){
			return element.getExtentOfChar(element.textContent.length - 1);
		}, "svg");
		createTest("SVGTextContentElement.get(Start|End)OfChar", function(element){
			const start = element.getStartPositionOfChar(element.textContent.length - 1);
			const end = element.getEndPositionOfChar(element.textContent.length - 1);
			return new DOMRect(start.x, start.y, end.x - start.x, end.y - start.y);
		}, "svg");

		document.querySelectorAll(".content-hidable").forEach(function(parentNode){
			parentNode.querySelector(".toggle").addEventListener("click", function(){
				parentNode.classList.toggle("content-hidden");
			});
		});
	});
}());<commit_msg>domRectTest: update SVG tests<commit_after>(function(){
	"use strict";
	function byteArrayToHex(arrayBuffer){
		const chunks = [];
		(new Uint32Array(arrayBuffer)).forEach(function(num){
			chunks.push(num.toString(16));
		});
		return chunks.map(function(chunk){
			return "0".repeat(8 - chunk.length) + chunk;
		}).join("");
	}

	const container = document.getElementById("tests");
	const iframe = document.getElementById("iframe");
	const svg = document.getElementById("svg");
	const noIframe = document.getElementById("noIframe");
	const template = document.querySelector(".test");
	template.parentElement.removeChild(template);

	function getElements(useIframe = true){
		const doc = useIframe? (useIframe === "svg"? svg.contentDocument: iframe.contentDocument): noIframe;

		return Array.from(doc.querySelectorAll(".testRect"));
	}

	function formatNumber(number){
		const str = number.toString();
		return "" + str.substring(0, str.length - 2) + "" +
			str.substring(str.length - 2);
	}

	const properties = ["x", "y", "width", "height", "top", "left", "right", "bottom"];
	async function performTest(output, callback, useIframe = true){
		const rects = (await Promise.all(getElements(useIframe).map(async function(element){
			return {
				name: element.dataset.name,
				data: await callback(element)
			};
		}))).filter(function(rect){
			return rect.data;
		});
		const data = new Float64Array(rects.length * properties.length);
		rects.forEach(function(rect, i){
			properties.forEach(function(property, j){
				data[i * properties.length + j] = rect.data[property];
			});
		});

		const hash = await crypto.subtle.digest("SHA-256", data);
		output.querySelector(".hash").textContent = byteArrayToHex(hash);

		const dataNode = output.querySelector(".data");
		dataNode.innerHTML = "

	" +
			rects.map(function(rect){
				return "" + rect.name + "	";
			}).join("") +
			"

hash	" +
			rects.map(function(rect){
				return "	";
			}).join("") +
			"
" +
			properties.map(function(property){
				return "
" + property + "	" + rects.map(function(rect){
					const value = rect.data[property];
					if ((typeof value) === "number"){
						return "" +
							formatNumber(rect.data[property]) +
							"	";
					}
					else {
						return "not available	";
					}
				}).join("") + "
";
			}).join("") +
			"
";
		rects.forEach(async function(rect){
			const data = new Float64Array(properties.length);
			properties.forEach(function(property, i){
				data[i] = rect.data[property];
			});

			const hash = await crypto.subtle.digest("SHA-256", data);
			dataNode.querySelector(
				".rectHash[data-name=\"" + rect.name + "\"]"
			).textContent = byteArrayToHex(hash);
		});
	}

	function createTest(title, callback, useIframe){
		const output = template.cloneNode(true);
		output.querySelector(".title").textContent = title +
			(useIframe? " (" + (useIframe === "svg"? "svg": "iframe") + ")": "");
		output.querySelector(".refresh").addEventListener("click", function(){
			performTest(output, callback, useIframe);
		});
		output.querySelector(".performance").addEventListener("click", function(){
			let count = 200;
			let totalCount = 0;
			let totalDuration = 0;
			return function(){
				let duration = 0;
				let i = 0;
				while (duration < 1000){
					const start = Date.now();
					for (; i < count; i += 1){
						const rects = getElements(useIframe).map(function(element){
							return {
								name: element.dataset.name,
								data: callback(element)
							};
						});
						const data = new Float64Array(rects.length * properties.length);
						rects.forEach(function(rect, i){
							properties.forEach(function(property, j){
								data[i * properties.length + j] = rect.data[property];
							});
						});
					}
					duration += Date.now() - start;
					if (duration < 1000){
						count += Math.ceil(
							count * (1000 - duration) / 1000
						);
					}
				}
				totalCount += count;
				totalDuration += duration;
				alert(
					(totalCount / (totalDuration / 1000)).toFixed(2) + " tests/s\n" +
					(totalDuration * 1000 / totalCount).toFixed(2) + " µs/test\n" +
					"(" + totalCount + " samples)"
				);
			};
		}());

		container.appendChild(output);
		performTest(output, callback, useIframe);
	}
	async function getIntersectionEntryValue(parentElement, element, property){
		if (!(element instanceof Element)){
			return null;
		}

		return new Promise(function(resolve){
			const timeout = window.setTimeout(function(){
				resolve(null);
			}, 1000);
			const observer = new IntersectionObserver(function(entries){
				window.clearTimeout(timeout);
				resolve(entries[0][property]);
			}, {
				root: parentElement,
				rootMargin: "1000px",
			});
			observer.observe(element);
		});
	}
	iframe.addEventListener("load", function(){
		[true, false].forEach(function(useIframe){
			createTest("Element.getClientRects", function(element){
				return element.getClientRects()[0];
			}, useIframe);
			createTest("Element.getBoundingClientRect", function(element){
				return element.getBoundingClientRect();
			}, useIframe);
			createTest("Element.getBoxQuads", function(element){
				const quad = element.getBoxQuads();
				return quad[0].getBounds();
			}, useIframe);
			createTest("IntersectionObserverEntry.intersectionRect", function(element){
				return getIntersectionEntryValue(element.parentElement, element, "intersectionRect");
			}, useIframe);
			createTest("IntersectionObserverEntry.boundingClientRect", function(element){
				return getIntersectionEntryValue(element.parentElement, element, "boundingClientRect");
			}, useIframe);
			createTest("IntersectionObserverEntry.rootBounds", function(element){
				return getIntersectionEntryValue(element, element.firstChild, "rootBounds");
			}, useIframe);
			createTest("Range.getClientRects", function(element){
				const range = document.createRange();
				range.selectNode(element);
				return range.getClientRects()[0];
			}, useIframe);
			createTest("Range.getBoundingClientRect", function(element){
				const range = document.createRange();
				range.selectNode(element);
				return range.getBoundingClientRect();
			}, useIframe);
		});
		createTest("SVGGraphicsElement.getBBox", function(element){
			return element.getBBox();
		}, "svg");
		createTest("SVGTextContentElement.getExtentOfChar", function(element){
			return element.getEndPositionOfChar? element.getExtentOfChar(element.textContent.length - 1): null;
		}, "svg");
		createTest("SVGTextContentElement.get(Start|End)OfChar", function(element){
			if (!element.getStartPositionOfChar){
				return null;
			}
			const start = element.getStartPositionOfChar(element.textContent.length - 1);
			const end = element.getEndPositionOfChar(element.textContent.length - 1);
			return new DOMRect(start.x, start.y, end.x - start.x, end.y - start.y);
		}, "svg");
		createTest("SVGGeometryElement.getPointAtLength", function(element){
			if (!element.getPointAtLength){
				return null;
			}
			const start = element.getPointAtLength(Math.E);
			const end = element.getPointAtLength(Math.PI);
			return new DOMRect(start.x, start.y, end.x - start.x, end.y - start.y);
		}, "svg");

		document.querySelectorAll(".content-hidable").forEach(function(parentNode){
			parentNode.querySelector(".toggle").addEventListener("click", function(){
				parentNode.classList.toggle("content-hidden");
			});
		});
	});
}());<|endoftext|>


<commit_before>const testPlugin = require('./testPlugin');

describe('__() errors', () => {
  test('throwing error for missing value', () => {
    const code = '__("missing")';
    const translations = {};
    expect(() => testPlugin(code, translations)).toThrowError(/missing/);
  });

  test('tells what part is missing', () => {
    const code = '__("exists.missing.more")';
    const translations = { exists: { foo: 'bar' } };
    expect(() => testPlugin(code, translations)).toThrowError(/'missing'/);
  });

  test('does not allow objects to come through', () => {
    const code = '__("this_should_be_string")';
    const translations = { this_should_be_string: { but: 'it is not' } };
    expect(() => testPlugin(code, translations)).toThrowError(/incorrect type.*'object'/);
  });

  test('does not allow arrays to come through', () => {
    const code = '__("this_should_be_string")';
    const translations = { this_should_be_string: ['but it is an array'] };
    expect(() => testPlugin(code, translations)).toThrowError(/incorrect type/);
  });

  test('does not allow functions to come through', () => {
    const code = '__("this_should_be_string")';
    const translations = { this_should_be_string: () => 'but it is a function' };
    expect(() => testPlugin(code, translations)).toThrowError(/incorrect type.*'function'/);
  });
});

describe('__obj() errors', () => {
  test('throwing error for missing value', () => {
    const code = '__obj("missing")';
    const translations = {};
    expect(() => testPlugin(code, translations)).toThrowError(/missing/);
  });

  test('tells what part is missing', () => {
    const code = '__obj("exists.missing.more")';
    const translations = { exists: { foo: 'bar' } };
    expect(() => testPlugin(code, translations)).toThrowError(/'missing'/);
  });

  [
    { type: 'string', value: 'incorrectly string' },
    { type: 'number', value: 123 },
    { type: 'boolean', value: false },
    { type: 'null', value: null },
    { type: 'array', value: [1, 2, 3] },
  ].map(({ type, value }) => {
    test(`does not allow ${type} to come through`, () => {
      const code = '__obj("key")';
      const translations = { key: value };
      expect(() => testPlugin(code, translations)).toThrow();
    });
  });
});
<commit_msg>Extend __obj() error test for functions<commit_after>const testPlugin = require('./testPlugin');

describe('__() errors', () => {
  test('throwing error for missing value', () => {
    const code = '__("missing")';
    const translations = {};
    expect(() => testPlugin(code, translations)).toThrowError(/missing/);
  });

  test('tells what part is missing', () => {
    const code = '__("exists.missing.more")';
    const translations = { exists: { foo: 'bar' } };
    expect(() => testPlugin(code, translations)).toThrowError(/'missing'/);
  });

  test('does not allow objects to come through', () => {
    const code = '__("this_should_be_string")';
    const translations = { this_should_be_string: { but: 'it is not' } };
    expect(() => testPlugin(code, translations)).toThrowError(/incorrect type.*'object'/);
  });

  test('does not allow arrays to come through', () => {
    const code = '__("this_should_be_string")';
    const translations = { this_should_be_string: ['but it is an array'] };
    expect(() => testPlugin(code, translations)).toThrowError(/incorrect type/);
  });

  test('does not allow functions to come through', () => {
    const code = '__("this_should_be_string")';
    const translations = { this_should_be_string: () => 'but it is a function' };
    expect(() => testPlugin(code, translations)).toThrowError(/incorrect type.*'function'/);
  });
});

describe('__obj() errors', () => {
  test('throwing error for missing value', () => {
    const code = '__obj("missing")';
    const translations = {};
    expect(() => testPlugin(code, translations)).toThrowError(/missing/);
  });

  test('tells what part is missing', () => {
    const code = '__obj("exists.missing.more")';
    const translations = { exists: { foo: 'bar' } };
    expect(() => testPlugin(code, translations)).toThrowError(/'missing'/);
  });

  [
    { type: 'string', value: 'incorrectly string' },
    { type: 'number', value: 123 },
    { type: 'boolean', value: false },
    { type: 'null', value: null },
    { type: 'array', value: [1, 2, 3] },
    { type: 'deep function', value: { aFunc: () => 'is not accepted' } },
  ].map(({ type, value }) => {
    test(`does not allow ${type} to come through`, () => {
      const code = '__obj("key")';
      const translations = { key: value };
      expect(() => testPlugin(code, translations)).toThrow();
    });
  });
});
<|endoftext|>


<commit_before>var expect = require('chai').expect;
var errors = require('../lib/Errors');

describe('Errors', function() {
  it('maps status codes to custom errors', function() {
    var unauthorized = errors('401');
    expect(unauthorized.type).to.eq('AuthenticationError');

    var unacceptable = errors('406');
    expect(unacceptable.type).to.eq('UnsupportedFormatRequestedError');
  });
});
<commit_msg>add some more coverage on errors<commit_after>var expect = require('chai').expect;
var errors = require('../lib/Errors');

describe('Errors', function() {
  it('maps status codes to custom errors', function() {
    var unauthorized = errors('401');
    expect(unauthorized.name).to.eq('Error');
    expect(unauthorized.type).to.eq('AuthenticationError');
    expect(unauthorized.message).to.eq(undefined);
    expect(unauthorized.stack).to.be.a('string');

    var unacceptable = errors('406', 'foo');
    expect(unauthorized.name).to.eq('Error');
    expect(unacceptable.type).to.eq('UnsupportedFormatRequestedError');
    expect(unacceptable.message).to.eq('foo');
    expect(unauthorized.stack).to.be.a('string');
  });
});
<|endoftext|>


<commit_before>const fs = require('fs-extra')
const path = require('path')

const watcher = require('../lib')

const {prepareFixtureDir, reportLogs, cleanupFixtureDir} = require('./helper')

describe('events', function () {
  let fixtureDir, watchDir, mainLogFile, workerLogFile
  let errors, events, sub

  beforeEach(async function () {
    ({fixtureDir, watchDir, mainLogFile, workerLogFile} = await prepareFixtureDir())
    errors = []
    events = []

    await watcher.configure({mainLog: mainLogFile, workerLog: workerLogFile})

    sub = await watcher.watch(watchDir, (err, es) => {
      errors.push(err)
      events.push(...es)
    })
  })

  afterEach(async function () {
    await sub.unwatch()
    await reportLogs(this.currentTest, mainLogFile, workerLogFile)
    await cleanupFixtureDir(fixtureDir)
  })

  function specMatches (spec, event) {
    return (spec.type === undefined || event.type === spec.type) &&
      (spec.kind === undefined || event.kind === spec.kind) &&
      (event.oldPath === undefined || event.oldPath === spec.oldPath) &&
      (event.newPath === (spec.newPath || ''))
  }

  function eventMatching (spec) {
    const isMatch = specMatches.bind(null, spec)
    return function () {
      return events.some(isMatch)
    }
  }

  function allEventsMatching (...specs) {
    const remaining = new Set(specs)

    return function () {
      for (const event of events) {
        for (const spec of remaining) {
          if (specMatches(spec, event)) {
            remaining.delete(spec)
          }
        }
      }

      return remaining.size === 0
    }
  }

  function orderedEventsMatching (...specs) {
    return function () {
      let specIndex = 0

      for (const event of events) {
        if (specs[specIndex] && specMatches(specs[specIndex], event)) {
          specIndex++
        }
      }

      return specIndex >= specs.length
    }
  }

  it('when a file is created', async function () {
    const createdFile = path.join(watchDir, 'file.txt')
    await fs.writeFile(createdFile, 'contents')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: createdFile
    }))
  })

  it('when a file is modified', async function () {
    const modifiedFile = path.join(watchDir, 'file.txt')
    await fs.writeFile(modifiedFile, 'initial contents\n')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: modifiedFile
    }))

    await fs.appendFile(modifiedFile, 'changed contents\n')
    await until('the modification event arrives', eventMatching({
      type: 'modified',
      kind: 'file',
      oldPath: modifiedFile
    }))
  })

  it('when a file is renamed', async function () {
    const oldPath = path.join(watchDir, 'old-file.txt')
    await fs.writeFile(oldPath, 'initial contents\n')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath,
      newPath: ''
    }))

    const newPath = path.join(watchDir, 'new-file.txt')

    await fs.rename(oldPath, newPath)

    await until('the rename event arrives', eventMatching({
      type: 'renamed',
      kind: 'file',
      oldPath,
      newPath
    }))
  })

  it('when a file is renamed from outside of the watch root in', async function () {
    const outsideFile = path.join(fixtureDir, 'file.txt')
    const insideFile = path.join(watchDir, 'file.txt')

    await fs.writeFile(outsideFile, 'contents')
    await fs.rename(outsideFile, insideFile)

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: insideFile
    }))
  })

  it('when a file is renamed from inside of the watch root out ^windows', async function () {
    const outsideFile = path.join(fixtureDir, 'file.txt')
    const insideFile = path.join(watchDir, 'file.txt')
    const flagFile = path.join(watchDir, 'flag.txt')

    await fs.writeFile(insideFile, 'contents')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: insideFile
    }))

    await fs.rename(insideFile, outsideFile)
    await fs.writeFile(flagFile, 'flag 1')

    await until('the flag file event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: flagFile
    }))

    // Trigger another batch of events on Linux
    await fs.writeFile(flagFile, 'flag 2')

    await until('the deletion event arrives', eventMatching({
      type: 'deleted',
      kind: 'file',
      oldPath: insideFile
    }))
  })

  it('when a file is deleted', async function () {
    const deletedPath = path.join(watchDir, 'file.txt')
    await fs.writeFile(deletedPath, 'initial contents\n')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: deletedPath
    }))

    await fs.unlink(deletedPath)

    await until('the deletion event arrives', eventMatching({
      type: 'deleted',
      oldPath: deletedPath
    }))
  })

  it('understands coalesced creation and deletion events', async function () {
    const deletedPath = path.join(watchDir, 'deleted.txt')
    const recreatedPath = path.join(watchDir, 'recreated.txt')
    const createdPath = path.join(watchDir, 'created.txt')

    await fs.writeFile(deletedPath, 'initial contents\n')
    await until('file creation event arrives', eventMatching(
      {type: 'created', kind: 'file', oldPath: deletedPath}
    ))

    await fs.unlink(deletedPath)
    await fs.writeFile(recreatedPath, 'initial contents\n')
    await fs.unlink(recreatedPath)
    await fs.writeFile(recreatedPath, 'newly created\n')
    await fs.writeFile(createdPath, 'and another\n')

    await until('all events arrive', orderedEventsMatching(
      {type: 'deleted', oldPath: deletedPath},
      {type: 'created', kind: 'file', oldPath: recreatedPath},
      {type: 'deleted', kind: 'file', oldPath: recreatedPath},
      {type: 'created', kind: 'file', oldPath: recreatedPath},
      {type: 'created', kind: 'file', oldPath: createdPath}
    ))
  })

  it('correlates rapid file rename events', async function () {
    const oldPath0 = path.join(watchDir, 'old-file-0.txt')
    const oldPath1 = path.join(watchDir, 'old-file-1.txt')
    const oldPath2 = path.join(watchDir, 'old-file-2.txt')
    const newPath0 = path.join(watchDir, 'new-file-0.txt')
    const newPath1 = path.join(watchDir, 'new-file-1.txt')
    const newPath2 = path.join(watchDir, 'new-file-2.txt')

    await Promise.all(
      [oldPath0, oldPath1, oldPath2].map(oldPath => fs.writeFile(oldPath, 'original\n'))
    )
    await until('all creation events arrive', allEventsMatching(
      {type: 'created', kind: 'file', oldPath: oldPath0},
      {type: 'created', kind: 'file', oldPath: oldPath1},
      {type: 'created', kind: 'file', oldPath: oldPath2}
    ))

    await Promise.all([
      fs.rename(oldPath0, newPath0),
      fs.rename(oldPath1, newPath1),
      fs.rename(oldPath2, newPath2)
    ])

    await until('all rename events arrive', allEventsMatching(
      {type: 'renamed', kind: 'file', oldPath: oldPath0, newPath: newPath0},
      {type: 'renamed', kind: 'file', oldPath: oldPath1, newPath: newPath1},
      {type: 'renamed', kind: 'file', oldPath: oldPath2, newPath: newPath2}
    ))
  })

  it('when a directory is created', async function () {
    const subdir = path.join(watchDir, 'subdir')
    await fs.mkdirs(subdir)

    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: subdir}
    ))
  })

  it('when a directory is renamed', async function () {
    const oldDir = path.join(watchDir, 'subdir')
    const newDir = path.join(watchDir, 'newdir')

    await fs.mkdirs(oldDir)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: oldDir}
    ))

    await fs.rename(oldDir, newDir)
    await until('directory rename event arrives', eventMatching(
      {type: 'renamed', kind: 'directory', oldPath: oldDir, newPath: newDir}
    ))
  })

  it('when a directory is deleted', async function () {
    const subdir = path.join(watchDir, 'subdir')
    await fs.mkdirs(subdir)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: subdir}
    ))

    await fs.rmdir(subdir)
    await until('directory deletion event arrives', eventMatching(
      {type: 'deleted', oldPath: subdir}
    ))
  })

  it('when a directory is deleted and a file is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    await fs.mkdir(reusedPath)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))

    await fs.rmdir(reusedPath)
    await fs.writeFile(reusedPath, 'IMMA FILE NOW, SURPRIIIISE\n')

    await until('deletion and creation events arrive', orderedEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))
  })

  it('when a directory is deleted and a file is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldFilePath = path.join(watchDir, 'oldfile')

    await Promise.all([
      fs.mkdirs(reusedPath),
      fs.writeFile(oldFilePath, 'original\n')
    ])
    await until('directory and file creation events arrive', allEventsMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath},
      {type: 'created', kind: 'file', oldPath: oldFilePath}
    ))

    await fs.rmdir(reusedPath)
    await fs.rename(oldFilePath, reusedPath)

    await until('deletion and rename events arrive', allEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'renamed', kind: 'file', oldPath: oldFilePath, newPath: reusedPath}
    ))
  })

  it('when a directory is renamed and a file is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const newDirPath = path.join(watchDir, 'newdir')

    await fs.mkdirs(reusedPath)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))

    await fs.rename(reusedPath, newDirPath)
    await fs.writeFile(reusedPath, 'oh look a file\n')

    await until('rename and creation events arrive', allEventsMatching(
      {type: 'renamed', kind: 'directory', oldPath: reusedPath, newPath: newDirPath},
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))
  })

  it('when a directory is renamed and a file is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldFilePath = path.join(watchDir, 'oldfile')
    const newDirPath = path.join(watchDir, 'newdir')

    await Promise.all([
      fs.mkdirs(reusedPath),
      fs.writeFile(oldFilePath, 'started as a file\n')
    ])
    await until('directory and file creation evenst arrive', allEventsMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath},
      {type: 'created', kind: 'file', oldPath: oldFilePath}
    ))

    await fs.rename(reusedPath, newDirPath)
    await fs.rename(oldFilePath, reusedPath)

    await until('rename events arrive', allEventsMatching(
      {type: 'renamed', kind: 'directory', oldPath: reusedPath, newPath: newDirPath},
      {type: 'renamed', kind: 'file', oldPath: oldFilePath, newPath: reusedPath}
    ))
  })

  it('when a file is deleted and a directory is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    await fs.writeFile(reusedPath, 'something\n')
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))

    await fs.unlink(reusedPath)
    await fs.mkdir(reusedPath)

    await until('delete and create events arrive', orderedEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))
  })

  it('when a file is deleted and a directory is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldDirPath = path.join(watchDir, 'olddir')

    await Promise.all([
      fs.writeFile(reusedPath, 'something\n'),
      fs.mkdir(oldDirPath)
    ])
    await until('creation events arrive', allEventsMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath},
      {type: 'created', kind: 'directory', oldPath: oldDirPath}
    ))

    await fs.unlink(reusedPath)
    await fs.rename(oldDirPath, reusedPath)

    await until('delete and rename events arrive', allEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'renamed', kind: 'directory', oldPath: oldDirPath, newPath: reusedPath}
    ))
  })

  it('when a file is renamed and a directory is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const newFilePath = path.join(watchDir, 'newfile')

    await fs.writeFile(reusedPath, 'something\n')
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))

    await fs.rename(reusedPath, newFilePath)
    await fs.mkdir(reusedPath)

    await until('rename and create events arrive', orderedEventsMatching(
      {type: 'renamed', kind: 'file', oldPath: reusedPath, newPath: newFilePath},
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))
  })

  it('when a file is renamed and a directory is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldDirPath = path.join(watchDir, 'olddir')
    const newFilePath = path.join(watchDir, 'newfile')

    await Promise.all([
      fs.writeFile(reusedPath, 'something\n'),
      fs.mkdir(oldDirPath)
    ])
    await until('file and directory creation events arrive', allEventsMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath},
      {type: 'created', kind: 'directory', oldPath: oldDirPath}
    ))

    await fs.rename(reusedPath, newFilePath)
    await fs.rename(oldDirPath, reusedPath)

    await until('rename events arrive', orderedEventsMatching(
      {type: 'renamed', kind: 'file', oldPath: reusedPath, newPath: newFilePath},
      {type: 'renamed', kind: 'directory', oldPath: oldDirPath, newPath: reusedPath}
    ))
  })
})
<commit_msg>Oh right Windows can't tell the kind of a deleted entry<commit_after>const fs = require('fs-extra')
const path = require('path')

const watcher = require('../lib')

const {prepareFixtureDir, reportLogs, cleanupFixtureDir} = require('./helper')

describe('events', function () {
  let fixtureDir, watchDir, mainLogFile, workerLogFile
  let errors, events, sub

  beforeEach(async function () {
    ({fixtureDir, watchDir, mainLogFile, workerLogFile} = await prepareFixtureDir())
    errors = []
    events = []

    await watcher.configure({mainLog: mainLogFile, workerLog: workerLogFile})

    sub = await watcher.watch(watchDir, (err, es) => {
      errors.push(err)
      events.push(...es)
    })
  })

  afterEach(async function () {
    await sub.unwatch()
    await reportLogs(this.currentTest, mainLogFile, workerLogFile)
    await cleanupFixtureDir(fixtureDir)
  })

  function specMatches (spec, event) {
    return (spec.type === undefined || event.type === spec.type) &&
      (spec.kind === undefined || event.kind === spec.kind) &&
      (event.oldPath === undefined || event.oldPath === spec.oldPath) &&
      (event.newPath === (spec.newPath || ''))
  }

  function eventMatching (spec) {
    const isMatch = specMatches.bind(null, spec)
    return function () {
      return events.some(isMatch)
    }
  }

  function allEventsMatching (...specs) {
    const remaining = new Set(specs)

    return function () {
      for (const event of events) {
        for (const spec of remaining) {
          if (specMatches(spec, event)) {
            remaining.delete(spec)
          }
        }
      }

      return remaining.size === 0
    }
  }

  function orderedEventsMatching (...specs) {
    return function () {
      let specIndex = 0

      for (const event of events) {
        if (specs[specIndex] && specMatches(specs[specIndex], event)) {
          specIndex++
        }
      }

      return specIndex >= specs.length
    }
  }

  it('when a file is created', async function () {
    const createdFile = path.join(watchDir, 'file.txt')
    await fs.writeFile(createdFile, 'contents')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: createdFile
    }))
  })

  it('when a file is modified', async function () {
    const modifiedFile = path.join(watchDir, 'file.txt')
    await fs.writeFile(modifiedFile, 'initial contents\n')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: modifiedFile
    }))

    await fs.appendFile(modifiedFile, 'changed contents\n')
    await until('the modification event arrives', eventMatching({
      type: 'modified',
      kind: 'file',
      oldPath: modifiedFile
    }))
  })

  it('when a file is renamed', async function () {
    const oldPath = path.join(watchDir, 'old-file.txt')
    await fs.writeFile(oldPath, 'initial contents\n')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath,
      newPath: ''
    }))

    const newPath = path.join(watchDir, 'new-file.txt')

    await fs.rename(oldPath, newPath)

    await until('the rename event arrives', eventMatching({
      type: 'renamed',
      kind: 'file',
      oldPath,
      newPath
    }))
  })

  it('when a file is renamed from outside of the watch root in', async function () {
    const outsideFile = path.join(fixtureDir, 'file.txt')
    const insideFile = path.join(watchDir, 'file.txt')

    await fs.writeFile(outsideFile, 'contents')
    await fs.rename(outsideFile, insideFile)

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: insideFile
    }))
  })

  it('when a file is renamed from inside of the watch root out ^windows', async function () {
    const outsideFile = path.join(fixtureDir, 'file.txt')
    const insideFile = path.join(watchDir, 'file.txt')
    const flagFile = path.join(watchDir, 'flag.txt')

    await fs.writeFile(insideFile, 'contents')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: insideFile
    }))

    await fs.rename(insideFile, outsideFile)
    await fs.writeFile(flagFile, 'flag 1')

    await until('the flag file event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: flagFile
    }))

    // Trigger another batch of events on Linux
    await fs.writeFile(flagFile, 'flag 2')

    await until('the deletion event arrives', eventMatching({
      type: 'deleted',
      kind: 'file',
      oldPath: insideFile
    }))
  })

  it('when a file is deleted', async function () {
    const deletedPath = path.join(watchDir, 'file.txt')
    await fs.writeFile(deletedPath, 'initial contents\n')

    await until('the creation event arrives', eventMatching({
      type: 'created',
      kind: 'file',
      oldPath: deletedPath
    }))

    await fs.unlink(deletedPath)

    await until('the deletion event arrives', eventMatching({
      type: 'deleted',
      oldPath: deletedPath
    }))
  })

  it('understands coalesced creation and deletion events', async function () {
    const deletedPath = path.join(watchDir, 'deleted.txt')
    const recreatedPath = path.join(watchDir, 'recreated.txt')
    const createdPath = path.join(watchDir, 'created.txt')

    await fs.writeFile(deletedPath, 'initial contents\n')
    await until('file creation event arrives', eventMatching(
      {type: 'created', kind: 'file', oldPath: deletedPath}
    ))

    await fs.unlink(deletedPath)
    await fs.writeFile(recreatedPath, 'initial contents\n')
    await fs.unlink(recreatedPath)
    await fs.writeFile(recreatedPath, 'newly created\n')
    await fs.writeFile(createdPath, 'and another\n')

    await until('all events arrive', orderedEventsMatching(
      {type: 'deleted', oldPath: deletedPath},
      {type: 'created', kind: 'file', oldPath: recreatedPath},
      {type: 'deleted', oldPath: recreatedPath},
      {type: 'created', kind: 'file', oldPath: recreatedPath},
      {type: 'created', kind: 'file', oldPath: createdPath}
    ))
  })

  it('correlates rapid file rename events', async function () {
    const oldPath0 = path.join(watchDir, 'old-file-0.txt')
    const oldPath1 = path.join(watchDir, 'old-file-1.txt')
    const oldPath2 = path.join(watchDir, 'old-file-2.txt')
    const newPath0 = path.join(watchDir, 'new-file-0.txt')
    const newPath1 = path.join(watchDir, 'new-file-1.txt')
    const newPath2 = path.join(watchDir, 'new-file-2.txt')

    await Promise.all(
      [oldPath0, oldPath1, oldPath2].map(oldPath => fs.writeFile(oldPath, 'original\n'))
    )
    await until('all creation events arrive', allEventsMatching(
      {type: 'created', kind: 'file', oldPath: oldPath0},
      {type: 'created', kind: 'file', oldPath: oldPath1},
      {type: 'created', kind: 'file', oldPath: oldPath2}
    ))

    await Promise.all([
      fs.rename(oldPath0, newPath0),
      fs.rename(oldPath1, newPath1),
      fs.rename(oldPath2, newPath2)
    ])

    await until('all rename events arrive', allEventsMatching(
      {type: 'renamed', kind: 'file', oldPath: oldPath0, newPath: newPath0},
      {type: 'renamed', kind: 'file', oldPath: oldPath1, newPath: newPath1},
      {type: 'renamed', kind: 'file', oldPath: oldPath2, newPath: newPath2}
    ))
  })

  it('when a directory is created', async function () {
    const subdir = path.join(watchDir, 'subdir')
    await fs.mkdirs(subdir)

    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: subdir}
    ))
  })

  it('when a directory is renamed', async function () {
    const oldDir = path.join(watchDir, 'subdir')
    const newDir = path.join(watchDir, 'newdir')

    await fs.mkdirs(oldDir)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: oldDir}
    ))

    await fs.rename(oldDir, newDir)
    await until('directory rename event arrives', eventMatching(
      {type: 'renamed', kind: 'directory', oldPath: oldDir, newPath: newDir}
    ))
  })

  it('when a directory is deleted', async function () {
    const subdir = path.join(watchDir, 'subdir')
    await fs.mkdirs(subdir)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: subdir}
    ))

    await fs.rmdir(subdir)
    await until('directory deletion event arrives', eventMatching(
      {type: 'deleted', oldPath: subdir}
    ))
  })

  it('when a directory is deleted and a file is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    await fs.mkdir(reusedPath)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))

    await fs.rmdir(reusedPath)
    await fs.writeFile(reusedPath, 'IMMA FILE NOW, SURPRIIIISE\n')

    await until('deletion and creation events arrive', orderedEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))
  })

  it('when a directory is deleted and a file is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldFilePath = path.join(watchDir, 'oldfile')

    await Promise.all([
      fs.mkdirs(reusedPath),
      fs.writeFile(oldFilePath, 'original\n')
    ])
    await until('directory and file creation events arrive', allEventsMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath},
      {type: 'created', kind: 'file', oldPath: oldFilePath}
    ))

    await fs.rmdir(reusedPath)
    await fs.rename(oldFilePath, reusedPath)

    await until('deletion and rename events arrive', allEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'renamed', kind: 'file', oldPath: oldFilePath, newPath: reusedPath}
    ))
  })

  it('when a directory is renamed and a file is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const newDirPath = path.join(watchDir, 'newdir')

    await fs.mkdirs(reusedPath)
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))

    await fs.rename(reusedPath, newDirPath)
    await fs.writeFile(reusedPath, 'oh look a file\n')

    await until('rename and creation events arrive', allEventsMatching(
      {type: 'renamed', kind: 'directory', oldPath: reusedPath, newPath: newDirPath},
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))
  })

  it('when a directory is renamed and a file is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldFilePath = path.join(watchDir, 'oldfile')
    const newDirPath = path.join(watchDir, 'newdir')

    await Promise.all([
      fs.mkdirs(reusedPath),
      fs.writeFile(oldFilePath, 'started as a file\n')
    ])
    await until('directory and file creation evenst arrive', allEventsMatching(
      {type: 'created', kind: 'directory', oldPath: reusedPath},
      {type: 'created', kind: 'file', oldPath: oldFilePath}
    ))

    await fs.rename(reusedPath, newDirPath)
    await fs.rename(oldFilePath, reusedPath)

    await until('rename events arrive', allEventsMatching(
      {type: 'renamed', kind: 'directory', oldPath: reusedPath, newPath: newDirPath},
      {type: 'renamed', kind: 'file', oldPath: oldFilePath, newPath: reusedPath}
    ))
  })

  it('when a file is deleted and a directory is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    await fs.writeFile(reusedPath, 'something\n')
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))

    await fs.unlink(reusedPath)
    await fs.mkdir(reusedPath)

    await until('delete and create events arrive', orderedEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))
  })

  it('when a file is deleted and a directory is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldDirPath = path.join(watchDir, 'olddir')

    await Promise.all([
      fs.writeFile(reusedPath, 'something\n'),
      fs.mkdir(oldDirPath)
    ])
    await until('creation events arrive', allEventsMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath},
      {type: 'created', kind: 'directory', oldPath: oldDirPath}
    ))

    await fs.unlink(reusedPath)
    await fs.rename(oldDirPath, reusedPath)

    await until('delete and rename events arrive', allEventsMatching(
      {type: 'deleted', oldPath: reusedPath},
      {type: 'renamed', kind: 'directory', oldPath: oldDirPath, newPath: reusedPath}
    ))
  })

  it('when a file is renamed and a directory is created in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const newFilePath = path.join(watchDir, 'newfile')

    await fs.writeFile(reusedPath, 'something\n')
    await until('directory creation event arrives', eventMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath}
    ))

    await fs.rename(reusedPath, newFilePath)
    await fs.mkdir(reusedPath)

    await until('rename and create events arrive', orderedEventsMatching(
      {type: 'renamed', kind: 'file', oldPath: reusedPath, newPath: newFilePath},
      {type: 'created', kind: 'directory', oldPath: reusedPath}
    ))
  })

  it('when a file is renamed and a directory is renamed in its place', async function () {
    const reusedPath = path.join(watchDir, 'reused')
    const oldDirPath = path.join(watchDir, 'olddir')
    const newFilePath = path.join(watchDir, 'newfile')

    await Promise.all([
      fs.writeFile(reusedPath, 'something\n'),
      fs.mkdir(oldDirPath)
    ])
    await until('file and directory creation events arrive', allEventsMatching(
      {type: 'created', kind: 'file', oldPath: reusedPath},
      {type: 'created', kind: 'directory', oldPath: oldDirPath}
    ))

    await fs.rename(reusedPath, newFilePath)
    await fs.rename(oldDirPath, reusedPath)

    await until('rename events arrive', orderedEventsMatching(
      {type: 'renamed', kind: 'file', oldPath: reusedPath, newPath: newFilePath},
      {type: 'renamed', kind: 'directory', oldPath: oldDirPath, newPath: reusedPath}
    ))
  })
})
<|endoftext|>


<commit_before>var mapInit = function() {
    var window_height = $(window).height();
    var canvas_height = window_height - (window_height/8);
    $('#map').height(canvas_height);

    var map = L.map('map').setView([42, -88], 5);
    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
                attribution: '© OpenStreetMap contributors'
    }).addTo(map);

    $(window).resize(adjustMapSize);

    popup = L.popup();

    map.on('click', onMapClick);

    $('#zipcode-form').submit(zipcodeQuery);

    function adjustMapSize() {
        var window_height = $(window).height();
        var canvas_height = window_height - (window_height/8);
        $('#map').height(canvas_height);
    }

    function onMapClick(e) {
        popup
            .setLatLng(e.latlng)
            .setContent(e.latlng.toString())
            .openOn(map);
    }

    function zipcodeQuery(e) {
        var postData = $(this).serialize();
        var formURL = $(this).attr("action");
        console.log(postData);
        $.ajax({
            url: formURL,
            type: 'post',
            data: postData,
            dataType: 'json',

            // addMarker
            success: addStoreMarkers,

            // handleZipcodeError
            error: handleAjaxError
        }); // ajax
        e.preventDefault(); //STOP default action
    } // zipcode submit

    function addStoreMarkers(data, textStatus, jqXHR) {
        console.log(textStatus);
        console.log(jqXHR.status);
        if (data.length>0) {
            for (var i=0; i<data.length; i++) {
                var marker = L.marker([data[i].lat, data[i].lng]).addTo(map);
                marker.bindPopup("Hello world!
I am a popup.");
            }
        }
    }

    function handleAjaxError(data, textStatus, jqXHR) {
        console.log(textStatus);
        console.log(jqXHR.status);
    }

}; // init

<commit_msg>cleaning up<commit_after>var mapInit = function() {
    var window_height = $(window).height();
    var canvas_height = window_height - (window_height/8);
    $('#map').height(canvas_height);

    var map = L.map('map').setView([42, -88], 5);
    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
                attribution: '© OpenStreetMap contributors'
    }).addTo(map);

    $(window).resize(adjustMapSize);

    popup = L.popup();

    map.on('click', onMapClick);

    $('#zipcode-form').submit(zipcodeQuery);

    function adjustMapSize() {
        var window_height = $(window).height();
        var canvas_height = window_height - (window_height/8);
        $('#map').height(canvas_height);
    }

    function onMapClick(e) {
        popup
            .setLatLng(e.latlng)
            .setContent(e.latlng.toString())
            .openOn(map);
    }

    function zipcodeQuery(e) {
        var postData = $(this).serialize();
        var formURL = $(this).attr("action");
        console.log(postData);
        $.ajax({
            url: formURL,
            type: 'post',
            data: postData,
            dataType: 'json',
            success: addStoreMarkers,
            error: handleAjaxError
        }); // ajax
        e.preventDefault(); //STOP default action
    } // zipcode submit

    function addStoreMarkers(data, textStatus, jqXHR) {
        console.log(textStatus);
        console.log(jqXHR.status);
        if (data.length>0) {
            for (var i=0; i<data.length; i++) {
                var marker = L.marker([data[i].lat, data[i].lng]).addTo(map);
                marker.bindPopup("Hello world!
I am a popup.");
            }
        }
    }

    function handleAjaxError(data, textStatus, jqXHR) {
        console.log(textStatus);
        console.log(jqXHR.status);
    }

}; // init

<|endoftext|>