<commit_before>var deps = {
	Core: {
		src: ['Leaflet.js',
		      'core/Util.js',
		      'core/Class.js',
		      'core/Events.js',
		      'core/Browser.js',
		      'geometry/Point.js',
		      'geometry/Bounds.js',
		      'geometry/Transformation.js',
		      'dom/DomUtil.js',
		      'geo/LatLng.js',
		      'geo/LatLngBounds.js',
		      'geo/projection/Projection.LonLat.js',
		      'geo/projection/Projection.SphericalMercator.js',
		      'geo/crs/CRS.js',
		      'geo/crs/CRS.Simple.js',
		      'geo/crs/CRS.Earth.js',
		      'geo/crs/CRS.EPSG3857.js',
		      'geo/crs/CRS.EPSG4326.js',
		      'map/Map.js',
		      'layer/Layer.js'
		      ],
		desc: 'The core of the library, including OOP, events, DOM facilities, basic units, projections (EPSG:3857 and EPSG:4326) and the base Map class.'
	},

	EPSG3395: {
		src: ['geo/projection/Projection.Mercator.js',
		      'geo/crs/CRS.EPSG3395.js'],
		desc: 'EPSG:3395 projection (used by some map providers).',
		heading: 'Additional projections'
	},

	GridLayer: {
		src: ['layer/tile/GridLayer.js'],
		desc: 'Used as base class for grid-like layers like TileLayer.',
		heading: 'Layers'
	},

	TileLayer: {
		src: ['layer/tile/TileLayer.js'],
		desc: 'The base class for displaying tile layers on the map.',
		deps: ['GridLayer']
	},

	TileLayerWMS: {
		src: ['layer/tile/TileLayer.WMS.js'],
		desc: 'WMS tile layer.',
		deps: ['TileLayer']
	},

	ImageOverlay: {
		src: ['layer/ImageOverlay.js'],
		desc: 'Used to display an image over a particular rectangular area of the map.'
	},

	Marker: {
		src: ['layer/marker/Icon.js',
		      'layer/marker/Icon.Default.js',
		      'layer/marker/Marker.js'],
		desc: 'Markers to put on the map.'
	},

	DivIcon: {
		src: ['layer/marker/DivIcon.js'],
		deps: ['Marker'],
		desc: 'Lightweight div-based icon for markers.'
	},

	Popup: {
		src: [
			'layer/PopupBase.js',
			'layer/Popup.js',
			'layer/Layer.Popup.js',
			'layer/marker/Marker.Popup.js'
		],
		deps: ['Marker'],
		desc: 'Used to display the map popup (used mostly for binding HTML data to markers and paths on click).'
	},

	Label: {
		src: [
			'layer/Label.js',
			'layer/Layer.Label.js',
			'layer/marker/Marker.Label.js'
		],
		deps: ['Popup', 'Marker'],
		desc: 'Used to display the map popup (used mostly for binding HTML data to markers and paths on click).'
	},

	LayerGroup: {
		src: ['layer/LayerGroup.js'],
		desc: 'Allows grouping several layers to handle them as one.'
	},

	FeatureGroup: {
		src: ['layer/FeatureGroup.js'],
		deps: ['LayerGroup', 'Popup'],
		desc: 'Extends LayerGroup with mouse events and bindPopup method shared between layers.'
	},

	Path: {
		src: [
			'layer/vector/Renderer.js',
			'layer/vector/Path.js'
		],
		desc: 'Vector rendering core.',
		heading: 'Vector layers'
	},

	Polyline: {
		src: ['geometry/LineUtil.js',
		      'layer/vector/Polyline.js'],
		deps: ['Path'],
		desc: 'Polyline overlays.'
	},

	Polygon: {
		src: ['geometry/PolyUtil.js',
		      'layer/vector/Polygon.js'],
		deps: ['Polyline'],
		desc: 'Polygon overlays.'
	},

	Rectangle: {
		src: ['layer/vector/Rectangle.js'],
		deps: ['Polygon'],
		desc: ['Rectangle overlays.']
	},

	CircleMarker: {
		src: ['layer/vector/CircleMarker.js'],
		deps: ['Path'],
		desc: 'Circle overlays with a constant pixel radius.'
	},

	Circle: {
		src: ['layer/vector/Circle.js'],
		deps: ['CircleMarker'],
		desc: 'Circle overlays (with radius in meters).'
	},

	SVG: {
		src: ['layer/vector/SVG.js'],
		deps: ['Path'],
		desc: 'SVG backend for vector layers.'
	},

	VML: {
		src: ['layer/vector/SVG.VML.js'],
		deps: ['SVG'],
		desc: 'VML fallback for vector layers in IE7-8.'
	},

	Canvas: {
		src: ['layer/vector/Canvas.js'],
		deps: ['CircleMarker', 'Path', 'Polygon', 'Polyline'],
		desc: 'Canvas backend for vector layers.'
	},

	GeoJSON: {
		src: ['layer/GeoJSON.js'],
		deps: ['Polygon', 'Circle', 'CircleMarker', 'Marker', 'FeatureGroup'],
		desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
	},

	MapDrag: {
		src: ['dom/DomEvent.js',
		      'dom/Draggable.js',
		      'core/Handler.js',
		      'map/handler/Map.Drag.js'],
		desc: 'Makes the map draggable (by mouse or touch).',
		heading: 'Interaction'
	},

	MouseZoom: {
		src: ['dom/DomEvent.js',
		      'core/Handler.js',
		      'map/handler/Map.DoubleClickZoom.js',
		      'map/handler/Map.ScrollWheelZoom.js'],
		desc: 'Scroll wheel zoom and double click zoom on the map.'
	},

	TouchZoom: {
		src: ['dom/DomEvent.js',
		      'dom/DomEvent.DoubleTap.js',
		      'dom/DomEvent.Pointer.js',
		      'core/Handler.js',
		      'map/handler/Map.TouchZoom.js',
		      'map/handler/Map.Tap.js'],
		deps: ['AnimationZoom'],
		desc: 'Enables smooth touch zoom / tap / longhold / doubletap on iOS, IE10, Android.'
	},

	BoxZoom: {
		src: ['map/handler/Map.BoxZoom.js'],
		deps: ['MouseZoom'],
		desc: 'Enables zooming to bounding box by shift-dragging the map.'
	},

	Keyboard: {
		src: ['map/handler/Map.Keyboard.js'],
		desc: 'Enables keyboard pan/zoom when the map is focused.'
	},

	MarkerDrag: {
		src: ['layer/marker/Marker.Drag.js'],
		deps: ['Marker'],
		desc: 'Makes markers draggable (by mouse or touch).'
	},

	ControlZoom: {
		src: ['control/Control.js',
		      'control/Control.Zoom.js'],
		heading: 'Controls',
		desc: 'Basic zoom control with two buttons (zoom in / zoom out).'
	},

	ControlAttrib: {
		src: ['control/Control.js',
		      'control/Control.Attribution.js'],
		desc: 'Attribution control.'
	},

	ControlScale: {
		src: ['control/Control.js',
		      'control/Control.Scale.js'],
		desc: 'Scale control.'
	},

	ControlLayers: {
		src: ['control/Control.js',
		      'control/Control.Layers.js'],
		desc: 'Layer Switcher control.'
	},

	AnimationPan: {
		src: [
			'dom/DomEvent.js',
			'dom/PosAnimation.js',
			'map/anim/Map.PanAnimation.js'
			],
		heading: 'Animation',
		desc: 'Core panning animation support.'
	},

	AnimationZoom: {
		src: [
			'map/anim/Map.ZoomAnimation.js',
			'map/anim/Map.FlyTo.js'
			],
		deps: ['AnimationPan'],
		desc: 'Smooth zooming animation. Works only on browsers that support CSS3 Transitions.'
	},

	Geolocation: {
		src: ['map/ext/Map.Geolocation.js'],
		desc: 'Adds Map#locate method and related events to make geolocation easier.',
		heading: 'Misc'
	}
};

