<commit_before>/**
 * Draw On a Map Google Maps Javascript library.
 *
 * Script should be initialised like this:
 *
 * var doam = new DrawOnAMap(targetElementId);
 *
 */
function DrawOnAMap(elemId, options)
{
    /**
     * Plugin reference
     *
     * @access private
     */
    var plugin = this;

    /**
     * Defaults
     *
     * @access private
     */
    var defaults = {
        distanceBetweenMarkers: 1000,
        strokeColor: '#FF0000',
        strokeWeight: 2,
        fillColor: '#FF0000',
        fillOpacity: 0.2,
        mapOptions: {
            center:             new google.maps.LatLng(55.55, -2.06),
            zoom:               6,
            panControl:         false,
            zoomControl:        true,
            mapTypeControl:     false,
            scaleControl:       false,
            streetViewControl:  false,
            overviewMapControl: false,
            rotateControl:      false,
            mapTypeId:          google.maps.MapTypeId.ROADMAP,
            styles: [
                {
                    featureType: 'poi',
                    'stylers': [
                        {
                            visibility: 'off'
                        }
                    ]
                }
            ]
        }
    };

    /**
     * Class arrays
     *
     * @access private
     */
    var controls = [],
        activeListeners = [],
        markersArray = [],
        points = [];

    /**
     * Class references
     *
     * @access private
     */
    var lastMarker = null,
        trace = null,
        bounds = new google.maps.LatLngBounds();

    /**
     * Class boolean variables
     *
     * @access private
     */
    var beingDragged = false;

    /**
     * Class objects
     *
     * @access public
     */
    this.options = {},
    this.elem = {};

    // --------------------------- Public  Methods -------------------------- //

    /**
     * Class constructor
     *
     * @param function callback Callback function
     *
     * @access public
     */
    this.init = function(callback) {
        // Initialise public options
        plugin.options = _extend({}, defaults, options);

        // Get the element
        var elem = document.getElementById(elemId);

        // Test for element
        if (elem === null) {
           throw new Error('Unable to find element with id of: ' + elemId);
        }

        // Set element
        plugin.elem = elem;

        // Attempt to load the map state from storage
        var getparams = _getLocalStorage();

        if (Object.keys(getparams).length == 4) {

            // Get map params from url
            var zoom = parseInt(getparams.zoom);
            var center = new google.maps.LatLng(
                getparams.centerY,
                getparams.centerX
            );

            // Create new map and set draggable mode
            _createMap(true);

            // Loop through map points and add to map
            var mapPoints = JSON.parse(getparams.points);
            if (mapPoints.length > 0) {
                for(var i = 0; i < mapPoints.length; i++) {
                    _placeMarker(
                        new google.maps.LatLng(
                            mapPoints[i].k,
                            mapPoints[i].A
                        )
                    );
                }

                // Add first marker and redraw map
                _placeMarker(
                    points[0]
                );
            }

            // Set map zoom and center
            plugin.map.setCenter(center);
            plugin.map.setZoom(zoom);
        } else {
            // Create a new map
            _createMap(false);
        }

        // Call empty loader function if provided
        if (typeof callback === 'function') {
            callback();
        }
    }

    /**
     * Resets the map
     *
     * @access public
     */
    this.resetMap = function() {
       // Create new map and enable drawmode
       _createMap(false);

       // Remove the overlay data from memory
       _clearOverlays();

        // Clear the local storage
        window.localStorage.removeItem('DrawOnAMap:Points');
        window.localStorage.removeItem('DrawOnAMap:zoom');
        window.localStorage.removeItem('DrawOnAMap:center:x');
        window.localStorage.removeItem('DrawOnAMap:center:y');

        bounds = new google.maps.LatLngBounds();
        points = [];
        trace = null;

        // Call empty reset function
        this.onReset();
    }

    /**
     * Get the draggable state
     *
     * @access public
     */
    this.isDraggable = function() {
        return plugin.map.draggable;
    }

    /**
     * Return the map object
     *
     * @access public
     */
    this.getMap = function() {
        return this.map;
    }

    /**
     * @access public
     */
    this.getActiveListeners = function() {
        return activeListeners;
    }

    /**
     * Set the draggable set
     *
     * @access public
     */
    this.setActiveMode = function() {
        beingDragged = true;
    }

    /**
     * Unset the draggable set
     *
     * @access public
     */
    this.unSetActiveMode = function() {
        beingDragged = false;
    }

    /**
     * Get the draggable state
     *
     * @access public
     */
    this.isActive = function() {
       return beingDragged;
    }

    /**
     * Return the points array
     *
     * @access public
     */
    this.getPoints = function() {
        return points;
    }

    /**
     * Return the map bounds
     *
     * @access public
     */
    this.getBounds = function() {
        return bounds;
    }

    /**
     * Empty functions which can be overriden.
     *
     * @access public
     */
    this.onComplete = function() {}
    this.onReset = function() {}
    this.onDraw = function() {}
    this.onSaveState = function() {}
    this.onZoom = function() {}
    this.onIdle = function() {}

    /**
     * Method used to check if a point is inside the drawn area
     *
     * @access public
     */
    this.containsPoint = function(point) {
        return google.maps.geometry.poly.containsLocation(point, trace);
    }

    // --------------------------- Private Methods -------------------------- //

    /**
     * Drag end listener
     *
     * @access private
     */
    function _addIdleListener() {
        // Add draggable event listener
        if (_contains(activeListeners, 'idle') === false) {
            google.maps.event.addListener(plugin.map, 'idle', function() {
                 _saveMapState();
                 plugin.onIdle();
            });
            activeListeners.push('idle');
        }
    }

    /**
     * Mouse down listener
     *
     * @access private
     */
    function _addMouseDownListener() {
        // Add mouse down event handler
        if (_contains(activeListeners, 'mousedown') === false) {
            google.maps.event.addListener(plugin.map, 'mousedown', function(event) {

                // Unset any previous lines
                _clearOverlays();
                points = [];
                bounds = new google.maps.LatLngBounds();

                // Record the fact that the line is being drawn
                plugin.setActiveMode();

                // Place a marker at the start point
                _placeMarker(event.latLng);

                // Add the mouse move listener
                _addMouseMoveListener();

            });
            activeListeners.push('mousedown');
        }
   }

    /**
     * Mouse move listener
     *
     * @access private
     */
    function _addMouseMoveListener() {
        if (_contains(activeListeners, 'mousemove') === false) {
            google.maps.event.addListener(plugin.map, 'mousemove', function(event) {
                // Calculate the distance between the previous marker
                // and the co-ordinates currentlly underneath the mouse
                var distance = google.maps.geometry.spherical.computeDistanceBetween(
                    event.latLng,
                    lastMarker
                );

                // If the distance is greater than the threshold
                // amount, drop a new marker
                if (distance > plugin.options.distanceBetweenMarkers) {
                    _placeMarker(event.latLng);
                }
            });
            activeListeners.push('mousemove');
        }
    }

    /**
     * Mouse up listener
     *
     * @access private
     */
    function _addMouseUpListener() {
        // Add mouse up event handler
        google.maps.event.addListener(plugin.map, 'mouseup', function() {

            // Record the fact that the line is no longer being drawn
            plugin.unSetActiveMode();

            // Remove mouse move listener
            _removeListener('mousemove');

            // Add the start point to the array of points - in order to
            // close up the polygon
            _placeMarker(points[0]);

            // If there are fewer than 3 points, we can't dray a line,
            // so reset the line
            if(points.length < 4) {
                _clearOverlays();
                points = [];
                throw new Error(
                    'To draw hold down the left mouse button and ' +
                    'draw your search area by dragging the ' +
                    'cursor/mouse. Do not just click, instead ' +
                    'hold the left button down.'
                );
            }

            // Turn off draw mode and turn on drag mode
            _toggleDragMode(true);

            // Save the map state
            _saveMapState();
        });
    }

    /**
     * Zoom changed listener
     *
     * @access private
     */
    function _addZoomChangedListener() {
        // Add zoom change event listener
        if (_contains(activeListeners, 'zoom_changed') === false) {
            google.maps.event.addListener(plugin.map, 'zoom_changed', function() {
                // Call zoomStart for custom functions
                plugin.onZoom();

                // Unset active mode
                if (plugin.isActive()) {
                    plugin.unSetActiveMode();
                }

                // Saving zoom level is handled by the idle listener
            });
            activeListeners.push('zoom_changed');
        }
    }

    /**
     * Clear and add in map controls
     *
     * @access private
     */
    function _addControls() {

        // Set CSS styles for the DIV containing the control
        // Setting padding to 5 px will offset the control
        // from the edge of the map
        var container = document.createElement('div');
        container.index = 1;
        container.className = 'dysMapControlContainer';

        // Add in controls to map
        for(i in controls) {
            // Setup the click event listeners
            google.maps.event.addDomListener(
                controls[i].control,
                'click',
                controls[i].func
            );

            container.appendChild(controls[i].control);
        }

        plugin.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
            container
        );
    }

    /**
     * Markers array setter
     *
     * @param object marker
     *
     * @access private
     */
    function _addToMarkersArray(marker) {
        markersArray.push(marker);
    }

    /**
     * Clears all overlays from the map
     *
     * @access private
     */
    function _clearOverlays() {
        if (this.markersArray) {
            for (i in this.markersArray) {
                markersArray[i].setMap(null);
            }
            markersArray = [];
        }
        if (trace) {
            trace.setMap(null);
            trace = null;
        }
    }

    /**
     * Array contains function
     *
     * @param Array  a   comparison array
     * @param Object obj comparison object
     *
     * @access private
     */
    function _contains(a, obj) {
        var i = a.length;
        while (i--) {
            if (a[i] === obj) {
                return true;
            }
        }
        return false;
    }

   /**
    * Create the map object
    *
    * @param boolean draggable Toggle Dragable State
    *
    * @access private
    */
   function _createMap(draggable) {
        // Create new google map with provided options
        plugin.map = new google.maps.Map(
           plugin.elem,
           plugin.options.mapOptions
        );

        // Add reset control
        _createControl('Reset', function() {
            plugin.resetMap();
        });

        // Add Drag function in
        _createControl('Drag', function() {
            _toggleDragMode(!plugin.isDraggable());
        });

        // Add in controls to map
        _addControls();

        // Turn off dragable state on first load
        _toggleDragMode(draggable);
    }

    /**
     * Function to create a custom map control
     *
     * @param string   label
     * @param function func
     *
     * @access private
     */
    function _createControl(label, func) {

        // Create container div
        var controlDiv = document.createElement('div');
        controlDiv.className = 'dysMapControl';

        // Set CSS for the control border
        var controlUI = document.createElement('a');
        controlUI.className = 'dysMapControlInner';
        controlDiv.appendChild(controlUI);

        // Set CSS for the control interior
        var controlText = document.createElement('span');
        controlText.innerHTML = label;
        controlText.className = 'dysMapControlLabel';
        controlUI.appendChild(controlText);

        // Add to array
        controls[label] = {
            control: controlDiv,
            controlUI: controlUI,
            func: func
        }
    }

    /**
     * Add a line onto the map
     *
     * @access private
     */
    function _drawLine() {
        // Remove any previous lines
        _clearOverlays();

        // Call empty draw function
        plugin.onDraw();

        // Draw the line/polygon
        if (points[0] == points[points.length-1] && points.length > 2) {
            // The trace has been completed, so display a polygon
            trace = new google.maps.Polygon({
                path: points,
                strokeColor: plugin.options.strokeColor,
                fillColor: plugin.options.fillColor,
                fillOpacity: plugin.options.fillOpacity,
                strokeWeight: plugin.options.strokeWeight,
                clickable: false // This needs to be false, otherwise the line
                                 // consumes the 'mouseup' event if the mouse is
                                 // over the line when the mouse button is released
            });

            // Call the complete function
            plugin.onComplete();
        } else {
            // The trace hasn't been completed yet, so display a line
            trace = new google.maps.Polyline({
                path: points,
                strokeColor: plugin.options.strokeColor,
                strokeWeight: plugin.options.strokeWeight,
                clickable: false // This needs to be false, otherwise the line
                                 // consumes the 'mouseup' event if the mouse is
                                 // over the line when the mouse button is released
            });
        }

        _addToMarkersArray(trace);
        trace.setMap(plugin.map);
    }

    /**
     * Extend the options of the class
     *
     * @return array
     */
    function _extend() {
        for(var i=1; i<arguments.length; i++) {
            for(var key in arguments[i]) {
                if(arguments[i].hasOwnProperty(key)) {
                    arguments[0][key] = arguments[i][key];
                }
            }
        }
        return arguments[0];
    }

    /**
     * Get all of the get parameters from local storage
     *
     * @access private
     */
    function _getLocalStorage() {
        var vars = {};
        if (plugin.persistMap === true) {
            if (typeof Storage !== 'undefined') {
                if (window.localStorage.getItem('DrawOnAMap:Points') !== null) {
                    vars.points = window.localStorage.getItem('DrawOnAMap:Points');
                    vars.zoom = window.localStorage.getItem('DrawOnAMap:zoom');
                    vars.centerX = window.localStorage.getItem('DrawOnAMap:center:x');
                    vars.centerY = window.localStorage.getItem('DrawOnAMap:center:y');
                }
            } else {
                throw new Error(
                    'Unable to persist! Local Storage is not available.'
                );
            }
        }
        return vars;
    }

    /**
     * Add a marker into the global points array
     *
     * @param LatLng location
     *
     * @access private
     */
    function _placeMarker(location) {
        lastMarker = location;
        points.push(location);
        bounds.extend(location);
        _drawLine();
    }

    /**
     * Remove a specific listener
     *
     * @access private
     */
    function _removeListener(listener) {
        google.maps.event.clearListeners(plugin.map, listener);
        if (_contains(activeListeners, listener)) {
            activeListeners.splice(listener, 1);
        }
    }

    /**
     * This function reloads the url to persist the map state
     *
     * @access private
     */
    function _saveMapState() {
        if (plugin.persistMap === true) {
            if (typeof Storage !== 'undefined' && typeof JSON !== 'undefined') {
                var mapPoints = JSON.stringify(points);
                window.localStorage.setItem('DrawOnAMap:Points', mapPoints);
                window.localStorage.setItem('DrawOnAMap:zoom', plugin.map.getZoom());
                window.localStorage.setItem('DrawOnAMap:center:x', plugin.map.getCenter().lng());
                window.localStorage.setItem('DrawOnAMap:center:y', plugin.map.getCenter().lat());
            } else {
                throw new Error(
                    'Unable to persist! Local Storage is not available.'
                );
            }
        }

        // Call save state function
        plugin.onSaveState();
    }

   /**
    * Turn dragable state on/off
    *
    * @param boolean draggable
    *
    * @access private
    */
    function _toggleDragMode(draggable) {
        // Add draw controls if not in drag mode
        if(!draggable) {
            _removeListener('zoom_changed');
            _removeListener('idle');
            _addMouseDownListener();
            _addMouseUpListener();
            controls['Drag'].controlUI.innerHTML = 'Drag';
        } else {
            _removeListener('mousemove');
            _removeListener('mousedown');
            _removeListener('mouseup');
            _addZoomChangedListener();
            _addIdleListener();
            controls['Drag'].controlUI.innerHTML = 'Draw';
        }

        // Set draggable state on map
        plugin.map.setOptions({
            draggable: draggable
        });
    }
}
<commit_msg>Added map search prototype<commit_after>/**
 * Object used for map searching
 *
 * @type object
 */
function MapSearch()
{
    /**
     * Default google map options
     *
     * @type object
     */
    var defaultMapOptions = {
        center:             new google.maps.LatLng(55.55, -2.06),
        zoom:               6,
        panControl:         false,
        zoomControl:        true,
        mapTypeControl:     false,
        scaleControl:       false,
        streetViewControl:  false,
        overviewMapControl: false,
        rotateControl:      false,
        mapTypeId:          google.maps.MapTypeId.ROADMAP,
        styles: [
            {
                featureType: 'poi',
                'stylers': [
                    {
                        visibility: 'off'
                    }
                ]
            }
        ]
    };

    /**
     * Return the default map options
     *
     * @returns object
     */
    this.getDefaultMapOptions = function() {
        return defaultMapOptions;
    };

    /**
     * Extend the options of the class
     *
     * @access public
     *
     * @return array
     */
    this.extend = function() {
        for(var i=1; i<arguments.length; i++) {
            for(var key in arguments[i]) {
                if(arguments[i].hasOwnProperty(key)) {
                    arguments[0][key] = arguments[i][key];
                }
            }
        }
        return arguments[0];
    };

    /**
     * Array contains function
     *
     * @param Array  a   comparison array
     * @param Object obj comparison object
     *
     * @access public
     */
    this.contains = function(a, obj) {
        var i = a.length;
        while (i--) {
            if (a[i] === obj) {
                return true;
            }
        }
        return false;
    };
}

/**
 * Draw On a Map Google Maps Javascript library.
 *
 * Script should be initialised like this:
 *
 * var doam = new DrawOnAMap(targetElementId);
 *
 */