if (typeof exports !== 'undefined') {
	exports.deps = deps;
}
<commit_msg>Fix bad Label description in deps.js<commit_after>var deps = {
	Core: {
		src: ['Leaflet.js',
		      'core/Util.js',
		      'core/Class.js',
		      'core/Events.js',
		      'core/Browser.js',
		      'geometry/Point.js',
		      'geometry/Bounds.js',
		      'geometry/Transformation.js',
		      'dom/DomUtil.js',
		      'geo/LatLng.js',
		      'geo/LatLngBounds.js',
		      'geo/projection/Projection.LonLat.js',
		      'geo/projection/Projection.SphericalMercator.js',
		      'geo/crs/CRS.js',
		      'geo/crs/CRS.Simple.js',
		      'geo/crs/CRS.Earth.js',
		      'geo/crs/CRS.EPSG3857.js',
		      'geo/crs/CRS.EPSG4326.js',
		      'map/Map.js',
		      'layer/Layer.js'
		      ],
		desc: 'The core of the library, including OOP, events, DOM facilities, basic units, projections (EPSG:3857 and EPSG:4326) and the base Map class.'
	},

	EPSG3395: {
		src: ['geo/projection/Projection.Mercator.js',
		      'geo/crs/CRS.EPSG3395.js'],
		desc: 'EPSG:3395 projection (used by some map providers).',
		heading: 'Additional projections'
	},

	GridLayer: {
		src: ['layer/tile/GridLayer.js'],
		desc: 'Used as base class for grid-like layers like TileLayer.',
		heading: 'Layers'
	},

	TileLayer: {
		src: ['layer/tile/TileLayer.js'],
		desc: 'The base class for displaying tile layers on the map.',
		deps: ['GridLayer']
	},

	TileLayerWMS: {
		src: ['layer/tile/TileLayer.WMS.js'],
		desc: 'WMS tile layer.',
		deps: ['TileLayer']
	},

	ImageOverlay: {
		src: ['layer/ImageOverlay.js'],
		desc: 'Used to display an image over a particular rectangular area of the map.'
	},

	Marker: {
		src: ['layer/marker/Icon.js',
		      'layer/marker/Icon.Default.js',
		      'layer/marker/Marker.js'],
		desc: 'Markers to put on the map.'
	},

	DivIcon: {
		src: ['layer/marker/DivIcon.js'],
		deps: ['Marker'],
		desc: 'Lightweight div-based icon for markers.'
	},

	Popup: {
		src: [
			'layer/PopupBase.js',
			'layer/Popup.js',
			'layer/Layer.Popup.js',
			'layer/marker/Marker.Popup.js'
		],
		deps: ['Marker'],
		desc: 'Used to display the map popup (used mostly for binding HTML data to markers and paths on click).'
	},

	Label: {
		src: [
			'layer/Label.js',
			'layer/Layer.Label.js',
			'layer/marker/Marker.Label.js'
		],
		deps: ['Popup', 'Marker'],
		desc: 'Used to display the map label (used mostly for binding short descriptions to markers and paths on mouseover).'
	},

	LayerGroup: {
		src: ['layer/LayerGroup.js'],
		desc: 'Allows grouping several layers to handle them as one.'
	},

	FeatureGroup: {
		src: ['layer/FeatureGroup.js'],
		deps: ['LayerGroup', 'Popup'],
		desc: 'Extends LayerGroup with mouse events and bindPopup method shared between layers.'
	},

	Path: {
		src: [
			'layer/vector/Renderer.js',
			'layer/vector/Path.js'
		],
		desc: 'Vector rendering core.',
		heading: 'Vector layers'
	},

	Polyline: {
		src: ['geometry/LineUtil.js',
		      'layer/vector/Polyline.js'],
		deps: ['Path'],
		desc: 'Polyline overlays.'
	},

	Polygon: {
		src: ['geometry/PolyUtil.js',
		      'layer/vector/Polygon.js'],
		deps: ['Polyline'],
		desc: 'Polygon overlays.'
	},

	Rectangle: {
		src: ['layer/vector/Rectangle.js'],
		deps: ['Polygon'],
		desc: ['Rectangle overlays.']
	},

	CircleMarker: {
		src: ['layer/vector/CircleMarker.js'],
		deps: ['Path'],
		desc: 'Circle overlays with a constant pixel radius.'
	},

	Circle: {
		src: ['layer/vector/Circle.js'],
		deps: ['CircleMarker'],
		desc: 'Circle overlays (with radius in meters).'
	},

	SVG: {
		src: ['layer/vector/SVG.js'],
		deps: ['Path'],
		desc: 'SVG backend for vector layers.'
	},

	VML: {
		src: ['layer/vector/SVG.VML.js'],
		deps: ['SVG'],
		desc: 'VML fallback for vector layers in IE7-8.'
	},

	Canvas: {
		src: ['layer/vector/Canvas.js'],
		deps: ['CircleMarker', 'Path', 'Polygon', 'Polyline'],
		desc: 'Canvas backend for vector layers.'
	},

	GeoJSON: {
		src: ['layer/GeoJSON.js'],
		deps: ['Polygon', 'Circle', 'CircleMarker', 'Marker', 'FeatureGroup'],
		desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
	},

	MapDrag: {
		src: ['dom/DomEvent.js',
		      'dom/Draggable.js',
		      'core/Handler.js',
		      'map/handler/Map.Drag.js'],
		desc: 'Makes the map draggable (by mouse or touch).',
		heading: 'Interaction'
	},

	MouseZoom: {
		src: ['dom/DomEvent.js',
		      'core/Handler.js',
		      'map/handler/Map.DoubleClickZoom.js',
		      'map/handler/Map.ScrollWheelZoom.js'],
		desc: 'Scroll wheel zoom and double click zoom on the map.'
	},

	TouchZoom: {
		src: ['dom/DomEvent.js',
		      'dom/DomEvent.DoubleTap.js',
		      'dom/DomEvent.Pointer.js',
		      'core/Handler.js',
		      'map/handler/Map.TouchZoom.js',
		      'map/handler/Map.Tap.js'],
		deps: ['AnimationZoom'],
		desc: 'Enables smooth touch zoom / tap / longhold / doubletap on iOS, IE10, Android.'
	},

	BoxZoom: {
		src: ['map/handler/Map.BoxZoom.js'],
		deps: ['MouseZoom'],
		desc: 'Enables zooming to bounding box by shift-dragging the map.'
	},

	Keyboard: {
		src: ['map/handler/Map.Keyboard.js'],
		desc: 'Enables keyboard pan/zoom when the map is focused.'
	},

	MarkerDrag: {
		src: ['layer/marker/Marker.Drag.js'],
		deps: ['Marker'],
		desc: 'Makes markers draggable (by mouse or touch).'
	},

	ControlZoom: {
		src: ['control/Control.js',
		      'control/Control.Zoom.js'],
		heading: 'Controls',
		desc: 'Basic zoom control with two buttons (zoom in / zoom out).'
	},

	ControlAttrib: {
		src: ['control/Control.js',
		      'control/Control.Attribution.js'],
		desc: 'Attribution control.'
	},

	ControlScale: {
		src: ['control/Control.js',
		      'control/Control.Scale.js'],
		desc: 'Scale control.'
	},

	ControlLayers: {
		src: ['control/Control.js',
		      'control/Control.Layers.js'],
		desc: 'Layer Switcher control.'
	},

	AnimationPan: {
		src: [
			'dom/DomEvent.js',
			'dom/PosAnimation.js',
			'map/anim/Map.PanAnimation.js'
			],
		heading: 'Animation',
		desc: 'Core panning animation support.'
	},

	AnimationZoom: {
		src: [
			'map/anim/Map.ZoomAnimation.js',
			'map/anim/Map.FlyTo.js'
			],
		deps: ['AnimationPan'],
		desc: 'Smooth zooming animation. Works only on browsers that support CSS3 Transitions.'
	},

	Geolocation: {
		src: ['map/ext/Map.Geolocation.js'],
		desc: 'Adds Map#locate method and related events to make geolocation easier.',
		heading: 'Misc'
	}
};

if (typeof exports !== 'undefined') {
	exports.deps = deps;
}
<|endoftext|>


<commit_before>'use strict';
import * as _ from 'lodash';
let SortedSet = require('collections/sorted-set');

// checks for one row of the iterchunk board for a match like [0010].
export function hasMatchInSingleRow (row) {
    return _.max(_.values(_.countBy(row))) > 2;
};

// return the index of all occurrences of `value` in `list`. [5, 3, 7, 5], 5 -> [0, 3]
export function indexOfAll (list, value) {
    return _.reduce(list, (acc, e, i) => {
        if (e === value) {
            acc.push(i);
        }

        return acc;
    }, []);
};

// checks across both rows for a match, like [0101]
//                                           [2031]
export function hasMatchInPairOfRows (pairOfRows) {
    let allValues = _.uniq(_.flatten(pairOfRows));
    let allMatches = _.map(allValues, value => {
        return _.uniq([...indexOfAll(pairOfRows[0], value),
                       ...indexOfAll(pairOfRows[1], value)]).sort();
    });

    return _.some(allMatches, match => {
        return _.some([
            _.isEqual(match, [0, 1, 2]),
            _.isEqual(match, [1, 2, 3]),
            _.isEqual(match, [0, 1, 2, 3]),
        ]);
    });
};

/**
 * @private
 * @description Used to provide more granularity to functions that need to call iterchunks
 * on a non-transposed set of orbs, as well as a transposed set of orbs.
 * @see findMatches
 */
let _iterchunks = (orbs, chunkLimitRange, includePositionInformation, isTransposed) => {
    let chunks = [];
    let [width, height] = chunkLimitRange;
    let [finalPositionWidth, finalPositionHeight] = [orbs[0].length - width, orbs.length - height];
    _.each(_.range(0, finalPositionHeight + 1), heightIndex => {
        _.each(_.range(0, finalPositionWidth + 1), widthIndex => {
            let chunkData = orbs.slice(heightIndex, heightIndex + height).map(row => {
                return row.slice(widthIndex, widthIndex + width);
            });

            if (includePositionInformation) {
                let startingCoordinates = [heightIndex, widthIndex];
                let endingCoordinates = [heightIndex + height - 1, widthIndex + width - 1];
                if (isTransposed) {
                    startingCoordinates = startingCoordinates.reverse();
                    endingCoordinates = endingCoordinates.reverse();
                }

                chunkData.push({
                    position: {
                        first: startingCoordinates,
                        last: endingCoordinates
                    }
                });
            }

            chunks.push(chunkData);
        });
    });
    return chunks;
};

/**
 * With `orbs` being
 * [ [ 6, 5, 4 ],
 *   [ 3, 2, 2 ],
 *   [ 6, 4, 0 ] ]
 * And with a [3, 2] `chunkLimitRange`, this will yield each 3x2
 * grouping, and then, each available 2x3 grouping.
 * [ [ [6, 5, 4], [3, 2, 2] ], [ [3, 2, 2], [6, 4, 0] ],
 *   [ [6, 3, 6], [5, 2, 4] ], [ [5, 2, 4], [4, 2, 0] ] ]
 *
 * If you want to also return the position of the first member of the chunk,
 * as row/col coordinates, pass in `includePositionInformation`. That will return the
 * same data as above, but with a final piece of information, an object with a `position`
 * key that maps to the first and last row/col coordinates of that chunk.
 * [
 *     [
 *         [6, 5, 4], [3, 2, 2],
 *         {
 *             position: {
 *                 first: [0, 0],
 *                 last: [1, 2]
 *             }
 *         }
 *     ],
 *     [
 *         [3, 2, 2], [6, 4, 0],
 *         {
 *             position: {
 *                 first: [1, 0],
 *                 last: [2, 2]
 *             }
 *         }
 *     ],
 *     [
 *         [6, 3, 6], [5, 2, 4],
 *         {
 *             position: {
 *                 first: [0, 0],
 *                 last: [2, 1]
 *             }
 *         }
 *     ],
 *     [
 *         [5, 2, 4], [4, 2, 0]
 *         {
 *             position: {
 *                 first: [0, 1],
 *                 last: [2, 2]
 *             }
 *         }
 *     ]
 * ]
 */
export function iterchunks (orbs, chunkLimitRange = [4, 2], includePositionInformation = false) {
    let transposedOrbs = _.zip(...orbs);
    return [
            ..._iterchunks(orbs, chunkLimitRange, includePositionInformation, false),
            ..._iterchunks(transposedOrbs, chunkLimitRange, includePositionInformation, true)
    ]
};

let _findMatches = (chunks, isTransposed) => {
    let matches = [];

    _.each(chunks, chunk => {
        let orbs = _.first(chunk);
        let metadata = _.last(chunk);
        if (_.uniq(orbs).length === 1) {
            let anchor = metadata.position.first;
            let firstOrb = anchor;
            let secondOrb;
            let thirdOrb;

            if (isTransposed) {
                secondOrb = [anchor[0] + 1, anchor[1]];
                thirdOrb = [anchor[0] + 2, anchor[1]];
            } else {
                secondOrb = [anchor[0], anchor[1] + 1];
                thirdOrb = [anchor[0], anchor[1] + 2];
            }

            let absolutePositions = [
                firstOrb,
                secondOrb,
                thirdOrb
            ];
            matches.push(absolutePositions);
        }
    });

    return matches;
};

export function findMatches(orbs) {
    let chunksOriginal = _iterchunks(orbs, [3, 1], true);
    let chunksTransposed = _iterchunks(_.zip(...orbs), [3, 1], true, true);

    return [
            ..._findMatches(chunksOriginal, false),
            ..._findMatches(chunksTransposed, true)
    ];

};