function DrawOnAMap(elemId, options)
{
    /**
     * Plugin reference
     *
     * @access private
     */
    var plugin = this;

    /**
     * Defaults
     *
     * @access private
     */
    var defaults = {
        distanceBetweenMarkers: 1000,
        strokeColor: '#FF0000',
        strokeWeight: 2,
        fillColor: '#FF0000',
        fillOpacity: 0.2
    };

    /**
     * Class arrays
     *
     * @access private
     */
    var controls = [],
        activeListeners = [],
        markersArray = [],
        points = [];

    /**
     * Class references
     *
     * @access private
     */
    var lastMarker = null,
        trace = null,
        bounds = new google.maps.LatLngBounds();

    /**
     * Class boolean variables
     *
     * @access private
     */
    var beingDragged = false;

    /**
     * Class objects
     *
     * @access public
     */
    this.options = {},
    this.elem = {};

    // --------------------------- Public  Methods -------------------------- //

    /**
     * Class constructor
     *
     * @param function callback Callback function
     *
     * @access public
     */
    this.init = function(callback) {

        // Merge map defaults with line defaults
        defaults.mapOptions = plugin.getDefaultMapOptions();

        // Initialise public options
        plugin.options = plugin.extend({}, defaults, options);

        // Get the element
        var elem = document.getElementById(elemId);

        // Test for element
        if (elem === null) {
           throw new Error('Unable to find element with id of: ' + elemId);
        }

        // Set element
        plugin.elem = elem;

        // Attempt to load the map state from storage
        var getparams = _getLocalStorage();

        if (Object.keys(getparams).length == 4) {

            // Get map params from url
            var zoom = parseInt(getparams.zoom);
            var center = new google.maps.LatLng(
                getparams.centerY,
                getparams.centerX
            );

            // Create new map and set draggable mode
            _createMap(true);

            // Loop through map points and add to map
            var mapPoints = JSON.parse(getparams.points);
            if (mapPoints.length > 0) {
                for(var i = 0; i < mapPoints.length; i++) {
                    _placeMarker(
                        new google.maps.LatLng(
                            mapPoints[i].k,
                            mapPoints[i].A
                        )
                    );
                }

                // Add first marker and redraw map
                _placeMarker(
                    points[0]
                );
            }

            // Set map zoom and center
            plugin.map.setCenter(center);
            plugin.map.setZoom(zoom);
        } else {
            // Create a new map
            _createMap(false);
        }

        // Call empty loader function if provided
        if (typeof callback === 'function') {
            callback();
        }
    };

    /**
     * Resets the map
     *
     * @access public
     */
    this.resetMap = function() {
       // Create new map and enable drawmode
       _createMap(false);

       // Remove the overlay data from memory
       _clearOverlays();

        // Clear the local storage
        window.localStorage.removeItem('DrawOnAMap:Points');
        window.localStorage.removeItem('DrawOnAMap:zoom');
        window.localStorage.removeItem('DrawOnAMap:center:x');
        window.localStorage.removeItem('DrawOnAMap:center:y');

        bounds = new google.maps.LatLngBounds();
        points = [];
        trace = null;

        // Call empty reset function
        this.onReset();
    };

    /**
     * Get the draggable state
     *
     * @access public
     */
    this.isDraggable = function() {
        return plugin.map.draggable;
    };

    /**
     * Return the map object
     *
     * @access public
     */
    this.getMap = function() {
        return this.map;
    };

    /**
     * @access public
     */
    this.getActiveListeners = function() {
        return activeListeners;
    };

    /**
     * Set the draggable set
     *
     * @access public
     */
    this.setActiveMode = function() {
        beingDragged = true;
    };

    /**
     * Unset the draggable set
     *
     * @access public
     */
    this.unSetActiveMode = function() {
        beingDragged = false;
    };

    /**
     * Get the draggable state
     *
     * @access public
     */
    this.isActive = function() {
       return beingDragged;
    };

    /**
     * Return the points array
     *
     * @access public
     */
    this.getPoints = function() {
        return points;
    };

    /**
     * Return the map bounds
     *
     * @access public
     */
    this.getBounds = function() {
        return bounds;
    };

    /**
     * Empty functions which can be overriden.
     *
     * @access public
     */
    this.onComplete = function() {};
    this.onReset = function() {};
    this.onDraw = function() {};
    this.onSaveState = function() {};
    this.onZoom = function() {};
    this.onIdle = function() {};

    /**
     * Method used to check if a point is inside the drawn area
     *
     * @param LatLng point Google latlng object
     *
     * @access public
     */
    this.containsPoint = function(point) {
        return google.maps.geometry.poly.containsLocation(point, trace);
    };

    // --------------------------- Private Methods -------------------------- //

    /**
     * Drag end listener
     *
     * @access private
     */
    function _addIdleListener() {
        // Add draggable event listener
        if (plugin.contains(activeListeners, 'idle') === false) {
            google.maps.event.addListener(plugin.map, 'idle', function() {
                 _saveMapState();
                 plugin.onIdle();
            });
            activeListeners.push('idle');
        }
    }

    /**
     * Mouse down listener
     *
     * @access private
     */
    function _addMouseDownListener() {
        // Add mouse down event handler
        if (plugin.contains(activeListeners, 'mousedown') === false) {
            google.maps.event.addListener(plugin.map, 'mousedown', function(event) {

                // Unset any previous lines
                _clearOverlays();
                points = [];
                bounds = new google.maps.LatLngBounds();

                // Record the fact that the line is being drawn
                plugin.setActiveMode();

                // Place a marker at the start point
                _placeMarker(event.latLng);

                // Add the mouse move listener
                _addMouseMoveListener();

            });
            activeListeners.push('mousedown');
        }
   }

    /**
     * Mouse move listener
     *
     * @access private
     */
    function _addMouseMoveListener() {
        if (plugin.contains(activeListeners, 'mousemove') === false) {
            google.maps.event.addListener(plugin.map, 'mousemove', function(event) {
                // Calculate the distance between the previous marker
                // and the co-ordinates currentlly underneath the mouse
                var distance = google.maps.geometry.spherical.computeDistanceBetween(
                    event.latLng,
                    lastMarker
                );

                // If the distance is greater than the threshold
                // amount, drop a new marker
                if (distance > plugin.options.distanceBetweenMarkers) {
                    _placeMarker(event.latLng);
                }
            });
            activeListeners.push('mousemove');
        }
    }

    /**
     * Mouse up listener
     *
     * @access private
     */
    function _addMouseUpListener() {
        // Add mouse up event handler
        google.maps.event.addListener(plugin.map, 'mouseup', function() {

            // Record the fact that the line is no longer being drawn
            plugin.unSetActiveMode();

            // Remove mouse move listener
            _removeListener('mousemove');

            // Add the start point to the array of points - in order to
            // close up the polygon
            _placeMarker(points[0]);

            // If there are fewer than 3 points, we can't dray a line,
            // so reset the line
            if(points.length < 4) {
                _clearOverlays();
                points = [];
                throw new Error(
                    'To draw hold down the left mouse button and ' +
                    'draw your search area by dragging the ' +
                    'cursor/mouse. Do not just click, instead ' +
                    'hold the left button down.'
                );
            }

            // Turn off draw mode and turn on drag mode
            _toggleDragMode(true);

            // Save the map state
            _saveMapState();
        });
    }

    /**
     * Zoom changed listener
     *
     * @access private
     */
    function _addZoomChangedListener() {
        // Add zoom change event listener
        if (plugin.contains(activeListeners, 'zoom_changed') === false) {
            google.maps.event.addListener(plugin.map, 'zoom_changed', function() {
                // Call zoomStart for custom functions
                plugin.onZoom();

                // Unset active mode
                if (plugin.isActive()) {
                    plugin.unSetActiveMode();
                }

                // Saving zoom level is handled by the idle listener
            });
            activeListeners.push('zoom_changed');
        }
    }

    /**
     * Clear and add in map controls
     *
     * @access private
     */
    function _addControls() {

        // Set CSS styles for the DIV containing the control
        // Setting padding to 5 px will offset the control
        // from the edge of the map
        var container = document.createElement('div');
        container.index = 1;
        container.className = 'dysMapControlContainer';

        // Add in controls to map
        for(i in controls) {
            // Setup the click event listeners
            google.maps.event.addDomListener(
                controls[i].control,
                'click',
                controls[i].func
            );

            container.appendChild(controls[i].control);
        }

        plugin.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
            container
        );
    }

    /**
     * Markers array setter
     *
     * @param object marker
     *
     * @access private
     */
    function _addToMarkersArray(marker) {
        markersArray.push(marker);
    }

    /**
     * Clears all overlays from the map
     *
     * @access private
     */
    function _clearOverlays() {
        if (this.markersArray) {
            for (i in this.markersArray) {
                markersArray[i].setMap(null);
            }
            markersArray = [];
        }
        if (trace) {
            trace.setMap(null);
            trace = null;
        }
    }

   /**
    * Create the map object
    *
    * @param boolean draggable Toggle Dragable State
    *
    * @access private
    */
   function _createMap(draggable) {
        // Create new google map with provided options
        plugin.map = new google.maps.Map(
           plugin.elem,
           plugin.options.mapOptions
        );

        // Add reset control
        _createControl('Reset', function() {
            plugin.resetMap();
        });

        // Add Drag function in
        _createControl('Drag', function() {
            _toggleDragMode(!plugin.isDraggable());
        });

        // Add in controls to map
        _addControls();

        // Turn off dragable state on first load
        _toggleDragMode(draggable);
    }

    /**
     * Function to create a custom map control
     *
     * @param string   label
     * @param function func
     *
     * @access private
     */
    function _createControl(label, func) {

        // Create container div
        var controlDiv = document.createElement('div');
        controlDiv.className = 'dysMapControl';

        // Set CSS for the control border
        var controlUI = document.createElement('a');
        controlUI.className = 'dysMapControlInner';
        controlDiv.appendChild(controlUI);

        // Set CSS for the control interior
        var controlText = document.createElement('span');
        controlText.innerHTML = label;
        controlText.className = 'dysMapControlLabel';
        controlUI.appendChild(controlText);

        // Add to array
        controls[label] = {
            control: controlDiv,
            controlUI: controlUI,
            func: func
        }
    }

    /**
     * Add a line onto the map
     *
     * @access private
     */
    function _drawLine() {
        // Remove any previous lines
        _clearOverlays();

        // Call empty draw function
        plugin.onDraw();

        // Draw the line/polygon
        if (points[0] == points[points.length-1] && points.length > 2) {
            // The trace has been completed, so display a polygon
            trace = new google.maps.Polygon({
                path: points,
                strokeColor: plugin.options.strokeColor,
                fillColor: plugin.options.fillColor,
                fillOpacity: plugin.options.fillOpacity,
                strokeWeight: plugin.options.strokeWeight,
                clickable: false // This needs to be false, otherwise the line
                                 // consumes the 'mouseup' event if the mouse is
                                 // over the line when the mouse button is released
            });

            // Call the complete function
            plugin.onComplete();
        } else {
            // The trace hasn't been completed yet, so display a line
            trace = new google.maps.Polyline({
                path: points,
                strokeColor: plugin.options.strokeColor,
                strokeWeight: plugin.options.strokeWeight,
                clickable: false // This needs to be false, otherwise the line
                                 // consumes the 'mouseup' event if the mouse is
                                 // over the line when the mouse button is released
            });
        }

        _addToMarkersArray(trace);
        trace.setMap(plugin.map);
    }

    /**
     * Get all of the get parameters from local storage
     *
     * @access private
     */
    function _getLocalStorage() {
        var vars = {};
        if (plugin.persistMap === true) {
            if (typeof Storage !== 'undefined') {
                if (window.localStorage.getItem('DrawOnAMap:Points') !== null) {
                    vars.points = window.localStorage.getItem('DrawOnAMap:Points');
                    vars.zoom = window.localStorage.getItem('DrawOnAMap:zoom');
                    vars.centerX = window.localStorage.getItem('DrawOnAMap:center:x');
                    vars.centerY = window.localStorage.getItem('DrawOnAMap:center:y');
                }
            } else {
                throw new Error(
                    'Unable to persist! Local Storage is not available.'
                );
            }
        }
        return vars;
    }

    /**
     * Add a marker into the global points array
     *
     * @param LatLng location
     *
     * @access private
     */
    function _placeMarker(location) {
        lastMarker = location;
        points.push(location);
        bounds.extend(location);
        _drawLine();
    }

    /**
     * Remove a specific listener
     *
     * @access private
     */
    function _removeListener(listener) {
        google.maps.event.clearListeners(plugin.map, listener);
        if (plugin.contains(activeListeners, listener)) {
            activeListeners.splice(listener, 1);
        }
    }

    /**
     * This function reloads the url to persist the map state
     *
     * @access private
     */
    function _saveMapState() {
        if (plugin.persistMap === true) {
            if (typeof Storage !== 'undefined' && typeof JSON !== 'undefined') {
                var mapPoints = JSON.stringify(points);
                window.localStorage.setItem('DrawOnAMap:Points', mapPoints);
                window.localStorage.setItem('DrawOnAMap:zoom', plugin.map.getZoom());
                window.localStorage.setItem('DrawOnAMap:center:x', plugin.map.getCenter().lng());
                window.localStorage.setItem('DrawOnAMap:center:y', plugin.map.getCenter().lat());
            } else {
                throw new Error(
                    'Unable to persist! Local Storage is not available.'
                );
            }
        }

        // Call save state function
        plugin.onSaveState();
    }

   /**
    * Turn dragable state on/off
    *
    * @param boolean draggable
    *
    * @access private
    */
    function _toggleDragMode(draggable) {
        // Add draw controls if not in drag mode
        if(!draggable) {
            _removeListener('zoom_changed');
            _removeListener('idle');
            _addMouseDownListener();
            _addMouseUpListener();
            controls['Drag'].controlUI.innerHTML = 'Drag';
        } else {
            _removeListener('mousemove');
            _removeListener('mousedown');
            _removeListener('mouseup');
            _addZoomChangedListener();
            _addIdleListener();
            controls['Drag'].controlUI.innerHTML = 'Draw';
        }

        // Set draggable state on map
        plugin.map.setOptions({
            draggable: draggable
        });
    }
}

/**
 * Inherit Map Search Functions
 */
DrawOnAMap.prototype = new MapSearch();<|endoftext|>


<commit_before>// Run tasks whenever watched files change
module.exports = {
	all: {
		files: ['<%= dev.html %>/**/*.html',
				'<%= dev.styles %>/*.{scss,sass,less,styl}',
				'<%= dev.css %>/*.css',
				'<%= dev.coffee %>/**/*.coffee',
				'<%= dev.js %>/**/*.js',
				'<%= dev.img %>/**/*.{png,jpg,gif}',
				'<%= dev.templates %>/pages/**/*.jade',
				'<%= dev.php %>/**/*.php',
				'<%= dev.fonts %>/**/*.{eot,svg,ttf,otf,woff,woff2}'],
		tasks: ['default'],
		options: {
			spawn: false,
		},
	}
}
<commit_msg>watch task fixed<commit_after>// Run tasks whenever watched files change
module.exports = {
	all: {
		files: ['<%= dev.html %>/**/*.html',
				'<%= dev.less %>/*.less',
				'<%= dev.scss %>/*.scss',
				'<%= dev.sass %>/*.sass',
				'<%= dev.stylus %>/*.styl',
				'<%= dev.css %>/*.css',
				'<%= dev.coffee %>/**/*.coffee',
				'<%= dev.js %>/**/*.js',
				'<%= dev.img %>/**/*.{png,jpg,gif}',
				'<%= dev.jade %>/pages/**/*.jade',
				'<%= dev.php %>/**/*.php',
				'<%= dev.fonts %>/**/*.{eot,svg,ttf,otf,woff,woff2}'],
		tasks: ['default'],
		options: {
			spawn: false,
		},
	}
}
<|endoftext|>


<commit_before>(function($) {
    $("body").ajaxError(
        function(event, request, settings, error) {
            var data;
            TCM.removeLoading();
            if(error === "UNAUTHORIZED" || error === "FORBIDDEN") {
                data = $.parseJSON(request.responseText);
                window.location = data.login_url + "?next=" + window.location.pathname;
            } else {
                // @@@ do something nicer than an alert()?
                alert("Help! Something broke, and we're not sure just what. Try reloading the page.");
            }
        });

    $.ajaxSetup(
        {
            dataType: "json",
            dataFilter: function(data, type) {
                if (type == "json") {
                    var messagelist = $("#messages ul"),
                    parsed = $.parseJSON(data),
                    messages = $(parsed.messages);
                    messages.each(function() {
                        $(ich.message(this)).appendTo(messagelist);
                    });
                    $('#messages').messages();
                }
                return data;
            }
        }
    );
})(jQuery);<commit_msg>Ditch alert() on unknown ajax error.<commit_after>(function($) {
    $("body").ajaxError(
        function(event, request, settings, error) {
            var data;
            TCM.removeLoading();
            if(error === "UNAUTHORIZED" || error === "FORBIDDEN") {
                data = $.parseJSON(request.responseText);
                window.location = data.login_url + "?next=" + window.location.pathname;
            } // @@@ any global default error handling needed?
        });

    $.ajaxSetup(
        {
            dataType: "json",
            dataFilter: function(data, type) {
                if (type == "json") {
                    var messagelist = $("#messages ul"),
                    parsed = $.parseJSON(data),
                    messages = $(parsed.messages);
                    messages.each(function() {
                        $(ich.message(this)).appendTo(messagelist);
                    });
                    $('#messages').messages();
                }
                return data;
            }
        }
    );
})(jQuery);
<|endoftext|>


<commit_before>import createStaticNode from './shapes/staticNode';
import isArray from '../util/isArray';
import isStringOrNumber from '../util/isStringOrNumber';
import isVoid from '../util/isVoid';
import DOMRegistry from '../DOM/DOMRegistry';
import quoteAttributeValueForBrowser from './quoteAttributeValueForBrowser';

function countChildren(children) {
	if (!isVoid(children)) {
		if (isArray(children)) {
			return children.length;
		} else {
			return 1;
		}
	} else {
		return 0;
	}
}

/**
 *  WORK IN PROGRESS
 *
 *  Need to run tests for this one!!
 *
 * */
function renderMarkupForStyles(styles, component) {

	let serialized = '';

	for (let styleName in styles) {

		let styleValue = styles[styleName];

		if (styleValue != null) {
			serialized += styleName + ':';
			serialized += styleValue + ';';
		}
	}
	return serialized || null;

}

function renderMarkupForAttributes(name, value) {

	const propertyInfo = DOMRegistry[name] || null;

	if (propertyInfo) {

		if ( isVoid( value ) ||
			propertyInfo.hasBooleanValue && !value ||
			propertyInfo.hasNumericValue && ( value !== value ) ||
			propertyInfo.hasPositiveNumericValue && value < 1 ||
			value.length === 0 ) {
			return '';
		}
		let attributeName = propertyInfo.attributeName;
		if (propertyInfo.hasBooleanValue) {
			return attributeName + '=""';
		}
		return attributeName + '=' + quoteAttributeValueForBrowser(value);
	} else {

		if (value == null) {
			return '';
		}
		return name + '=' + quoteAttributeValueForBrowser(value);

	}
}
function createStaticAttributes(node, excludeAttrs) {

	let HTML = '';

	const props = node.attrs;

	for (let propKey in props) {

		var propValue = props[propKey];
		if (propValue == null) {
			continue;
		}
		renderMarkupForStyles
		if (propKey === 'style') {
			propValue = renderMarkupForStyles(propKey, propValue );
		}

		let markup = null;

		markup =  renderMarkupForAttributes(propKey, propValue );

		if (markup) {
			HTML += ' ' + markup;
		}

	}
	return HTML;

 // TODO
	return Object.keys(node.attrs).map(attr => {
		if (attr === 'data-inferno') {
			return `${ attr }`;
		}
		return `${ attr }="${ node.attrs[attr] }"`;
	}).join(' ');
}

function createStaticTreeChildren(children) {
	let isLastChildNode = false;

	if (isArray(children)) {
		return children.map((child, i) => {
			if (isStringOrNumber(child)) {
				if (isLastChildNode) {
					isLastChildNode = true;
					return '<!---->' + child;
				} else {
					isLastChildNode = true;
					return child;
				}
			}
			isLastChildNode = false;
			return createStaticTreeNode(false, child);
		}).join('');
	} else {
		if (isStringOrNumber(children)) {
			return children;
		} else {
			return createStaticTreeNode(false, children);
		}
	}
}