export function combineMatches(matches) {
    let combinedMatches = [];
    let unused = matches;
    let couldMatch;
    let before;
    let currentMatch;

    while (unused[0] != null) {
        currentMatch = new SortedSet(unused[0]);
        unused.shift();
        couldMatch = _.clone(unused);

        _.each(couldMatch, m => {
            //only union if there is an overlap!
            if (currentMatch.intersection(m).toArray()[0] != null) {
                before = currentMatch.toArray();
                currentMatch.swap(0, currentMatch.length, currentMatch.union(m));
                if (before != currentMatch.toArray()) {
                    unused.splice(unused.indexOf(m), 1);
                }
            }
        });
        combinedMatches.push(currentMatch.toArray());
    }
    return combinedMatches;
};

export class Board {
    constructor (width = 8, height = 8, types = _.range(7)) {
        this.width = width;
        this.height = height;
        this.types = types;
        this.createValidOrbs();
        // this.evaluateAll(this.orbs);
    }

    get size() {
        return [this.width, this.height];
    }

    get availableTypes() {
        return _.uniq(_.flatten(this.orbs)).sort();
    }

    /**
      * 1. logs data for each match and replaces each orb with 'X'
      * 2. replaces each 'X' and all above orbs with either the orb directly above or a random new orb
      * 3. returns the match data -> [[match1Type, match1Amount], [match2Type, match2Amount], ...]
      */
    evaluate(matches, dropOptions = this.types) {
        let matchData = [];

        _.each(matches, match => {
            // log data
            matchData.push([this.orbs[match[0][0]][match[0][1]], match.length]);
            // replace each coordinate with 'X'
            _.each(match, coord => {
                let [row, col] = coord;
                this.orbs[row][col] = 'X'
            })
        });

        /**
          * drop down and generate matches
          * 1. reads across starting from the top
          * 2. when it hits 'X', loops from that position directly up
          * 3. if the row isn't 0, it takes the orb from above
          * 4. if the row is 0, it creates a random orb
        */
        _.each(_.range(this.height), row =>{
            _.each(_.range(this.width), col => { //1
                if (this.orbs[row][col] == 'X') {
                    for (var z = row; z >= 0; z--) { //2
                        if (z > 0) { //3
                            this.orbs[z][col] = this.orbs[z - 1][col];
                        } else { //4
                            this.orbs[z][col] = _.sample(dropOptions);;
                        };
                    };
                };
            });
        });

        return matchData;
    }

    evaluateAll() {
        var matchEvents = [];
        while (this.hasMatch()) {
            matchEvents.push(this.evaluate());
        }
    }

    needsShuffle() {
        return !this.hasMatch();
    }

    hasMatch() {
        let chunks = iterchunks(this.orbs);
        // [[[1, 2, 3], [2, 3, 4]], [[3, 4, 5], [4, 5, 6]]] becomes
        //  [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
        let flatChunks = _.flattenDepth(chunks, 1);
        let hasWideStyleMatch = _.some(_.map(flatChunks, hasMatchInSingleRow));
        return hasWideStyleMatch || _.some(_.map(chunks), hasMatchInPairOfRows);
    }

    hasMatchEvent() {
        return Boolean(findMatches(this.orbs)[0]);
    }

    swap(swapOrbs, playerSwap = true) {
        let [[row1, col1], [row2, col2]] = swapOrbs;
        let orbsBefore = _.cloneDeep(this.orbs);
        this.orbs[row1][col1] = orbsBefore[row2][col2]
        this.orbs[row2][col2] = orbsBefore[row1][col1]

        // undo the swap if it did not yeild a match
        if (playerSwap && !this.hasMatchEvent()) {
            this.orbs = orbsBefore;
        };
    }

    _unmatch(row, col, match, skipToRandom = false) {
        let thisOrb = this.orbs[row][col];
        let swapped = false;
        let directions = _.shuffle(['up', 'down', 'left', 'right']);
        for (let i = 0; i < 4; i++) {
            if (skipToRandom) { break };
            if (directions[i] === 'up' && !_.isUndefined(this.orbs[row - 1]) && this.orbs[row - 1][col] !== thisOrb) {
                this.swap([[row, col], [row - 1, col]], false);
                swapped = true;
                break;
            } else if (directions[i] === 'down' && !_.isUndefined(this.orbs[row + 1]) && this.orbs[row + 1][col] !== thisOrb) {
                this.swap([[row, col], [row + 1, col]], false);
                swapped = true;
                break;
            } else if (directions[i] === 'left' && !_.isUndefined(this.orbs[row][col - 1]) && this.orbs[row][col - 1] !== thisOrb) {
                this.swap([[row, col], [row, col - 1]], false);
                swapped = true;
                break;
            } else if (directions[i] === 'right' && !_.isUndefined(this.orbs[row][col + 1]) && this.orbs[row][col + 1] !== thisOrb) {
                this.swap([[row, col], [row, col + 1]], false);
                swapped = true;
                break;
            }
        }
        while (!swapped) {
            let [randomRow, randomCol] = [_.random(this.height - 1), _.random(this.width - 1)];
            if (!_.includes(match, [randomRow, randomCol]) && this.orbs[randomRow][randomCol] !== thisOrb) {
                this.swap([[row, col], [randomRow, randomCol]], false);
                swapped = true;
            }
        }
    }

    unmatch() {
        while (this.hasMatchEvent()) {
            let intersections = [];
            let match = combineMatches(findMatches(this.orbs))[0];
            let [rowCoords, colCoords] = _.zip(...match);
            if (_.uniq(rowCoords).length === 1 || _.uniq(colCoords).length === 1) {
                let midNeighbors;
                let [midRow, midCol] = match[Math.floor(match.length / 2)];
                if (_.uniq(rowCoords).length === 1) {
                    midNeighbors = [this.orbs[midRow][midCol - 1], this.orbs[midRow][midCol + 1]];
                } else {
                    midNeighbors = [this.orbs[midRow - 1][midCol], this.orbs[midRow + 1][midCol]];
                }
                let isSideBySideMatch = _.includes(midNeighbors, this.orbs[midRow][midCol]);
                this._unmatch(...match[Math.floor(match.length / 2)], match, isSideBySideMatch);
            } else {
                let matchRows = [];
                let matchCols = [];
                _.each(_.countBy(rowCoords), (v, k) => {
                    if (v > 2) {
                        matchRows.push(_.toInteger(k));
                    };
                });
                _.each(_.countBy(colCoords), (v, k) => {
                    if (v > 2) {
                        matchCols.push(_.toInteger(k));
                    };
                });
                _.each(match, coords => {
                    if (_.includes(matchRows, coords[0]) && _.includes(matchCols, coords[1])) {
                        intersections.push(coords);
                    };
                });
                this._unmatch(..._.sample(intersections), match);
            };
        };
    }

    shuffle() {
        this.orbs = _.map(this.orbs, row => _.shuffle(row));
        this.unmatch();
        if (this.needsShuffle()) {
            this.shuffle();
        };
    }

    createValidOrbs() {
        do {
            let chooseOrb = () => { return _.sample(this.types); };
            let sampleRow = () => { return _.times(this.width, chooseOrb); };
            this.orbs = _.zip(..._.times(this.height, sampleRow));
        }
        while (this.hasMatchEvent() || this.needsShuffle());
    }
};
<commit_msg>Integrate new logic into Board constructor<commit_after>'use strict';
import * as _ from 'lodash';
let SortedSet = require('collections/sorted-set');

// checks for one row of the iterchunk board for a match like [0010].
export function hasMatchInSingleRow (row) {
    return _.max(_.values(_.countBy(row))) > 2;
};

// return the index of all occurrences of `value` in `list`. [5, 3, 7, 5], 5 -> [0, 3]
export function indexOfAll (list, value) {
    return _.reduce(list, (acc, e, i) => {
        if (e === value) {
            acc.push(i);
        }

        return acc;
    }, []);
};

// checks across both rows for a match, like [0101]
//                                           [2031]
export function hasMatchInPairOfRows (pairOfRows) {
    let allValues = _.uniq(_.flatten(pairOfRows));
    let allMatches = _.map(allValues, value => {
        return _.uniq([...indexOfAll(pairOfRows[0], value),
                       ...indexOfAll(pairOfRows[1], value)]).sort();
    });

    return _.some(allMatches, match => {
        return _.some([
            _.isEqual(match, [0, 1, 2]),
            _.isEqual(match, [1, 2, 3]),
            _.isEqual(match, [0, 1, 2, 3]),
        ]);
    });
};

/**
 * @private
 * @description Used to provide more granularity to functions that need to call iterchunks
 * on a non-transposed set of orbs, as well as a transposed set of orbs.
 * @see findMatches
 */
let _iterchunks = (orbs, chunkLimitRange, includePositionInformation, isTransposed) => {
    let chunks = [];
    let [width, height] = chunkLimitRange;
    let [finalPositionWidth, finalPositionHeight] = [orbs[0].length - width, orbs.length - height];
    _.each(_.range(0, finalPositionHeight + 1), heightIndex => {
        _.each(_.range(0, finalPositionWidth + 1), widthIndex => {
            let chunkData = orbs.slice(heightIndex, heightIndex + height).map(row => {
                return row.slice(widthIndex, widthIndex + width);
            });

            if (includePositionInformation) {
                let startingCoordinates = [heightIndex, widthIndex];
                let endingCoordinates = [heightIndex + height - 1, widthIndex + width - 1];
                if (isTransposed) {
                    startingCoordinates = startingCoordinates.reverse();
                    endingCoordinates = endingCoordinates.reverse();
                }

                chunkData.push({
                    position: {
                        first: startingCoordinates,
                        last: endingCoordinates
                    }
                });
            }

            chunks.push(chunkData);
        });
    });
    return chunks;
};

/**
 * With `orbs` being
 * [ [ 6, 5, 4 ],
 *   [ 3, 2, 2 ],
 *   [ 6, 4, 0 ] ]
 * And with a [3, 2] `chunkLimitRange`, this will yield each 3x2
 * grouping, and then, each available 2x3 grouping.
 * [ [ [6, 5, 4], [3, 2, 2] ], [ [3, 2, 2], [6, 4, 0] ],
 *   [ [6, 3, 6], [5, 2, 4] ], [ [5, 2, 4], [4, 2, 0] ] ]
 *
 * If you want to also return the position of the first member of the chunk,
 * as row/col coordinates, pass in `includePositionInformation`. That will return the
 * same data as above, but with a final piece of information, an object with a `position`
 * key that maps to the first and last row/col coordinates of that chunk.
 * [
 *     [
 *         [6, 5, 4], [3, 2, 2],
 *         {
 *             position: {
 *                 first: [0, 0],
 *                 last: [1, 2]
 *             }
 *         }
 *     ],
 *     [
 *         [3, 2, 2], [6, 4, 0],
 *         {
 *             position: {
 *                 first: [1, 0],
 *                 last: [2, 2]
 *             }
 *         }
 *     ],
 *     [
 *         [6, 3, 6], [5, 2, 4],
 *         {
 *             position: {
 *                 first: [0, 0],
 *                 last: [2, 1]
 *             }
 *         }
 *     ],
 *     [
 *         [5, 2, 4], [4, 2, 0]
 *         {
 *             position: {
 *                 first: [0, 1],
 *                 last: [2, 2]
 *             }
 *         }
 *     ]
 * ]
 */
export function iterchunks (orbs, chunkLimitRange = [4, 2], includePositionInformation = false) {
    let transposedOrbs = _.zip(...orbs);
    return [
            ..._iterchunks(orbs, chunkLimitRange, includePositionInformation, false),
            ..._iterchunks(transposedOrbs, chunkLimitRange, includePositionInformation, true)
    ]
};

let _findMatches = (chunks, isTransposed) => {
    let matches = [];

    _.each(chunks, chunk => {
        let orbs = _.first(chunk);
        let metadata = _.last(chunk);
        if (_.uniq(orbs).length === 1) {
            let anchor = metadata.position.first;
            let firstOrb = anchor;
            let secondOrb;
            let thirdOrb;

            if (isTransposed) {
                secondOrb = [anchor[0] + 1, anchor[1]];
                thirdOrb = [anchor[0] + 2, anchor[1]];
            } else {
                secondOrb = [anchor[0], anchor[1] + 1];
                thirdOrb = [anchor[0], anchor[1] + 2];
            }

            let absolutePositions = [
                firstOrb,
                secondOrb,
                thirdOrb
            ];
            matches.push(absolutePositions);
        }
    });

    return matches;
};

export function findMatches(orbs) {
    let chunksOriginal = _iterchunks(orbs, [3, 1], true);
    let chunksTransposed = _iterchunks(_.zip(...orbs), [3, 1], true, true);

    return [
            ..._findMatches(chunksOriginal, false),
            ..._findMatches(chunksTransposed, true)
    ];

};

export function combineMatches(matches) {
    let combinedMatches = [];
    let unused = matches;
    let couldMatch;
    let before;
    let currentMatch;

    while (unused[0] != null) {
        currentMatch = new SortedSet(unused[0]);
        unused.shift();
        couldMatch = _.clone(unused);

        _.each(couldMatch, m => {
            //only union if there is an overlap!
            if (currentMatch.intersection(m).toArray()[0] != null) {
                before = currentMatch.toArray();
                currentMatch.swap(0, currentMatch.length, currentMatch.union(m));
                if (before != currentMatch.toArray()) {
                    unused.splice(unused.indexOf(m), 1);
                }
            }
        });
        combinedMatches.push(currentMatch.toArray());
    }
    return combinedMatches;
};

export class Board {
    constructor (width = 8, height = 8, types = _.range(7)) {
        this.width = width;
        this.height = height;
        this.types = types;
        let chooseOrb = () => { return _.sample(this.types); };
        let sampleRow = () => { return _.times(this.width, chooseOrb); };
        this.orbs = _.zip(..._.times(this.height, sampleRow));
        if (this.hasMatchEvent() || this.needsShuffle()) {
            this.shuffle();
        };
        // this.evaluateAll()
    }

    get size() {
        return [this.width, this.height];
    }

    get availableTypes() {
        return _.uniq(_.flatten(this.orbs)).sort();
    }

    /**
      * 1. logs data for each match and replaces each orb with '\u241a'
      * 2. replaces each '\u241a' and all above orbs with either the orb directly above or a random new orb
      * 3. returns the match data -> [[match1Type, match1Amount], [match2Type, match2Amount], ...]
      */
    evaluate(matches, dropOptions = this.types) {
        let matchData = [];

        _.each(matches, match => {
            // log data
            matchData.push([this.orbs[match[0][0]][match[0][1]], match.length]);
            // replace each coordinate with '\u241a'
            _.each(match, coord => {
                let [row, col] = coord;
                this.orbs[row][col] = '\u241a'
            })
        });

        /**
          * drop down and generate matches
          * 1. reads across starting from the top
          * 2. when it hits '\u241a', loops from that position directly up
          * 3. if the row isn't 0, it takes the orb from above
          * 4. if the row is 0, it creates a random orb
        */
        _.each(_.range(this.height), row =>{
            _.each(_.range(this.width), col => { //1
                if (this.orbs[row][col] == '\u241a') {
                    for (var z = row; z >= 0; z--) { //2
                        if (z > 0) { //3
                            this.orbs[z][col] = this.orbs[z - 1][col];
                        } else { //4
                            this.orbs[z][col] = _.sample(dropOptions);;
                        };
                    };
                };
            });
        });

        return matchData;
    }

    evaluateAll() {
        var matchEvents = [];
        while (this.hasMatch()) {
            matchEvents.push(this.evaluate());
        }
    }

    needsShuffle() {
        return !this.hasMatch();
    }

    hasMatch() {
        let chunks = iterchunks(this.orbs);
        // [[[1, 2, 3], [2, 3, 4]], [[3, 4, 5], [4, 5, 6]]] becomes
        //  [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
        let flatChunks = _.flattenDepth(chunks, 1);
        let hasWideStyleMatch = _.some(_.map(flatChunks, hasMatchInSingleRow));
        return hasWideStyleMatch || _.some(_.map(chunks), hasMatchInPairOfRows);
    }

    hasMatchEvent() {
        return Boolean(findMatches(this.orbs)[0]);
    }

    swap(swapOrbs, playerSwap = true) {
        let [[row1, col1], [row2, col2]] = swapOrbs;
        let orbsBefore = _.cloneDeep(this.orbs);
        this.orbs[row1][col1] = orbsBefore[row2][col2]
        this.orbs[row2][col2] = orbsBefore[row1][col1]

        // undo the swap if it did not yeild a match
        if (playerSwap && !this.hasMatchEvent()) {
            this.orbs = orbsBefore;
        };
    }

    _unmatch(row, col, match, skipToRandom = false) {
        let thisOrb = this.orbs[row][col];
        let swapped = false;
        let directions = _.shuffle(['up', 'down', 'left', 'right']);
        for (let i = 0; i < 4; i++) {
            if (skipToRandom) { break };
            if (directions[i] === 'up' && !_.isUndefined(this.orbs[row - 1]) && this.orbs[row - 1][col] !== thisOrb) {
                this.swap([[row, col], [row - 1, col]], false);
                swapped = true;
                break;
            } else if (directions[i] === 'down' && !_.isUndefined(this.orbs[row + 1]) && this.orbs[row + 1][col] !== thisOrb) {
                this.swap([[row, col], [row + 1, col]], false);
                swapped = true;
                break;
            } else if (directions[i] === 'left' && !_.isUndefined(this.orbs[row][col - 1]) && this.orbs[row][col - 1] !== thisOrb) {
                this.swap([[row, col], [row, col - 1]], false);
                swapped = true;
                break;
            } else if (directions[i] === 'right' && !_.isUndefined(this.orbs[row][col + 1]) && this.orbs[row][col + 1] !== thisOrb) {
                this.swap([[row, col], [row, col + 1]], false);
                swapped = true;
                break;
            }
        }
        while (!swapped) {
            let [randomRow, randomCol] = [_.random(this.height - 1), _.random(this.width - 1)];
            if (!_.includes(match, [randomRow, randomCol]) && this.orbs[randomRow][randomCol] !== thisOrb) {
                this.swap([[row, col], [randomRow, randomCol]], false);
                swapped = true;
            }
        }
    }

    unmatch() {
        while (this.hasMatchEvent()) {
            let intersections = [];
            let match = combineMatches(findMatches(this.orbs))[0];
            let [rowCoords, colCoords] = _.zip(...match);
            if (_.uniq(rowCoords).length === 1 || _.uniq(colCoords).length === 1) {
                let midNeighbors;
                let [midRow, midCol] = match[Math.floor(match.length / 2)];
                if (_.uniq(rowCoords).length === 1) {
                    midNeighbors = [this.orbs[midRow][midCol - 1], this.orbs[midRow][midCol + 1]];
                } else {
                    midNeighbors = [this.orbs[midRow - 1][midCol], this.orbs[midRow + 1][midCol]];
                }
                let isSideBySideMatch = _.includes(midNeighbors, this.orbs[midRow][midCol]);
                this._unmatch(...match[Math.floor(match.length / 2)], match, isSideBySideMatch);
            } else {
                let matchRows = [];
                let matchCols = [];
                _.each(_.countBy(rowCoords), (v, k) => {
                    if (v > 2) {
                        matchRows.push(_.toInteger(k));
                    };
                });
                _.each(_.countBy(colCoords), (v, k) => {
                    if (v > 2) {
                        matchCols.push(_.toInteger(k));
                    };
                });
                _.each(match, coords => {
                    if (_.includes(matchRows, coords[0]) && _.includes(matchCols, coords[1])) {
                        intersections.push(coords);
                    };
                });
                this._unmatch(..._.sample(intersections), match);
            };
        };
    }

    shuffle() {
        this.orbs = _.map(this.orbs, row => _.shuffle(row));
        this.unmatch();
        if (this.needsShuffle()) {
            this.shuffle();
        };
    }
};
<|endoftext|>


<commit_before>var gulp = require('gulp');
var pluginLoader = require('gulp-load-plugins');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var del = require('del');

// Gulp plugins
var $g = pluginLoader();

// PostCSS plugins
var $p = pluginLoader({
    pattern: ['postcss-*', 'postcss.*', 'autoprefixer', 'cssnano', 'stylelint'],
    replaceString: /^postcss(-|\.)/
});

// Imagemin plugins
var $i = pluginLoader({
    pattern: ['imagemin-*', 'imagemin.*'],
    replaceString: /^imagemin(-|\.)/
});

// Environmant
var dev = $g.environments.development;
var prod = $g.environments.production;

var browserify = require('browserify');
var debowerify = require('debowerify');

var AUTOPREFIXER_BROWSERS = [
    'ie >= 10',
    'ie_mob >= 10',
    'ff >= 30',
    'chrome >= 34',
    'safari >= 7',
    'opera >= 23',
    'ios >= 7',
    'android >= 4.4',
    'bb >= 10'
];

var src = 'src/AppBundle/Resources/assets';
var dest = 'src/AppBundle/Resources/public';

gulp.task('clean', function() {
    return del([dest + '/*']);
});

gulp.task('clean:styles', function() {
    return del([dest + '/css/*']);
});

gulp.task('clean:scripts', function() {
    return del([dest + '/js/*']);
});

gulp.task('clean:fonts', function() {
    return del([dest + '/fonts/*']);
});

gulp.task('clean:images', function() {
    return del([dest + '/img/*']);
});

gulp.task('clean:favicons', function() {
    return del([
        'web/android-chrome-*.png',
        'web/apple-touch-*.png',
        'web/browserconfig.xml',
        'web/coast-*.png',
        'web/favicon.ico',
        'web/favicon-*.png',
        'web/firefox_app_*.png',
        'web/manifest.json',
        'web/manifest.webapp',
        'web/mstile-*.png',
        'web/open-graph.png',
        'web/twitter.png',
        'web/yandex-*.png',
        'web/yandex-browser-manifest.json'
    ]);
});