function createStaticTreeNode(isRoot, node) {
	let staticNode;

	if (isVoid(node)) {
		return '';
	}
	if (node.tag) {
		if (isRoot) {
			if (!node.attrs) {
				node.attrs = {};
			}
			node.attrs['data-inferno'] = true;
		}
		staticNode = `<${ node.tag }`;
		if (node.attrs) {
			staticNode += ' ' + createStaticAttributes(node, null);
		}
		staticNode += `>`;
		if (!isVoid(node.children)) {
			staticNode += createStaticTreeChildren(node.children);
		} else if (!isVoid(node.text)) {
			staticNode += node.text;
		}
		// check if node is self closing?
		staticNode += `</${ node.tag }>`;
	}

	return staticNode;
}

export default function createHTMLTree(schema, isRoot, dynamicNodeMap) {
	const dynamicFlags = dynamicNodeMap.get(schema);
	let node;

	// static html
	if (!dynamicFlags) {
		return createStaticNode(createStaticTreeNode(isRoot, schema));
	}

	return node;
}<commit_msg>quick fix<commit_after>import createStaticNode from './shapes/staticNode';
import isArray from '../util/isArray';
import isStringOrNumber from '../util/isStringOrNumber';
import isVoid from '../util/isVoid';
import DOMRegistry from '../DOM/DOMRegistry';
import quoteAttributeValueForBrowser from './quoteAttributeValueForBrowser';

function countChildren(children) {
	if (!isVoid(children)) {
		if (isArray(children)) {
			return children.length;
		} else {
			return 1;
		}
	} else {
		return 0;
	}
}

/**
 *  WORK IN PROGRESS
 *
 *  Need to run tests for this one!!
 *
 * */
function renderMarkupForStyles(styles, component) {

	let serialized = '';

	for (let styleName in styles) {

		let styleValue = styles[styleName];

		if (styleValue !== null) {
			serialized += styleName + ':';
			serialized += styleValue + ';';
		}
	}
	return serialized || null;
}

function renderMarkupForAttributes(name, value) {

	const propertyInfo = DOMRegistry[name] || null;

	if (propertyInfo) {

		if (isVoid(value) ||
			propertyInfo.hasBooleanValue && !value ||
			propertyInfo.hasNumericValue && (value !== value) ||
			propertyInfo.hasPositiveNumericValue && value < 1 ||
			value.length === 0) {
			return '';
		}
		let attributeName = propertyInfo.attributeName;
		if (propertyInfo.hasBooleanValue) {
			return attributeName + '=""';
		}
		return `${ attributeName }=${ quoteAttributeValueForBrowser(value) }`;
	} else {

		if (isVoid(value)) {
			return '';
		}

		// custom attributes
		return `${ name }=${ quoteAttributeValueForBrowser(value) }`;
	}
}
function createStaticAttributes(node, excludeAttrs) {

	let HTML = '';

	const props = node.attrs;

	for (let propKey in props) {

		var propValue = props[propKey];
		if (propValue == null) {
			continue;
		}
		renderMarkupForStyles
		if (propKey === 'style') {
			propValue = renderMarkupForStyles(propKey, propValue );
		}

		let markup = null;

		markup =  renderMarkupForAttributes(propKey, propValue );

		if (markup) {
			HTML += ' ' + markup;
		}

	}
	return HTML;

 // TODO
	return Object.keys(node.attrs).map(attr => {
		if (attr === 'data-inferno') {
			return `${ attr }`;
		}
		return `${ attr }="${ node.attrs[attr] }"`;
	}).join(' ');
}

function createStaticTreeChildren(children) {
	let isLastChildNode = false;

	if (isArray(children)) {
		return children.map((child, i) => {
			if (isStringOrNumber(child)) {
				if (isLastChildNode) {
					isLastChildNode = true;
					return '<!---->' + child;
				} else {
					isLastChildNode = true;
					return child;
				}
			}
			isLastChildNode = false;
			return createStaticTreeNode(false, child);
		}).join('');
	} else {
		if (isStringOrNumber(children)) {
			return children;
		} else {
			return createStaticTreeNode(false, children);
		}
	}
}

function createStaticTreeNode(isRoot, node) {
	let staticNode;

	if (isVoid(node)) {
		return '';
	}
	if (node.tag) {
		if (isRoot) {
			if (!node.attrs) {
				node.attrs = {};
			}
			node.attrs['data-inferno'] = true;
		}
		staticNode = `<${ node.tag }`;
		if (node.attrs) {
			staticNode += ' ' + createStaticAttributes(node, null);
		}
		staticNode += `>`;
		if (!isVoid(node.children)) {
			staticNode += createStaticTreeChildren(node.children);
		} else if (!isVoid(node.text)) {
			staticNode += node.text;
		}
		// check if node is self closing?
		staticNode += `</${ node.tag }>`;
	}

	return staticNode;
}

export default function createHTMLTree(schema, isRoot, dynamicNodeMap) {
	const dynamicFlags = dynamicNodeMap.get(schema);
	let node;

	// static html
	if (!dynamicFlags) {
		return createStaticNode(createStaticTreeNode(isRoot, schema));
	}

	return node;
}<|endoftext|>


<commit_before>"use strict";

module.exports = {
    files: configuration.jsLintedFiles,
    tasks: ["jshint"]
};
<commit_msg>Simplifies watcher<commit_after>"use strict";

module.exports = {
    files: configuration.srcFiles.concat(configuration.testFiles),
    // Take care of linting and maintainability
    tasks: [
        "eslint",
        "exec:plato"
    ]
};
<|endoftext|>


<commit_before>import Three from 'three';

class ModelScene {
  // Renderer of the Scene
  renderer = undefined
  // The Scene
  scene = undefined
  // The Main Camera in the scene
  camera = undefined
  // The Model
  model = undefined
  // Data of the model
  modelData = {
    geometry: undefined,
    materials: undefined
  }
  // Current rendering state
  renderingState = {
    wireframe: false,
    shading: Three.SmoothShading
  }

  constructor(sceneCanvas, dimensions) {
    this.init(sceneCanvas, dimensions);
  }

  init(sceneCanvas, dimensions) {
    this.initRenderer(sceneCanvas, dimensions);
    this.initScene();
    this.initCamera(dimensions);
    this.initLight();

    this.animate();
  }

  initRenderer(sceneCanvas, dimensions) {
    this.renderer = new Three.WebGLRenderer({canvas: sceneCanvas, antialias: true});
    this.renderer.setSize(dimensions.width, dimensions.height);
    this.renderer.setClearColor(0x212121);
  }

  initScene() {
    this.scene = new Three.Scene();
  }

  initCamera(dimensions) {
    this.camera = new Three.PerspectiveCamera(45, dimensions.aspectRatio, 0.1, 10000);
    this.scene.add(this.camera);
    this.camera.position.set(0, 450, 500);
    this.camera.lookAt(this.scene.position);
  }

  initLight() {
    const light = new Three.PointLight(0xffffff);
    light.position.set(-100, 250, 200);
    this.scene.add(light);
  }

  animate = () => {
    requestAnimationFrame(this.animate);
    this.render();
  }

  render() {
    this.renderer.render(this.scene, this.camera);
  }

  updateModelData(modelData) {
    this.modelData = modelData;
    this.updateModel();
  }

  updateRenderingState(state) {
    Object.assign(this.renderingState, state);
    this.updateModel();
  }

  updateModel() {
    const {geometry} = this.modelData;
    if (this.model) {
      this.scene.remove(this.model);
    }
    const materials = this.getMaterials();
    if (materials.length === 1) {
      this.model = new Three.Mesh(geometry, materials[0]);
    } else {
      this.model = Three.SceneUtils.createMultiMaterialObject(geometry, materials);
    }
    this.model.scale.set(20, 20, 20);
    this.scene.add(this.model);
  }

  getMaterials() {
    const materials = [];
    const {wireframe, shading} = this.renderingState;
    if (this.modelData.materials) {
      materials.push(new Three.MeshFaceMaterial(this.modelData.materials.map(m => {
        m.shading = shading;
        return m;
      })));
    }

    if (wireframe) {
      materials.push(new Three.MeshBasicMaterial({
        color: 0x000000,
        shading: Three.FlatShading,
        wireframe: true,
        transparent: true
      }));
    }

    return materials;
  }

  onResize(dimensions) {
    this.camera.aspect = dimensions.aspectRatio;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(dimensions.width, dimensions.height);
  }

  /**
   * Dispose all entities in scene
   */
  dispose() {

  }
}

export default ModelScene;
<commit_msg>[refactor] Add comments for `ModelScene`<commit_after>import Three from 'three';

class ModelScene {
  // Renderer of the Scene
  renderer = undefined
  // The Scene
  scene = undefined
  // The Main Camera in the scene
  camera = undefined
  // The Model
  model = undefined
  // Data of the model
  modelData = {
    geometry: undefined,
    materials: undefined
  }
  // Current rendering state
  renderingState = {
    wireframe: false,
    shading: Three.SmoothShading
  }

  /**
   * Constructor function of the scene
   * @param  {DOMElement} sceneCanvas [the dom element for the canvas containing the scene]
   * @param  {Object} dimensions  [dimensions data of the canvas]
   */
  constructor(sceneCanvas, dimensions) {
    this._init(sceneCanvas, dimensions);
  }

  _init(sceneCanvas, dimensions) {
    this._initRenderer(sceneCanvas, dimensions);
    this._initScene();
    this._initCamera(dimensions);
    this._initLight();

    this._animate();
  }

  _initRenderer(sceneCanvas, dimensions) {
    this.renderer = new Three.WebGLRenderer({canvas: sceneCanvas, antialias: true});
    this.renderer.setSize(dimensions.width, dimensions.height);
    this.renderer.setClearColor(0x212121);
  }

  _initScene() {
    this.scene = new Three.Scene();
  }