gulp.task('styles', ['clean:styles'], function () {
    var processors = [
        $p.bemLinter(),
        $p.stylelint(),
        $p.autoprefixer(AUTOPREFIXER_BROWSERS)
    ];

    if (prod()) {
        processors.push($p.cssnano());
    }

    processors.push($p.reporter());

    return gulp.src(src + '/scss/style.scss')
        .pipe(dev($g.sourcemaps.init({loadMaps: true})))
        .pipe($g.sass().on('error', $g.sass.logError))
        .pipe($g.postcss(processors))
        .pipe($g.size({title: 'styles'}))
        .pipe(dev($g.sourcemaps.write('./')))
        .pipe(gulp.dest(dest + '/css'));
});

gulp.task('scripts', ['clean:scripts'], function () {
    var b = browserify({
        entries: src + '/js/app.js',
        debug: true,
        transform: [debowerify]
    });

    return b.bundle()
        .pipe(source('app.js'))
        .pipe(buffer())
        .pipe(dev($g.sourcemaps.init({loadMaps: true})))
        .pipe(prod($g.uglify()))
        .pipe($g.size({title: 'scripts'}))
        .pipe(dev($g.sourcemaps.write('./')))
        .pipe(gulp.dest(dest + '/js'));
});

gulp.task('fonts', ['clean:fonts'], function() {
    return gulp.src('bower_components/font-awesome/fonts/**/*.{ttf,woff,eof,svg}')
        .pipe($g.size({title: 'fonts'}))
        .pipe(gulp.dest(dest + '/fonts'));
});

gulp.task('images', ['clean:images'], function() {
    return gulp.src(src + '/img/**/*.{png,svg,jpg,gif}')
        .pipe($g.imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [
                $i.gifsicle(),
                $i.jpegtran(),
                $i.optipng(),
                $i.pngquant()
            ]
        }))
        .pipe($g.size({title: 'images'}))
        .pipe(gulp.dest(dest + '/img'));
});

gulp.task('favicons', ['clean:favicons'], function() {
    return gulp.src(src + '/img/symfony_logo.png')
        .pipe($g.favicons({
            appName: 'Symfony',
            appDescription: 'This is my application',
            background: '#ffffff',
            path: '/',
            url: '/',
            display: 'standalone',
            orientation: 'portrait',
            version: 1.0,
            logging: false,
            online: false,
            html: 'app/Resources/views/favicons.html.twig'
        }))
        .pipe($g.size({title: 'favicons'}))
        .pipe(gulp.dest('web/'));
});

gulp.task('watch', function() {
    gulp.watch(src + '/scss/**/*.scss', ['styles']);
    gulp.watch(src + '/js/**/*.js', ['scripts']);
});

gulp.task('build', ['styles', 'scripts', 'fonts', 'images', 'favicons']);
gulp.task('default', ['build']);
<commit_msg>Fix missing font file in build script<commit_after>var gulp = require('gulp');
var pluginLoader = require('gulp-load-plugins');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var del = require('del');

// Gulp plugins
var $g = pluginLoader();

// PostCSS plugins
var $p = pluginLoader({
    pattern: ['postcss-*', 'postcss.*', 'autoprefixer', 'cssnano', 'stylelint'],
    replaceString: /^postcss(-|\.)/
});

// Imagemin plugins
var $i = pluginLoader({
    pattern: ['imagemin-*', 'imagemin.*'],
    replaceString: /^imagemin(-|\.)/
});

// Environmant
var dev = $g.environments.development;
var prod = $g.environments.production;

var browserify = require('browserify');
var debowerify = require('debowerify');

var AUTOPREFIXER_BROWSERS = [
    'ie >= 10',
    'ie_mob >= 10',
    'ff >= 30',
    'chrome >= 34',
    'safari >= 7',
    'opera >= 23',
    'ios >= 7',
    'android >= 4.4',
    'bb >= 10'
];

var src = 'src/AppBundle/Resources/assets';
var dest = 'src/AppBundle/Resources/public';

gulp.task('clean', function() {
    return del([dest + '/*']);
});

gulp.task('clean:styles', function() {
    return del([dest + '/css/*']);
});

gulp.task('clean:scripts', function() {
    return del([dest + '/js/*']);
});

gulp.task('clean:fonts', function() {
    return del([dest + '/fonts/*']);
});

gulp.task('clean:images', function() {
    return del([dest + '/img/*']);
});

gulp.task('clean:favicons', function() {
    return del([
        'web/android-chrome-*.png',
        'web/apple-touch-*.png',
        'web/browserconfig.xml',
        'web/coast-*.png',
        'web/favicon.ico',
        'web/favicon-*.png',
        'web/firefox_app_*.png',
        'web/manifest.json',
        'web/manifest.webapp',
        'web/mstile-*.png',
        'web/open-graph.png',
        'web/twitter.png',
        'web/yandex-*.png',
        'web/yandex-browser-manifest.json'
    ]);
});

gulp.task('styles', ['clean:styles'], function () {
    var processors = [
        $p.bemLinter(),
        $p.stylelint(),
        $p.autoprefixer(AUTOPREFIXER_BROWSERS)
    ];

    if (prod()) {
        processors.push($p.cssnano());
    }

    processors.push($p.reporter());

    return gulp.src(src + '/scss/style.scss')
        .pipe(dev($g.sourcemaps.init({loadMaps: true})))
        .pipe($g.sass().on('error', $g.sass.logError))
        .pipe($g.postcss(processors))
        .pipe($g.size({title: 'styles'}))
        .pipe(dev($g.sourcemaps.write('./')))
        .pipe(gulp.dest(dest + '/css'));
});

gulp.task('scripts', ['clean:scripts'], function () {
    var b = browserify({
        entries: src + '/js/app.js',
        debug: true,
        transform: [debowerify]
    });

    return b.bundle()
        .pipe(source('app.js'))
        .pipe(buffer())
        .pipe(dev($g.sourcemaps.init({loadMaps: true})))
        .pipe(prod($g.uglify()))
        .pipe($g.size({title: 'scripts'}))
        .pipe(dev($g.sourcemaps.write('./')))
        .pipe(gulp.dest(dest + '/js'));
});

gulp.task('fonts', ['clean:fonts'], function() {
    return gulp.src('bower_components/font-awesome/fonts/**/*.{ttf,woff,woff2,eof,svg}')
        .pipe($g.size({title: 'fonts'}))
        .pipe(gulp.dest(dest + '/fonts'));
});

gulp.task('images', ['clean:images'], function() {
    return gulp.src(src + '/img/**/*.{png,svg,jpg,gif}')
        .pipe($g.imagemin({
            progressive: true,
            svgoPlugins: [{removeViewBox: false}],
            use: [
                $i.gifsicle(),
                $i.jpegtran(),
                $i.optipng(),
                $i.pngquant()
            ]
        }))
        .pipe($g.size({title: 'images'}))
        .pipe(gulp.dest(dest + '/img'));
});

gulp.task('favicons', ['clean:favicons'], function() {
    return gulp.src(src + '/img/symfony_logo.png')
        .pipe($g.favicons({
            appName: 'Symfony',
            appDescription: 'This is my application',
            background: '#ffffff',
            path: '/',
            url: '/',
            display: 'standalone',
            orientation: 'portrait',
            version: 1.0,
            logging: false,
            online: false,
            html: 'app/Resources/views/favicons.html.twig'
        }))
        .pipe($g.size({title: 'favicons'}))
        .pipe(gulp.dest('web/'));
});

gulp.task('watch', function() {
    gulp.watch(src + '/scss/**/*.scss', ['styles']);
    gulp.watch(src + '/js/**/*.js', ['scripts']);
});

gulp.task('build', ['styles', 'scripts', 'fonts', 'images', 'favicons']);
gulp.task('default', ['build']);
<|endoftext|>


<commit_before>jQuery(function ($) {
    "use strict";

    $.widget("ui.searchbar", {
        // Default options
        options: {
            filters_container: '',  // must be specified
            filters_link: ''  // must be specified
        },

        _create: function () {
            this.filters_container = $(this.options.filters_container);

            this._markup();
            this._filtersHandler();
            this._tagFilters(this.filters_container, this.element.find('filters'));
        },

        _markup: function() {
            var filters = '<filters></filters>';

            var el = this.element,
                input = el.find('input').detach(),
                html = '

	' + filters + '

';

            // Get rid of "hidden" class
            this.filters_container.hide().removeClass('hidden').addClass('searchbar_filters');

            el.addClass('searchbar').html(html);
            el.find('.input').append(input);
        },

        /**
         * Handle events of filters box
         * @private
         */
        _filtersHandler: function () {
            var self = this,
                filters = this.filters_container,
                filters_list = this.element.find('filters'),
                filters_link = $(this.options.filters_link);

            // Hide when focus is lost
            $(document).mouseup(function (e) {
                var $target = $(e.target);
                if (!filters.is(e.target) // if the target of the click isn't the container...
                    && filters.has(e.target).length === 0 // ... nor a descendant of the container
                    && !$target.hasClass('select2-results__option') // item selection should be ignored for select2
                    && !$target.hasClass('select2-search__field') // search field in select2
                    && !$target.is(filters_link) // if the target of the click isn't "Advanced" link
                )
                {
                    filters.hide();
                }
            });

            // Handle clicks on "Advanced" link
            filters_link.click(function(e) {
                filters.toggle();
                e.preventDefault();
                return false
            });

            filters.on("keyup change", "input, select", function() {
                self._tagFilters(filters, filters_list);
            });

            // Handle clicks on "x"
            filters_list.on("click", "x", function(e) {
                var span = $(this).parent(),
                    field = filters.find('#'+span.data('for'));
                field.val('');

                if (self._isDropdown(field))
                {
                    // Select2 requires event to be triggered
                    field.trigger("change");
                }
                span.remove();

                e.preventDefault();
                return false;
            });

            // Handle clicks on filter tag
            filters_list.on("click", "span", function(e) {
                var span = $(this),
                    field = filters.find('#'+span.data('for'));
                filters.show();

                if (self._isDropdown(field))
                {
                    field.select2("open");
                }
                else
                {
                    field.focus();
                }

                e.preventDefault();
                return false;
            });
        },

        /**
         * Create tags for applied filters
         * @private
         */
        _tagFilters: function(filters, container) {
            var self = this;
            var html = filters.find("input, select").map(function() {
                var el = $(this),
                    v = $(this).val();
                if (!v)
                {
                    return;

                }

                if (self._isDropdown(el))
                {
                    v = el.find("option:selected").text();
                }

                var id = $(this).attr('id'),
                    name = $(this).attr('title'),
                    title = v;

                // Shorten value
                if (v.length > 15) {
                    v = $.trim(v.substring(0, 12))+'...';
                }

                return '<x></x> ' + name + ': ' + v + '';
            }).get().join('');
            container.html(html);
        },

        /**
         * Check if given field is a "SELECT"
         * @param field
         * @returns {boolean}
         * @private
         */
        _isDropdown: function (field) {
            return (field.prop("tagName") == "SELECT")
        }
    });
});
<commit_msg>added template support<commit_after>jQuery(function ($) {
    "use strict";

    $.widget("ui.searchbar", {
        // Default options
        options: {
            filters_container: '',  // must be specified
            filters_link: '',  // must be specified
            filter_template: '<x></x> {{ name }}: {{ value }}',
            filter_selector: 'span', // css selector for filter item (root node in filter_template)
            close_selector: 'x' // css selector for "x" button in each filter (from filter_template)
        },

        _create: function () {
            this.filters_container = $(this.options.filters_container);

            this._markup();
            this._filtersHandler();
            this._tagFilters(this.filters_container, this.element.find('filters'));
        },

        _markup: function() {
            var filters = '<filters></filters>';

            var el = this.element,
                input = el.find('input').detach(),
                html = '

	' + filters + '

';

            // Get rid of "hidden" class
            this.filters_container.hide().removeClass('hidden').addClass('searchbar_filters');

            el.addClass('searchbar').html(html);
            el.find('.input').append(input);
        },

        /**
         * Handle events of filters box
         * @private
         */
        _filtersHandler: function () {
            var self = this,
                filters = this.filters_container,
                filters_list = this.element.find('filters'),
                filters_link = $(this.options.filters_link);

            // Hide when focus is lost
            $(document).mouseup(function (e) {
                var $target = $(e.target);
                if (!filters.is(e.target) // if the target of the click isn't the container...
                    && filters.has(e.target).length === 0 // ... nor a descendant of the container
                    && !$target.hasClass('select2-results__option') // item selection should be ignored for select2
                    && !$target.hasClass('select2-search__field') // search field in select2
                    && !$target.is(filters_link) // if the target of the click isn't "Advanced" link
                )
                {
                    filters.hide();
                }
            });

            // Handle clicks on "Advanced" link
            filters_link.click(function(e) {
                filters.toggle();
                e.preventDefault();
                return false
            });

            filters.on("keyup change", "input, select", function() {
                self._tagFilters(filters, filters_list);
            });

            // Handle clicks on "x"
            filters_list.on("click", self.options.close_selector, function(e) {
                var span = $(this).parent(),
                    field = filters.find('#'+span.data('for'));
                field.val('');

                if (self._isDropdown(field))
                {
                    // Select2 requires event to be triggered
                    field.trigger("change");
                }
                span.remove();

                e.preventDefault();
                return false;
            });

            // Handle clicks on filter tag
            filters_list.on("click", self.options.filter_selector, function(e) {
                var span = $(this),
                    field = filters.find('#'+span.data('for'));
                filters.show();

                if (self._isDropdown(field))
                {
                    field.select2("open");
                }
                else
                {
                    field.focus();
                }

                e.preventDefault();
                return false;
            });
        },

        /**
         * Create tags for applied filters
         * @private
         */
        _tagFilters: function(filters, container) {
            var self = this;
            var html = filters.find("input, select").map(function() {
                var el = $(this),
                    v = $(this).val();
                if (!v)
                {
                    return;

                }

                if (self._isDropdown(el))
                {
                    v = el.find("option:selected").text();
                }

                var id = $(this).attr('id'),
                    name = $(this).attr('title'),
                    title = v;

                // Shorten value
                if (v.length > 15) {
                    v = $.trim(v.substring(0, 12))+'...';
                }
                return self._applyTemplate(
                    self.options.filter_template,
                    { "id": id, "title": title, "name": name, "value": v }
                )
            }).get().join('');
            container.html(html);
        },

        /**
         * Check if given field is a "SELECT"
         * @param field
         * @returns {boolean}
         * @private
         */
        _isDropdown: function (field) {
            return (field.prop("tagName") == "SELECT")
        },

        /**
         * Replace {{ key }} with values from "params"
         * @param html - {String}
         * @param params - {Object}
         * @private
         * @returns {String}
         */
        _applyTemplate: function(html, params)
        {
            $.each(params, function (key, value) {
                html = html.replace("{{ " + key + " }}", value)
            });
            return html;
        }
    });
});
<|endoftext|>


<commit_before>$(function () {
    // NB: this file is included on multiple pages.  In each context,
    // some of the jQuery selectors below will return empty lists.
    var password_field = $('#id_password, #id_new_password1');

    $.validator.addMethod('password_strength', function (value) {
        return common.password_quality(value, undefined, password_field);
    }, function () {
        return common.password_warning(password_field.val(), password_field);
    });

    function highlight(class_to_add) {
        // Set a class on the enclosing control group.
        return function (element) {
            $(element).closest('.control-group')
                .removeClass('success error')
                .addClass(class_to_add);
        };
    }

    $('#registration, #password_reset').validate({
        rules: {
            password:      'password_strength',
            new_password1: 'password_strength',
        },
        errorElement: "p",
        errorPlacement: function (error, element) {
            // NB: this is called at most once, when the error element
            // is created.
            element.next('.help-inline.alert.alert-error').remove();
            if (element.next().is('label[for="' + element.attr('id') + '"]')) {
                error.insertAfter(element.next()).addClass('help-inline alert alert-error');
            } else {
                error.insertAfter(element).addClass('help-inline alert alert-error');
            }
        },
        highlight:   highlight('error'),
        unhighlight: highlight('success'),
    });

    if (password_field) {
        // Reset the state of the password strength bar if the page
        // was just reloaded due to a validation failure on the backend.
        common.password_quality(password_field.val(), $('#pw_strength .bar'), password_field);
    }

    if ($("#registration").length > 0) {
        // Check if there is no input field with errors.
        if ($('.help-inline:not(:empty)').length === 0) {
            // Find the first input field present in the form that is
            // not hidden and disabled and store it in a variable.
            var firstInputElement = $("input:not(:hidden, :disabled):first");
            // Focus on the first input field in the form.
            common.autofocus(firstInputElement);
        }

        // reset error message displays
        $('#id_team_subdomain_error_client').css('display', 'none');
        if ($('.team_subdomain_error_server').text() === '') {
            $('.team_subdomain_error_server').css('display', 'none');
        }

        $("#timezone").val(moment.tz.guess());
    }

    password_field.on('change keyup', function () {
        // Update the password strength bar even if we aren't validating
        // the field yet.
        common.password_quality($(this).val(), $('#pw_strength .bar'), $(this));
    });

    $("#send_confirm").validate({
        errorElement: "div",
        errorPlacement: function (error) {
            $('.email-frontend-error').empty();
            $("#send_confirm .alert.email-backend-error").remove();
            error.appendTo(".email-frontend-error").addClass("text-error");
        },
        success: function () {
            $('#errors').empty();
        },
    });

    $(".register-page #email, .login-page-container #id_username").on('focusout keydown', function (e) {
        // check if it is the "focusout" or if it is a keydown, then check if
        // the keycode was the one for "enter" (13).
        if (e.type === "focusout" || e.which === 13) {
            $(this).val($.trim($(this).val()));
        }
    });

    var show_subdomain_section = function (bool) {
        var action = bool ? "hide" : "show";
        $("#subdomain_section")[action]();
    };

    $("#realm_in_root_domain").change(function () {
        show_subdomain_section($(this).is(":checked"));
    });

    $("#login_form").validate({
        errorClass: "text-error",
        wrapper: "div",
        submitHandler: function (form) {
            $("#login_form").find('.loader').css('display', 'inline-block');
            $("#login_form").find("button .text").hide();

            form.submit();
        },
        invalidHandler: function () {
            // this removes all previous errors that were put on screen
            // by the server.
            $("#login_form .alert.alert-error").remove();
        },
    });

    function check_subdomain_avilable(subdomain) {
        var url = "/json/realm/subdomain/" + subdomain;
        $.get(url, function (response) {
            if (response.msg !== "available") {
                $("#id_team_subdomain_error_client").html(response.msg);
                $("#id_team_subdomain_error_client").show();
            }
        });
    }

    function update_full_name_section() {
        var selected_realm = $("#source_realm_select").find(":selected").val();
        if (selected_realm && selected_realm !== "on") {
            $("#full_name_input_section").hide();
            $("#profile_info_section").show();
            var avatar_url = $("#source_realm_select").find(":selected").attr('data-avatar');
            var full_name = $("#source_realm_select").find(":selected").attr('data-full-name');
            $("#profile_full_name").text(full_name);
            $("#id_full_name").val(full_name);
            $("#profile_avatar").attr("src", avatar_url);
        } else {
            $("#full_name_input_section").show();
            $("#profile_info_section").hide();
            $("#id_full_name").val("");
        }
    }

    $("#source_realm_select").change(update_full_name_section);
    update_full_name_section();

    var timer;
    $('#id_team_subdomain').on("keydown", function () {
        $('.team_subdomain_error_server').text('').css('display', 'none');
        $("#id_team_subdomain_error_client").css('display', 'none');
        clearTimeout(timer);
    });
    $('#id_team_subdomain').on("keyup", function () {
        clearTimeout(timer);
        timer = setTimeout(check_subdomain_avilable, 250, $('#id_team_subdomain').val());
    });
});
<commit_msg>portico: Add autofocus to the first input field with errors.<commit_after>$(function () {
    // NB: this file is included on multiple pages.  In each context,
    // some of the jQuery selectors below will return empty lists.
    var password_field = $('#id_password, #id_new_password1');

    $.validator.addMethod('password_strength', function (value) {
        return common.password_quality(value, undefined, password_field);
    }, function () {
        return common.password_warning(password_field.val(), password_field);
    });

    function highlight(class_to_add) {
        // Set a class on the enclosing control group.
        return function (element) {
            $(element).closest('.control-group')
                .removeClass('success error')
                .addClass(class_to_add);
        };
    }

    $('#registration, #password_reset').validate({
        rules: {
            password:      'password_strength',
            new_password1: 'password_strength',
        },
        errorElement: "p",
        errorPlacement: function (error, element) {
            // NB: this is called at most once, when the error element
            // is created.
            element.next('.help-inline.alert.alert-error').remove();
            if (element.next().is('label[for="' + element.attr('id') + '"]')) {
                error.insertAfter(element.next()).addClass('help-inline alert alert-error');
            } else {
                error.insertAfter(element).addClass('help-inline alert alert-error');
            }
        },
        highlight:   highlight('error'),
        unhighlight: highlight('success'),
    });

    if (password_field) {
        // Reset the state of the password strength bar if the page
        // was just reloaded due to a validation failure on the backend.
        common.password_quality(password_field.val(), $('#pw_strength .bar'), password_field);
    }

    if ($("#registration").length > 0) {
        // Check if there is no input field with errors.
        if ($('.help-inline:not(:empty)').length === 0) {
            // Find the first input field present in the form that is
            // not hidden and disabled and store it in a variable.
            var firstInputElement = $("input:not(:hidden, :disabled):first");
            // Focus on the first input field in the form.
            common.autofocus(firstInputElement);
        } else { // If input field with errors is present.
            // Find the input field having errors and stores it in a variable.
            var inputElementWithError = $('.help-inline:not(:empty):first').parent().find('input');
            // Focus on the input field having errors.
            common.autofocus(inputElementWithError);
        }

        // reset error message displays
        $('#id_team_subdomain_error_client').css('display', 'none');
        if ($('.team_subdomain_error_server').text() === '') {
            $('.team_subdomain_error_server').css('display', 'none');
        }

        $("#timezone").val(moment.tz.guess());
    }

    password_field.on('change keyup', function () {
        // Update the password strength bar even if we aren't validating
        // the field yet.
        common.password_quality($(this).val(), $('#pw_strength .bar'), $(this));
    });

    $("#send_confirm").validate({
        errorElement: "div",
        errorPlacement: function (error) {
            $('.email-frontend-error').empty();
            $("#send_confirm .alert.email-backend-error").remove();
            error.appendTo(".email-frontend-error").addClass("text-error");
        },
        success: function () {
            $('#errors').empty();
        },
    });

    $(".register-page #email, .login-page-container #id_username").on('focusout keydown', function (e) {
        // check if it is the "focusout" or if it is a keydown, then check if
        // the keycode was the one for "enter" (13).
        if (e.type === "focusout" || e.which === 13) {
            $(this).val($.trim($(this).val()));
        }
    });

    var show_subdomain_section = function (bool) {
        var action = bool ? "hide" : "show";
        $("#subdomain_section")[action]();
    };

    $("#realm_in_root_domain").change(function () {
        show_subdomain_section($(this).is(":checked"));
    });

    $("#login_form").validate({
        errorClass: "text-error",
        wrapper: "div",
        submitHandler: function (form) {
            $("#login_form").find('.loader').css('display', 'inline-block');
            $("#login_form").find("button .text").hide();

            form.submit();
        },
        invalidHandler: function () {
            // this removes all previous errors that were put on screen
            // by the server.
            $("#login_form .alert.alert-error").remove();
        },
    });

    function check_subdomain_avilable(subdomain) {
        var url = "/json/realm/subdomain/" + subdomain;
        $.get(url, function (response) {
            if (response.msg !== "available") {
                $("#id_team_subdomain_error_client").html(response.msg);
                $("#id_team_subdomain_error_client").show();
            }
        });
    }

    function update_full_name_section() {
        var selected_realm = $("#source_realm_select").find(":selected").val();
        if (selected_realm && selected_realm !== "on") {
            $("#full_name_input_section").hide();
            $("#profile_info_section").show();
            var avatar_url = $("#source_realm_select").find(":selected").attr('data-avatar');
            var full_name = $("#source_realm_select").find(":selected").attr('data-full-name');
            $("#profile_full_name").text(full_name);
            $("#id_full_name").val(full_name);
            $("#profile_avatar").attr("src", avatar_url);
        } else {
            $("#full_name_input_section").show();
            $("#profile_info_section").hide();
            $("#id_full_name").val("");
        }
    }

    $("#source_realm_select").change(update_full_name_section);
    update_full_name_section();

    var timer;
    $('#id_team_subdomain').on("keydown", function () {
        $('.team_subdomain_error_server').text('').css('display', 'none');
        $("#id_team_subdomain_error_client").css('display', 'none');
        clearTimeout(timer);
    });
    $('#id_team_subdomain').on("keyup", function () {
        clearTimeout(timer);
        timer = setTimeout(check_subdomain_avilable, 250, $('#id_team_subdomain').val());
    });
});
<|endoftext|>