  _initCamera(dimensions) {
    this.camera = new Three.PerspectiveCamera(45, dimensions.aspectRatio, 0.1, 10000);
    this.scene.add(this.camera);
    this.camera.position.set(0, 450, 500);
    this.camera.lookAt(this.scene.position);
  }

  _initLight() {
    const light = new Three.PointLight(0xffffff);
    light.position.set(-100, 250, 200);
    this.scene.add(light);
  }

  /**
   * Frame updater function
   */
  _animate = () => {
    requestAnimationFrame(this._animate);
    this._render();
  }

  /**
   * Render function which will be called for every fame
   */
  _render() {
    this.renderer.render(this.scene, this.camera);
  }

  /**
   * Update the model data of the model
   * @param  {Object} modelData [the new model data mainly comprising `geometry` and `materials`]
   */
  updateModelData(modelData) {
    this.modelData = modelData;
    this._updateModel();
  }

  /**
   * Update the rendering state of the model
   * @param  {Object} state [the new state which will be merged with the existing state]
   */
  updateRenderingState(state) {
    Object.assign(this.renderingState, state);
    this._updateModel();
  }

  /**
   * Reinitialize the current model (WARN: This function should try to be called as few as possible)
   * @return {[type]} [description]
   */
  _updateModel() {
    const {geometry} = this.modelData;
    if (this.model) {
      this.scene.remove(this.model);
    }
    const materials = this._getMaterials();
    if (materials.length === 1) {
      this.model = new Three.Mesh(geometry, materials[0]);
    } else {
      this.model = Three.SceneUtils.createMultiMaterialObject(geometry, materials);
    }
    this.model.scale.set(20, 20, 20);
    this.scene.add(this.model);
  }

  /**
   * Get the materials based on the current model data and rendering state
   * @return {[Object]} [an array of material object]
   */
  _getMaterials() {
    const materials = [];
    const {wireframe, shading} = this.renderingState;
    if (this.modelData.materials) {
      materials.push(new Three.MeshFaceMaterial(this.modelData.materials.map(m => {
        m.shading = shading;
        return m;
      })));
    }

    if (wireframe) {
      materials.push(new Three.MeshBasicMaterial({
        color: 0x000000,
        shading: Three.FlatShading,
        wireframe: true,
        transparent: true
      }));
    }

    return materials;
  }

  /**
   * Handler function for the scene when the scene dimensions are modified
   * @param  {Object} dimensions [data of the scene dimensions]
   */
  onResize(dimensions) {
    this.camera.aspect = dimensions.aspectRatio;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(dimensions.width, dimensions.height);
  }

  /**
   * Dispose all entities in scene
   */
  dispose() {

  }
}

export default ModelScene;
<|endoftext|>


<commit_before>/**
 * Created by lunik on 10/07/2017.
 */

import fs from 'fs'
import EventEmitter from 'events'
import Delogger from 'delogger'
import Folder from './folder'

export default class File extends EventEmitter {
  constructor (path, base) {
    super()

    let parsedPath = parsePath(path)
    this.base = base
    this._path = parsedPath.path
    this.type = 'file'
    this.name = parsedPath.fileName
    this.locked = false
    this.downloadCount = 0

    this.initMetadata()
    this.initWatch()

    this.log = new Delogger('File')
  }
  initWatch () {
    if (this.exist) {
      fs.watch(this.fullPath(), (eventType, filename) => this.watchChange(eventType, filename))
    }
  }
  initMetadata () {
    try {
      let stats = fs.statSync(this.fullPath())
      this._size = stats.size // Bytes
      this.exist = true
    } catch (err) {
      this._size = 0
      this.exist = false
    }
  }

  watchChange (eventType, filename) {
    this.initMetadata()
  }

  fullPath () {
    return `${this._path}/${this.name}`
  }
  size () {
    return this._size
  }

  lock () {
    this.locked = true
    this.emit('locked', this)
  }

  unlock () {
    this.locked = false
    this.emit('unlocked', this)
  }

  remove () {
    if (this.locked) {
      return false
    }
    this.log.info(`Removing ${this.fullPath()}`)

    fs.unlinkSync(this.fullPath())
    this.emit('remove', this)
  }

  addDownloader () {
    this.downloadCount++
    this.lock()
  }

  removeDownloader () {
    this.downloadCount--
    if (this.downloadCount <= 0) {
      this.downloadCount = 0
      this.unlock()
    }
  }

  rename (name) {
    if (this.locked) {
      return false
    }
    this.log.info(`Renaming ${this.fullPath()} into ${this._path}/${name}`)

    fs.renameSync(this.fullPath(), `${this._path}/${name}`)
    this.emit('rename', this)
  }

  create () {
    this.log.info(`Creating ${this.fullPath()}`)

    fs.writeFileSync(this.fullPath(), '')
    this.initWatch()
  }

  toJSON () {
    let cleanBase = this.base.split('/').slice(2).join('/')
    let url = '/' + removeBlank(`/folder/${cleanBase}/${this.name}`.split('/')).join('/')
    let download = '/' + removeBlank(`/file/${cleanBase}/${this.name}`.split('/')).join('/')
    let path = removeBlank(`${cleanBase}/${this.name}`.split('/')).join('/')
    return {
      name: this.name,
      type: this.type,
      locked: this.locked,
      downloadCount: this.downloadCount,
      size: this.size(),
      childs: this.childs,
      url,
      download: this instanceof Folder ? null : download,
      path: this instanceof Folder ? path : null
    }
  }

  download (res) {
    return new Promise((resolve, reject) => {
      this.addDownloader()
      res.download(this.fullPath(), (err) => {
        if (err) this.log.error(err)

        this.removeDownloader()

        resolve(err)
      })
    })
  }
}

function parsePath (path) {
  let temp = path.split('/')

  removeBlank(temp, 1)

  let name = temp.splice(temp.length - 1, 1)[0]
  return { fileName: name, path: temp.join('/') }
}

function removeBlank (array, begin) {
  begin = begin || 0
  for (var i = begin; i < array.length; i++) { // Begining at 1 to prevent first backslash removing
    if (array[i] === '' || array[i] === null) {
      array.splice(i, 1)
      i--
    }
  }
  return array
}

export { removeBlank, parsePath }
<commit_msg>Watch file recursivly<commit_after>/**
 * Created by lunik on 10/07/2017.
 */

import fs from 'fs'
import EventEmitter from 'events'
import Delogger from 'delogger'
import Folder from './folder'

export default class File extends EventEmitter {
  constructor (path, base) {
    super()

    let parsedPath = parsePath(path)
    this.base = base
    this._path = parsedPath.path
    this.type = 'file'
    this.name = parsedPath.fileName
    this.locked = false
    this.downloadCount = 0

    this.initMetadata()
    this.initWatch()

    this.log = new Delogger('File')
  }
  initWatch () {
    if (this.exist) {
      fs.watch(
        this.fullPath(), {
          recursive: true
        },
        (eventType, filename) => this.watchChange(eventType, filename)
      )
    }
  }
  initMetadata () {
    try {
      let stats = fs.statSync(this.fullPath())
      this._size = stats.size // Bytes
      this.exist = true
    } catch (err) {
      this._size = 0
      this.exist = false
    }
  }

  watchChange (eventType, filename) {
    this.initMetadata()
  }

  fullPath () {
    return `${this._path}/${this.name}`
  }
  size () {
    return this._size
  }

  lock () {
    this.locked = true
    this.emit('locked', this)
  }

  unlock () {
    this.locked = false
    this.emit('unlocked', this)
  }

  remove () {
    if (this.locked) {
      return false
    }
    this.log.info(`Removing ${this.fullPath()}`)

    fs.unlinkSync(this.fullPath())
    this.emit('remove', this)
  }

  addDownloader () {
    this.downloadCount++
    this.lock()
  }

  removeDownloader () {
    this.downloadCount--
    if (this.downloadCount <= 0) {
      this.downloadCount = 0
      this.unlock()
    }
  }

  rename (name) {
    if (this.locked) {
      return false
    }
    this.log.info(`Renaming ${this.fullPath()} into ${this._path}/${name}`)

    fs.renameSync(this.fullPath(), `${this._path}/${name}`)
    this.emit('rename', this)
  }

  create () {
    this.log.info(`Creating ${this.fullPath()}`)

    fs.writeFileSync(this.fullPath(), '')
    this.initWatch()
  }

  toJSON () {
    let cleanBase = this.base.split('/').slice(2).join('/')
    let url = '/' + removeBlank(`/folder/${cleanBase}/${this.name}`.split('/')).join('/')
    let download = '/' + removeBlank(`/file/${cleanBase}/${this.name}`.split('/')).join('/')
    let path = removeBlank(`${cleanBase}/${this.name}`.split('/')).join('/')
    return {
      name: this.name,
      type: this.type,
      locked: this.locked,
      downloadCount: this.downloadCount,
      size: this.size(),
      childs: this.childs,
      url,
      download: this instanceof Folder ? null : download,
      path: this instanceof Folder ? path : null
    }
  }

  download (res) {
    return new Promise((resolve, reject) => {
      this.addDownloader()
      res.download(this.fullPath(), (err) => {
        if (err) this.log.error(err)

        this.removeDownloader()

        resolve(err)
      })
    })
  }
}

function parsePath (path) {
  let temp = path.split('/')

  removeBlank(temp, 1)

  let name = temp.splice(temp.length - 1, 1)[0]
  return { fileName: name, path: temp.join('/') }
}

function removeBlank (array, begin) {
  begin = begin || 0
  for (var i = begin; i < array.length; i++) { // Begining at 1 to prevent first backslash removing
    if (array[i] === '' || array[i] === null) {
      array.splice(i, 1)
      i--
    }
  }
  return array
}