<commit_before>var deps = {
	Core: {
		src: ['Leaflet.js',
		      'core/Util.js',
		      'core/Class.js',
		      'core/Events.js',
		      'core/Browser.js',
		      'geometry/Point.js',
		      'geometry/Bounds.js',
		      'geometry/Transformation.js',
		      'dom/DomUtil.js',
		      'geo/LatLng.js',
		      'geo/LatLngBounds.js',
		      'geo/projection/Projection.js',
		      'geo/projection/Projection.SphericalMercator.js',
		      'geo/projection/Projection.LonLat.js',
		      'geo/crs/CRS.js',
		      'geo/crs/CRS.Simple.js',
		      'geo/crs/CRS.EPSG3857.js',
		      'geo/crs/CRS.EPSG4326.js',
		      'map/Map.js'],
		desc: 'The core of the library, including OOP, events, DOM facilities, basic units, projections (EPSG:3857 and EPSG:4326) and the base Map class.'
	},

	EPSG3395: {
		src: ['geo/projection/Projection.Mercator.js',
		      'geo/crs/CRS.EPSG3395.js'],
		desc: 'EPSG:3395 projection (used by some map providers).',
		heading: 'Additional projections'
	},

	TileLayer: {
		src: ['layer/tile/TileLayer.js'],
		desc: 'The base class for displaying tile layers on the map.',
		heading: 'Layers'
	},

	TileLayerWMS: {
		src: ['layer/tile/TileLayer.WMS.js'],
		desc: 'WMS tile layer.',
		deps: ['TileLayer']
	},

	TileLayerCanvas: {
		src: ['layer/tile/TileLayer.Canvas.js'],
		desc: 'Tile layer made from canvases (for custom drawing purposes).',
		deps: ['TileLayer']
	},

	ImageOverlay: {
		src: ['layer/ImageOverlay.js'],
		desc: 'Used to display an image over a particular rectangular area of the map.'
	},

	Marker: {
		src: ['layer/marker/Icon.js', 'layer/marker/Icon.Default.js', 'layer/marker/Marker.js'],
		desc: 'Markers to put on the map.'
	},

	DivIcon: {
		src: ['layer/marker/DivIcon.js'],
		deps: ['Marker'],
		desc: 'Lightweight div-based icon for markers.'
	},

	Popup: {
		src: ['layer/Popup.js', 'layer/marker/Marker.Popup.js', 'map/ext/Map.Popup.js'],
		deps: ['Marker'],
		desc: 'Used to display the map popup (used mostly for binding HTML data to markers and paths on click).'
	},

	LayerGroup: {
		src: ['layer/LayerGroup.js'],
		desc: 'Allows grouping several layers to handle them as one.'
	},

	FeatureGroup: {
		src: ['layer/FeatureGroup.js'],
		deps: ['LayerGroup', 'Popup'],
		desc: 'Extends LayerGroup with mouse events and bindPopup method shared between layers.'
	},

	Path: {
		src: ['layer/vector/Path.js', 'layer/vector/Path.SVG.js', 'layer/vector/Path.Popup.js'],
		desc: 'Vector rendering core (SVG-powered), enables overlaying the map with SVG paths.',
		heading: 'Vector layers'
	},

	PathVML: {
		src: ['layer/vector/Path.VML.js'],
		desc: 'VML fallback for vector rendering core (IE 6-8).'
	},

	PathCanvas: {
		src: ['layer/vector/canvas/Path.Canvas.js'],
		deps: ['Path', 'Polyline', 'Polygon', 'Circle'],
		desc: 'Canvas fallback for vector rendering core (makes it work on Android 2+).'
	},

	Polyline: {
		src: ['geometry/LineUtil.js', 'layer/vector/Polyline.js'],
		deps: ['Path'],
		desc: 'Polyline overlays.'
	},

	Polygon: {
		src: ['geometry/PolyUtil.js', 'layer/vector/Polygon.js'],
		deps: ['Polyline'],
		desc: 'Polygon overlays.'
	},

	MultiPoly: {
		src: ['layer/vector/MultiPoly.js'],
		deps: ['FeatureGroup', 'Polyline', 'Polygon'],
		desc: 'MultiPolygon and MultyPolyline layers.'
	},

	Rectangle: {
		src: ['layer/vector/Rectangle.js'],
		deps: ['Polygon'],
		desc: ['Rectangle overlays.']
	},

	Circle: {
		src: ['layer/vector/Circle.js'],
		deps: ['Path'],
		desc: 'Circle overlays (with radius in meters).'
	},

	CircleMarker: {
		src: ['layer/vector/CircleMarker.js'],
		deps: ['Circle'],
		desc: 'Circle overlays with a constant pixel radius.'
	},

	VectorsCanvas: {
		src: ['layer/vector/canvas/Polyline.Canvas.js',
		      'layer/vector/canvas/Polygon.Canvas.js',
		      'layer/vector/canvas/Circle.Canvas.js'],
		deps: ['PathCanvas', 'Polyline', 'Polygon', 'Circle'],
		desc: 'Canvas fallback for vector layers (polygons, polylines, circles)'
	},

	GeoJSON: {
		src: ['layer/GeoJSON.js'],
		deps: ['Marker', 'MultiPoly', 'FeatureGroup'],
		desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
	},

	MapDrag: {
		src: ['dom/DomEvent.js',
		      'dom/Draggable.js',
		      'core/Handler.js',
		      'map/handler/Map.Drag.js'],
		desc: 'Makes the map draggable (by mouse or touch).',
		heading: 'Interaction'
	},

	MouseZoom: {
		src: ['dom/DomEvent.js',
		      'core/Handler.js',
		      'map/handler/Map.DoubleClickZoom.js',
		      'map/handler/Map.ScrollWheelZoom.js'],
		desc: 'Scroll wheel zoom and double click zoom on the map.'
	},

	TouchZoom: {
		src: ['dom/DomEvent.js',
		      'dom/DomEvent.DoubleTap.js',
		      'dom/DomEvent.MsTouch.js',
		      'core/Handler.js',
		      'map/handler/Map.TouchZoom.js'],
		deps: ['MapAnimationZoom'],
		desc: 'Enables smooth touch zooming on iOS and IE10 and double tap on iOS/IE10/Android.'
	},

	BoxZoom: {
		src: ['map/handler/Map.BoxZoom.js'],
		desc: 'Enables zooming to bounding box by shift-dragging the map.'
	},

	Keyboard: {
		src: ['map/handler/Map.Keyboard.js'],
		desc: 'Enables keyboard pan/zoom when the map is focused.'
	},

	MarkerDrag: {
		src: ['layer/marker/Marker.Drag.js'],
		deps: ['Marker'],
		desc: 'Makes markers draggable (by mouse or touch).'
	},

	PolyEdit: {
		src: ['layer/vector/Polyline.Edit.js'],
		deps: ['Polyline', 'DivIcon'],
		desc: 'Polyline and polygon editing.'
	},

	ControlZoom: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Zoom.js'],
		heading: 'Controls',
		desc: 'Basic zoom control with two buttons (zoom in / zoom out).'
	},

	ControlAttrib: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Attribution.js'],
		desc: 'Attribution control.'
	},

	ControlScale: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Scale.js'],
		desc: 'Scale control.'
	},

	ControlLayers: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Layers.js'],
		desc: 'Layer Switcher control.'
	},

	AnimationPan: {
		src: [
			'dom/DomEvent.js',
			'dom/PosAnimation.js',
			'map/anim/Map.PanAnimation.js'
			],
		deps: ['AnimationPan'],
		desc: 'Core panning animation support.'
	},

	AnimationTimer: {
		src: ['dom/PosAnimation.Timer.js'],
		deps: ['AnimationPan'],
		desc: 'Timer-based pan animation fallback for browsers that don\'t support CSS3 transitions.'
	},

	AnimationZoom: {
		src: ['map/anim/Map.ZoomAnimation.js'],
		deps: ['AnimationPan'],
		desc: 'Smooth zooming animation. Works only on browsers that support CSS3 Transitions.'
	},

	Geolocation: {
		src: ['map/ext/Map.Geolocation.js'],
		desc: 'Adds Map#locate method and related events to make geolocation easier.',
		heading: 'Misc'
	}
};