export { removeBlank, parsePath }
<|endoftext|>


<commit_before>function populateLeaderboard(problemID, schoolName, problemIndex) {
	console.log("start");
	$("#leaderboard").empty()
	console.log(problemID)
	console.log(schoolName)
	var entries = getProblemSubmissionsWithSchool(problemID, schoolName);
	console.log("entry")
	console.log(entries);
	for(var a = 0; a < entries.length; a++) {
		var entry = entries[a]
		var user = getUser(entry.userID);
		console.log(entry.score);
		$("#leaderboard").append($("
"+(a+1)+"	"+user.firstName+"	"+user.schoolName+"	"+entry.score+"
"))
	}
}

function populateSchoolTabs(school) {
	$("#schoolTabs").empty()
	var schools = getSchools()
	for(var a = 0; a < schools.length; a++) {
		if (schools[a] === school) {
			$("#schoolTabs").append("
"+schools[a]+"
");
		} else {
			$("#schoolTabs").append("
"+schools[a]+"
");
		}
	}
}

function displayProblem(index, schoolName) {
	var problem = getProblemWithIndex(index)
	populateLeaderboard(problem.problemID, schoolName, index)

	var result = $.ajax({
		url: "problems/descriptions/header"+problem.problemName+".html",
		async: true,
		method: "GET",
		success: function(result) {
			$("#jumbotron").empty()
			$("#jumbotron").append(result);
		}
    });

	var result = $.ajax({
		url: "problems/descriptions/body"+problem.problemName+".html",
		async: true,
		method: "GET",
		success: function(result) {
			$("#rulesPanelBody").empty()
			$("#rulesPanelBody").append(result);
		}
    });
}

$(document).ready(function() {

	var schoolName = getGET("schoolName");
	if(schoolName == null || schoolName === "" || schoolName === " ") {
		schoolName = getSchools()[0];
	}
	console.log("scho: "+schoolName)
	populateSchoolTabs(schoolName);

	var index = parseInt(getGET("problemIndex"));
	if(index == null || index === "" || index === " ") {
		index = 0;
	}
	var size = getProblemsSize();
	$("#backButton").click(function() {
		index++;
		if(index == size-1) {
			$("#backButton").css("display", "none");
		}
		if(index == 1) {
			$("#nextButton").css("display", "inline");
		}

		displayProblem(index, schoolName)
	})
	$("#nextButton").click(function() {
		index--;
		if(index == 0) {
			$("#nextButton").css("display", "none");
		}
		if(index == size-2) {
			$("#backButton").css("display", "inline");
		}

		displayProblem(index, schoolName)
	})

	if(index == 0) {
		$("#nextButton").css("display", "none");
	}
	if(index == size-1) {
		$("#backButton").css("display", "none");
	}

	displayProblem(index, schoolName)

	$('.schoolTab').click(function() {
		$(".schoolTab").each(function(schoolIndex) {
  			$(this).removeClass("active")
		});
		$(this).addClass("active")
		schoolName = $(this).attr("schoolName")
		populateLeaderboard(getProblemWithIndex(index).problemID, schoolName, index)
	});

	renderMathInElement(document.getElementById("rulesPanelBody"));
})<commit_msg>Oh yea<commit_after>function populateLeaderboard(problemID, schoolName, problemIndex) {
	console.log("start");
	$("#leaderboard").empty()
	console.log(problemID)
	console.log(schoolName)
	var entries = getProblemSubmissionsWithSchool(problemID, schoolName);
	console.log("entry")
	console.log(entries);
	for(var a = 0; a < entries.length; a++) {
		var entry = entries[a]
		var user = getUser(entry.userID);
		console.log(entry.score);
		$("#leaderboard").append($("
"+(a+1)+"	"+user.firstName+"	"+user.schoolName+"	"+entry.score+"
"))
	}
}

function populateSchoolTabs(school) {
	$("#schoolTabs").empty()
	var schools = getSchools()
	for(var a = 0; a < schools.length; a++) {
		if (schools[a] === school) {
			$("#schoolTabs").append("
"+schools[a]+"
");
		} else {
			$("#schoolTabs").append("
"+schools[a]+"
");
		}
	}
}

function displayProblem(index, schoolName) {
	var problem = getProblemWithIndex(index)
	populateLeaderboard(problem.problemID, schoolName, index)

	var result = $.ajax({
		url: "problems/descriptions/header"+problem.problemName+".html",
		async: true,
		method: "GET",
		success: function(result) {
			$("#jumbotron").empty()
			$("#jumbotron").append(result);
		}
    });

	var result = $.ajax({
		url: "problems/descriptions/body"+problem.problemName+".html",
		async: true,
		method: "GET",
		success: function(result) {
			$("#rulesPanelBody").empty()
			$("#rulesPanelBody").append(result);
		}
    });
}

$(document).ready(function() {

	var schoolName = getGET("schoolName");
	if(schoolName == null || schoolName === "" || schoolName === " ") {
		schoolName = getSchools()[0];
	}
	console.log("scho: "+schoolName)
	populateSchoolTabs(schoolName);

	var index = parseInt(getGET("problemIndex"));
	if(isNaN(index) == true || index == null || index === "" || index === " ") {
		index = 0;
	}
	console.log("ind"+index)
	var size = getProblemsSize();
	$("#backButton").click(function() {
		index++;
		if(index == size-1) {
			$("#backButton").css("display", "none");
		}
		if(index == 1) {
			$("#nextButton").css("display", "inline");
		}

		displayProblem(index, schoolName)
	})
	$("#nextButton").click(function() {
		index--;
		if(index == 0) {
			$("#nextButton").css("display", "none");
		}
		if(index == size-2) {
			$("#backButton").css("display", "inline");
		}

		displayProblem(index, schoolName)
	})

	if(index == 0) {
		$("#nextButton").css("display", "none");
	}
	if(index == size-1) {
		$("#backButton").css("display", "none");
	}

	displayProblem(index, schoolName)

	$('.schoolTab').click(function() {
		$(".schoolTab").each(function(schoolIndex) {
  			$(this).removeClass("active")
		});
		$(this).addClass("active")
		schoolName = $(this).attr("schoolName")
		populateLeaderboard(getProblemWithIndex(index).problemID, schoolName, index)
	});

	renderMathInElement(document.getElementById("rulesPanelBody"));
})<|endoftext|>


<commit_before>import React from 'react'

import { connect } from 'react-redux'

class Message extends React.Component {
  render() {
    return (

        {this.props.author}: {this.props.message}

    )
  }
}

class MessageList extends React.Component {
  render() {
    return (

        {this.props.messages.map(
          msg => <Message key={msg.id}
                          author={msg.author} message={msg.message} />
        )}

    )
  }
}

const VisibleMessageList = connect(
  (state) => {
    return {
      messages: state.messages
    }
  }
)(MessageList)

class Input extends React.Component {

  constructor(props) {
      super(props);
      this.state = {
        value: ""
      }
  }

  render() {
    return (
      <input type="text" value={this.state.value}
             onChange={this.handleChange} />
    )
  }
}

export class App extends React.Component {
  render() {
    return (

        <Input/>
        <VisibleMessageList />

    )
  }
}
<commit_msg>Make OneLineInput form capable of hanlding submit events<commit_after>import React from 'react'

import { connect } from 'react-redux'

class Message extends React.Component {
  render() {
    return (

        {this.props.author}: {this.props.message}

    )
  }
}

class MessageList extends React.Component {
  render() {
    return (

        {this.props.messages.map(
          msg => <Message key={msg.id}
                          author={msg.author} message={msg.message} />
        )}

    )
  }
}

const VisibleMessageList = connect(
  (state) => {
    return {
      messages: state.messages
    }
  }
)(MessageList)

class OneLineInputForm extends React.Component {

  constructor(props) {
      super(props);
      this.state = {
        value: ""
      }
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit.bind(this)} >
        <input type="text" value={this.state.value}
               onChange={this.handleChange.bind(this)} />
      </form>
    )
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    if (this.props.onSubmit != null) {
      this.props.onSubmit(this.state.value);
    }

    this.setState({value: ""});
    event.preventDefault();
  }
}

export class App extends React.Component {
  render() {
    return (

        <OneLineInputForm />
        <VisibleMessageList />

    )
  }
}
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt, options) {
    return {
        options : {
            livereload: 7777
        },
        source: {
            files: ['src/**/*.js', 'test/unit/**.js', 'test/e2e/**.js'],
            tasks: [
                'jshint',
                'concat:dist',
                'ngAnnotate',
                'uglify',
                'test-unit',
                'concat:license'
            ]
        }
    };
};
<commit_msg>Add svg-star.js to grunt watch<commit_after>'use strict';

module.exports = function (grunt, options) {
    return {
        options : {
            livereload: 7777
        },
        source: {
            files: ['svg-star.js', 'src/**/*.js', 'test/unit/**.js', 'test/e2e/**.js'],
            tasks: [
                'jshint',
                'concat:dist',
                'ngAnnotate',
                'uglify',
                'test-unit',
                'concat:license'
            ]
        }
    };
};
<|endoftext|>


<commit_before>describe('MAS Editor', function () {
  'use strict';

  beforeEach(function(done) {
    var self = this;
    requirejs([
      'mas-editor',
      'phantom-shims'
    ],
    function (
      masEditor
    ) {
      var sandbox = document.createElement('div');
      sandbox.innerHTML = window.__html__['spec/javascripts/fixtures/mas-editor.html'];
      document.body.appendChild(sandbox);
      self.sandbox = sandbox;
      self.cmsFormNode = sandbox.querySelector('.js-edit-form');
      self.toolbarNode = sandbox.querySelector('.js-html-editor-toolbar');
      self.markdownEditorNode = sandbox.querySelector('.js-markdown-editor');
      self.markdownEditorContentNode = sandbox.querySelector('.js-markdown-editor-content');
      self.htmlEditorNode = sandbox.querySelector('.js-html-editor');
      self.htmlEditorContentNode = document.querySelector('.js-html-editor-content');
      self.switchModeButtonNodes = document.querySelectorAll('.js-switch-mode');
      self.classActive = 'is-active';

      self.masEditor = masEditor;
      self.masEditor.init({
        cmsFormNode: self.cmsFormNode,
        toolbarNode: self.toolbarNode,
        htmlEditorNode: self.htmlEditorNode,
        htmlEditorContentNode: self.htmlEditorContentNode,
        markdownEditorNode: self.markdownEditorNode,
        markdownEditorContentNode: self.markdownEditorContentNode,
        switchModeButtonNodes: self.switchModeButtonNodes
      });

      done();
    }, done);
  });

  afterEach(function() {
    this.sandbox.parentNode.removeChild(this.sandbox);
  });

  describe('Switching Edit mode', function() {
    it('allows the mode to be changed', function() {
      this.masEditor.mode = 'markdown';
      this.masEditor.changeMode('html');
      expect(this.masEditor.mode).to.equal('html');
    });

    it('ensures the active classes are switched when mode is changed', function() {
      this.masEditor
        .show(this.htmlEditorNode)
        .hide(this.markdownEditorNode)
        .changeMode('markdown');

      expect(this.htmlEditorNode.classList.contains(this.classActive)).to.be.false;
      expect(this.markdownEditorNode.classList.contains(this.classActive)).to.be.true;
    });

    // TODO: Add a test for button mode switching
    // it('ensures the selected mode button has an active class', function() {
    //   expect(this.toolbarNode.classList.contains(this.classActive)).to.be.true;
    // });

    it('should throw an error if an unknown mode is selected', function() {
      expect(function(){this.masEditor.changeMode('foo');}.bind(this)).to.throw('That conversion isn\'t supported');
    });

    it('should add active class to the editor node', function() {
      this.masEditor.show(this.htmlEditorNode);
      expect(this.htmlEditorNode.classList.contains(this.classActive)).to.be.true;
    });

    it('should remove active class from the editor node', function() {
      this.masEditor.hide(this.htmlEditorNode);
      expect(this.htmlEditorNode.classList.contains(this.classActive)).to.be.false;
    });
  });

  describe('Events', function() {
    it('should catch form submit and route through handler function', function(){
      var spy = sinon.spy(this.masEditor, 'handleSubmit');
      // TODO Currently causing an infinite loop
      // this.masEditor.cmsFormNode.submit();
      expect(this.masEditor.handleSubmit).to.have.been.called;
    });

    it('should call changeMode when a mode button is clicked', function(){
      var spy = sinon.spy(this.masEditor, 'changeMode');
      this.switchModeButtonNodes[0].click();
      expect(spy.called).to.be.true;
    });
  });

});
<commit_msg>Refactor mas-editor_spec to remove unnecessary DOM references<commit_after>describe('MAS Editor', function () {
  'use strict';

  beforeEach(function(done) {
    var self = this;
    requirejs([
      'mas-editor',
      'phantom-shims'
    ],
    function (
      masEditor
    ) {
      self.sandbox = document.createElement('div');
      self.sandbox.innerHTML = window.__html__['spec/javascripts/fixtures/mas-editor.html'];
      document.body.appendChild(self.sandbox);
      self.classActive = 'is-active';

      self.masEditor = masEditor;
      self.masEditor.init({
        cmsFormNode: self.sandbox.querySelector('.js-edit-form'),
        toolbarNode: self.sandbox.querySelector('.js-html-editor-toolbar'),
        htmlEditorNode: self.sandbox.querySelector('.js-html-editor'),
        htmlEditorContentNode: document.querySelector('.js-html-editor-content'),
        markdownEditorNode: self.sandbox.querySelector('.js-markdown-editor'),
        markdownEditorContentNode: self.sandbox.querySelector('.js-markdown-editor-content'),
        switchModeButtonNodes: document.querySelectorAll('.js-switch-mode')
      });

      done();
    }, done);
  });

  afterEach(function() {
    this.sandbox.parentNode.removeChild(this.sandbox);
  });

  describe('Switching Edit mode', function() {
    it('allows the mode to be changed', function() {
      this.masEditor.mode = 'markdown';
      this.masEditor.changeMode('html');
      expect(this.masEditor.mode).to.equal('html');
    });

    it('ensures the active classes are switched when mode is changed', function() {
      this.masEditor
        .show(this.masEditor.htmlEditorNode)
        .hide(this.masEditor.markdownEditorNode)
        .changeMode('markdown');

      expect(this.masEditor.htmlEditorNode.classList.contains(this.classActive)).to.be.false;
      expect(this.masEditor.markdownEditorNode.classList.contains(this.classActive)).to.be.true;
    });

    // TODO: Add a test for button mode switching
    // it('ensures the selected mode button has an active class', function() {
    //   expect(this.masEditor.toolbarNode.classList.contains(this.classActive)).to.be.true;
    // });

    it('should throw an error if an unknown mode is selected', function() {
      expect(function(){this.masEditor.changeMode('foo');}.bind(this)).to.throw('That conversion isn\'t supported');
    });

    it('should add active class to the editor node', function() {
      this.masEditor.show(this.masEditor.htmlEditorNode);
      expect(this.masEditor.htmlEditorNode.classList.contains(this.classActive)).to.be.true;
    });

    it('should remove active class from the editor node', function() {
      this.masEditor.hide(this.masEditor.htmlEditorNode);
      expect(this.masEditor.htmlEditorNode.classList.contains(this.classActive)).to.be.false;
    });
  });

  describe('Events', function() {
    it('should catch form submit and route through handler function', function(){
      var spy = sinon.spy(this.masEditor, 'handleSubmit');
      // TODO Currently causing an infinite loop
      // this.masEditor.cmsFormNode.submit();
      expect(this.masEditor.handleSubmit).to.have.been.called;
    });

    it('should call changeMode when a mode button is clicked', function(){
      var spy = sinon.spy(this.masEditor, 'changeMode');
      this.masEditor.switchModeButtonNodes[0].click();
      expect(spy.called).to.be.true;
    });
  });

});
<|endoftext|>


<commit_before>import {
	BufferGeometry,
	BufferAttribute,
} from 'three';
import { MeshBVH } from '../core/MeshBVH.js';

onmessage = function ( { data } ) {

	let prevTime = performance.now();
	function onProgressCallback( progress ) {

		const currTime = performance.now();
		if ( currTime - prevTime >= 10 || progress === 1.0 ) {

			postMessage( {

				error: null,
				serialized: null,
				position: null,
				progress,

			} );
			prevTime = currTime;

		}

	}

	const { index, position, options } = data;
	try {

		const geometry = new BufferGeometry();
		geometry.setAttribute( 'position', new BufferAttribute( position, 3, false ) );
		if ( index ) {

			geometry.setIndex( new BufferAttribute( index, 1, false ) );

		}

		if ( options.includedProgressCallback ) {

			options.onProgress = onProgressCallback;

		}

		if ( options.groups ) {

			const groups = options.groups;
			for ( const i in groups ) {

				const group = groups[ i ];
				geometry.addGroup( group.start, group.count, group.materialIndex );

			}

		}

		const bvh = new MeshBVH( geometry, options );
		const serialized = MeshBVH.serialize( bvh, { copyIndexBuffer: false } );

		postMessage( {

			error: null,
			serialized,
			position,
			progress: 1,

		}, [ serialized.index.buffer, position.buffer ] );

	} catch ( error ) {

		postMessage( {

			error,
			serialized: null,
			position: null,
			progress: 1,

		} );

	}

};
<commit_msg>Transfer roots<commit_after>import {
	BufferGeometry,
	BufferAttribute,
} from 'three';
import { MeshBVH } from '../core/MeshBVH.js';

onmessage = function ( { data } ) {

	let prevTime = performance.now();
	function onProgressCallback( progress ) {

		const currTime = performance.now();
		if ( currTime - prevTime >= 10 || progress === 1.0 ) {

			postMessage( {

				error: null,
				serialized: null,
				position: null,
				progress,

			} );
			prevTime = currTime;

		}

	}

	const { index, position, options } = data;
	try {

		const geometry = new BufferGeometry();
		geometry.setAttribute( 'position', new BufferAttribute( position, 3, false ) );
		if ( index ) {

			geometry.setIndex( new BufferAttribute( index, 1, false ) );

		}

		if ( options.includedProgressCallback ) {

			options.onProgress = onProgressCallback;

		}

		if ( options.groups ) {

			const groups = options.groups;
			for ( const i in groups ) {

				const group = groups[ i ];
				geometry.addGroup( group.start, group.count, group.materialIndex );

			}

		}

		const bvh = new MeshBVH( geometry, options );
		const serialized = MeshBVH.serialize( bvh, { copyIndexBuffer: false } );

		postMessage( {

			error: null,
			serialized,
			position,
			progress: 1,

		}, [ serialized.index.buffer, position.buffer, ...serialized.roots ] );

	} catch ( error ) {

		postMessage( {

			error,
			serialized: null,
			position: null,
			progress: 1,

		} );

	}

};
<|endoftext|>