if (typeof exports !== 'undefined') {
	exports.deps = deps;
}
<commit_msg>better dependencies formatting<commit_after>var deps = {
	Core: {
		src: ['Leaflet.js',
		      'core/Util.js',
		      'core/Class.js',
		      'core/Events.js',
		      'core/Browser.js',
		      'geometry/Point.js',
		      'geometry/Bounds.js',
		      'geometry/Transformation.js',
		      'dom/DomUtil.js',
		      'geo/LatLng.js',
		      'geo/LatLngBounds.js',
		      'geo/projection/Projection.js',
		      'geo/projection/Projection.SphericalMercator.js',
		      'geo/projection/Projection.LonLat.js',
		      'geo/crs/CRS.js',
		      'geo/crs/CRS.Simple.js',
		      'geo/crs/CRS.EPSG3857.js',
		      'geo/crs/CRS.EPSG4326.js',
		      'map/Map.js'],
		desc: 'The core of the library, including OOP, events, DOM facilities, basic units, projections (EPSG:3857 and EPSG:4326) and the base Map class.'
	},

	EPSG3395: {
		src: ['geo/projection/Projection.Mercator.js',
		      'geo/crs/CRS.EPSG3395.js'],
		desc: 'EPSG:3395 projection (used by some map providers).',
		heading: 'Additional projections'
	},

	TileLayer: {
		src: ['layer/tile/TileLayer.js'],
		desc: 'The base class for displaying tile layers on the map.',
		heading: 'Layers'
	},

	TileLayerWMS: {
		src: ['layer/tile/TileLayer.WMS.js'],
		desc: 'WMS tile layer.',
		deps: ['TileLayer']
	},

	TileLayerCanvas: {
		src: ['layer/tile/TileLayer.Canvas.js'],
		desc: 'Tile layer made from canvases (for custom drawing purposes).',
		deps: ['TileLayer']
	},

	ImageOverlay: {
		src: ['layer/ImageOverlay.js'],
		desc: 'Used to display an image over a particular rectangular area of the map.'
	},

	Marker: {
		src: ['layer/marker/Icon.js',
		      'layer/marker/Icon.Default.js',
		      'layer/marker/Marker.js'],
		desc: 'Markers to put on the map.'
	},

	DivIcon: {
		src: ['layer/marker/DivIcon.js'],
		deps: ['Marker'],
		desc: 'Lightweight div-based icon for markers.'
	},

	Popup: {
		src: ['layer/Popup.js',
		      'layer/marker/Marker.Popup.js',
		      'map/ext/Map.Popup.js'],
		deps: ['Marker'],
		desc: 'Used to display the map popup (used mostly for binding HTML data to markers and paths on click).'
	},

	LayerGroup: {
		src: ['layer/LayerGroup.js'],
		desc: 'Allows grouping several layers to handle them as one.'
	},

	FeatureGroup: {
		src: ['layer/FeatureGroup.js'],
		deps: ['LayerGroup', 'Popup'],
		desc: 'Extends LayerGroup with mouse events and bindPopup method shared between layers.'
	},

	Path: {
		src: ['layer/vector/Path.js',
		      'layer/vector/Path.SVG.js',
		      'layer/vector/Path.Popup.js'],
		desc: 'Vector rendering core (SVG-powered), enables overlaying the map with SVG paths.',
		heading: 'Vector layers'
	},

	PathVML: {
		src: ['layer/vector/Path.VML.js'],
		desc: 'VML fallback for vector rendering core (IE 6-8).'
	},

	PathCanvas: {
		src: ['layer/vector/canvas/Path.Canvas.js'],
		deps: ['Path', 'Polyline', 'Polygon', 'Circle'],
		desc: 'Canvas fallback for vector rendering core (makes it work on Android 2+).'
	},

	Polyline: {
		src: ['geometry/LineUtil.js',
		      'layer/vector/Polyline.js'],
		deps: ['Path'],
		desc: 'Polyline overlays.'
	},

	Polygon: {
		src: ['geometry/PolyUtil.js',
		      'layer/vector/Polygon.js'],
		deps: ['Polyline'],
		desc: 'Polygon overlays.'
	},

	MultiPoly: {
		src: ['layer/vector/MultiPoly.js'],
		deps: ['FeatureGroup', 'Polyline', 'Polygon'],
		desc: 'MultiPolygon and MultyPolyline layers.'
	},

	Rectangle: {
		src: ['layer/vector/Rectangle.js'],
		deps: ['Polygon'],
		desc: ['Rectangle overlays.']
	},

	Circle: {
		src: ['layer/vector/Circle.js'],
		deps: ['Path'],
		desc: 'Circle overlays (with radius in meters).'
	},

	CircleMarker: {
		src: ['layer/vector/CircleMarker.js'],
		deps: ['Circle'],
		desc: 'Circle overlays with a constant pixel radius.'
	},

	VectorsCanvas: {
		src: ['layer/vector/canvas/Polyline.Canvas.js',
		      'layer/vector/canvas/Polygon.Canvas.js',
		      'layer/vector/canvas/Circle.Canvas.js'],
		deps: ['PathCanvas', 'Polyline', 'Polygon', 'Circle'],
		desc: 'Canvas fallback for vector layers (polygons, polylines, circles)'
	},

	GeoJSON: {
		src: ['layer/GeoJSON.js'],
		deps: ['Marker', 'MultiPoly', 'FeatureGroup'],
		desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
	},

	MapDrag: {
		src: ['dom/DomEvent.js',
		      'dom/Draggable.js',
		      'core/Handler.js',
		      'map/handler/Map.Drag.js'],
		desc: 'Makes the map draggable (by mouse or touch).',
		heading: 'Interaction'
	},

	MouseZoom: {
		src: ['dom/DomEvent.js',
		      'core/Handler.js',
		      'map/handler/Map.DoubleClickZoom.js',
		      'map/handler/Map.ScrollWheelZoom.js'],
		desc: 'Scroll wheel zoom and double click zoom on the map.'
	},

	TouchZoom: {
		src: ['dom/DomEvent.js',
		      'dom/DomEvent.DoubleTap.js',
		      'dom/DomEvent.MsTouch.js',
		      'core/Handler.js',
		      'map/handler/Map.TouchZoom.js'],
		deps: ['MapAnimationZoom'],
		desc: 'Enables smooth touch zooming on iOS and IE10 and double tap on iOS/IE10/Android.'
	},

	BoxZoom: {
		src: ['map/handler/Map.BoxZoom.js'],
		desc: 'Enables zooming to bounding box by shift-dragging the map.'
	},

	Keyboard: {
		src: ['map/handler/Map.Keyboard.js'],
		desc: 'Enables keyboard pan/zoom when the map is focused.'
	},

	MarkerDrag: {
		src: ['layer/marker/Marker.Drag.js'],
		deps: ['Marker'],
		desc: 'Makes markers draggable (by mouse or touch).'
	},

	PolyEdit: {
		src: ['layer/vector/Polyline.Edit.js'],
		deps: ['Polyline', 'DivIcon'],
		desc: 'Polyline and polygon editing.'
	},

	ControlZoom: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Zoom.js'],
		heading: 'Controls',
		desc: 'Basic zoom control with two buttons (zoom in / zoom out).'
	},

	ControlAttrib: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Attribution.js'],
		desc: 'Attribution control.'
	},

	ControlScale: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Scale.js'],
		desc: 'Scale control.'
	},

	ControlLayers: {
		src: ['control/Control.js',
		      'map/ext/Map.Control.js',
		      'control/Control.Layers.js'],
		desc: 'Layer Switcher control.'
	},

	AnimationPan: {
		src: [
			'dom/DomEvent.js',
			'dom/PosAnimation.js',
			'map/anim/Map.PanAnimation.js'
			],
		deps: ['AnimationPan'],
		desc: 'Core panning animation support.'
	},

	AnimationTimer: {
		src: ['dom/PosAnimation.Timer.js'],
		deps: ['AnimationPan'],
		desc: 'Timer-based pan animation fallback for browsers that don\'t support CSS3 transitions.'
	},

	AnimationZoom: {
		src: ['map/anim/Map.ZoomAnimation.js'],
		deps: ['AnimationPan'],
		desc: 'Smooth zooming animation. Works only on browsers that support CSS3 Transitions.'
	},

	Geolocation: {
		src: ['map/ext/Map.Geolocation.js'],
		desc: 'Adds Map#locate method and related events to make geolocation easier.',
		heading: 'Misc'
	}
};

if (typeof exports !== 'undefined') {
	exports.deps = deps;
}
<|endoftext|>


<commit_before>var gulp    = require("gulp");
var notify  = require("gulp-notify");
var run     = require("gulp-run");
var phpunit = require("gulp-phpunit");

gulp.task("tests", function ()
{
	gulp.src("./tests/CLImateTest.php")
    .pipe( run("clear") )
		.pipe( phpunit("phpunit"), {
			debug: true,
			notify: true
		})
    .on("error", function() {
    	run("notify-send 'Tests Failed' 'Got some problems dude.'").exec();
    })
		.pipe( run("notify-send 'Tests Passed' 'Nailed it.'"));
});

gulp.task("watch", function()
{
	gulp.watch([ "./src/JoeTannenbaum/CLImate/**/*.php", "./tests/**/*"], [ "tests" ]);
})

gulp.task("default", [ "tests", "watch" ]);<commit_msg>fixed gulp filepath<commit_after>var gulp    = require("gulp");
var notify  = require("gulp-notify");
var run     = require("gulp-run");
var phpunit = require("gulp-phpunit");

gulp.task("tests", function ()
{
	gulp.src("./tests/CLImateTest.php")
    .pipe( run("clear") )
		.pipe( phpunit("phpunit"), {
			debug: true,
			notify: true
		})
    .on("error", function() {
    	run("notify-send 'Tests Failed' 'Got some problems dude.'").exec();
    })
		.pipe( run("notify-send 'Tests Passed' 'Nailed it.'"));
});

gulp.task("watch", function()
{
	gulp.watch([ "./src/**/*.php", "./tests/**/*"], [ "tests" ]);
})

gulp.task("default", [ "tests", "watch" ]);<|endoftext|>