<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: For creating grouped collapsible content areas.
//>>label: Collapsible Sets (Accordions)
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.collapsible.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [
	"jquery",
	"../jquery.mobile.widget",
	"./collapsible",
	"./addFirstLastClasses" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

var childCollapsiblesSelector = ":mobile-collapsible, " + $.mobile.collapsible.initSelector;

$.widget( "mobile.collapsibleset", $.extend( {

	// The initSelector is deprecated as of 1.4.0. In 1.5.0 we will use
	// :jqmData(role='collapsibleset') which will allow us to get rid of the line
	// below altogether, because the autoinit will generate such an initSelector
	initSelector: ":jqmData(role='collapsible-set'),:jqmData(role='collapsibleset')",

	options: $.extend( {
		enhanced: false,
	}, $.mobile.collapsible.defaults ),

	_handleCollapsibleExpand: function( event ) {
		var closestCollapsible = $( event.target ).closest( ".ui-collapsible" );

		if ( closestCollapsible.parent().is( ":mobile-collapsibleset, :jqmData(role='collapsible-set')" ) ) {
			closestCollapsible
				.siblings( ".ui-collapsible:not(.ui-collapsible-collapsed)" )
				.collapsible( "collapse" );
		}
	},

	_create: function() {
		var elem = this.element,
			opts = this.options;

		$.extend( this, {
			_classes: ""
		});

		if ( !opts.enhanced ) {
			elem.addClass( "ui-collapsible-set " +
				this._themeClassFromOption( "ui-group-theme-", opts.theme ) + " " +
				( opts.corners && opts.inset ? "ui-corner-all " : "" ) );
			this.element.find( $.mobile.collapsible.initSelector ).collapsible();
		}

		this._on( elem, { collapsibleexpand: "_handleCollapsibleExpand" } );
	},

	_themeClassFromOption: function( prefix, value ) {
		return ( value ? ( value === "none" ? "" : prefix + value ) : "" );
	},

	_init: function() {
		this._refresh( true );

		// Because the corners are handled by the collapsible itself and the default state is collapsed
		// That was causing https://github.com/jquery/jquery-mobile/issues/4116
		this.element
			.children( childCollapsiblesSelector )
			.filter( ":jqmData(collapsed='false')" )
			.collapsible( "expand" );
	},

	_setOptions: function( options ) {
		var ret,
			elem = this.element,
			themeClass = this._themeClassFromOption( "ui-group-theme-", options.theme );

		if ( themeClass ) {
			elem
				.removeClass( this._themeClassFromOption( "ui-group-theme-", this.options.theme ) )
				.addClass( themeClass );
		}

		if ( options.corners !== undefined ) {
			elem.toggleClass( "ui-corner-all", options.corners );
		}

		ret = this._super( options );
		this.element.children( ":mobile-collapsible" ).collapsible( "refresh" );
		return ret;
	},

	_destroy: function() {
		var el = this.element;

		this._removeFirstLastClasses( el.children( childCollapsiblesSelector ) );
		el
			.removeClass( "ui-collapsible-set ui-corner-all " +
				this._themeClassFromOption( "ui-group-theme", this.options.theme ) )
			.children( ":mobile-collapsible" )
			.collapsible( "destroy" );
	},

	_refresh: function( create ) {
		var collapsiblesInSet = this.element.children( childCollapsiblesSelector );

		$.mobile.collapsible.prototype.enhance( collapsiblesInSet.not( ".ui-collapsible" ) );

		this._addFirstLastClasses( collapsiblesInSet, this._getVisibles( collapsiblesInSet, create ), create );
	},

	refresh: function() {
		this._refresh( false );
	}
}, $.mobile.behaviors.addFirstLastClasses ) );

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>Collapsible set: Depend on page so you get the $.widget shim.<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: For creating grouped collapsible content areas.
//>>label: Collapsible Sets (Accordions)
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.collapsible.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [
	"jquery",

	// Deprecated as of 1.4.0 and will be removed in 1.5.0
	// We only need this dependency so we get the $.widget shim from page, so we
	// can use $.mobile.collapsible.initSelector, which is added by the shim.
	// As of 1.5.0 we will assume that all children of the collapsibleset are to
	// be turned into collapsibles.
	"./page",
	"../jquery.mobile.widget",
	"./collapsible",
	"./addFirstLastClasses" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

var childCollapsiblesSelector = ":mobile-collapsible, " + $.mobile.collapsible.initSelector;

$.widget( "mobile.collapsibleset", $.extend( {

	// The initSelector is deprecated as of 1.4.0. In 1.5.0 we will use
	// :jqmData(role='collapsibleset') which will allow us to get rid of the line
	// below altogether, because the autoinit will generate such an initSelector
	initSelector: ":jqmData(role='collapsible-set'),:jqmData(role='collapsibleset')",

	options: $.extend( {
		enhanced: false,
	}, $.mobile.collapsible.defaults ),

	_handleCollapsibleExpand: function( event ) {
		var closestCollapsible = $( event.target ).closest( ".ui-collapsible" );

		if ( closestCollapsible.parent().is( ":mobile-collapsibleset, :jqmData(role='collapsible-set')" ) ) {
			closestCollapsible
				.siblings( ".ui-collapsible:not(.ui-collapsible-collapsed)" )
				.collapsible( "collapse" );
		}
	},

	_create: function() {
		var elem = this.element,
			opts = this.options;

		$.extend( this, {
			_classes: ""
		});

		if ( !opts.enhanced ) {
			elem.addClass( "ui-collapsible-set " +
				this._themeClassFromOption( "ui-group-theme-", opts.theme ) + " " +
				( opts.corners && opts.inset ? "ui-corner-all " : "" ) );
			this.element.find( $.mobile.collapsible.initSelector ).collapsible();
		}

		this._on( elem, { collapsibleexpand: "_handleCollapsibleExpand" } );
	},

	_themeClassFromOption: function( prefix, value ) {
		return ( value ? ( value === "none" ? "" : prefix + value ) : "" );
	},

	_init: function() {
		this._refresh( true );

		// Because the corners are handled by the collapsible itself and the default state is collapsed
		// That was causing https://github.com/jquery/jquery-mobile/issues/4116
		this.element
			.children( childCollapsiblesSelector )
			.filter( ":jqmData(collapsed='false')" )
			.collapsible( "expand" );
	},

	_setOptions: function( options ) {
		var ret,
			elem = this.element,
			themeClass = this._themeClassFromOption( "ui-group-theme-", options.theme );

		if ( themeClass ) {
			elem
				.removeClass( this._themeClassFromOption( "ui-group-theme-", this.options.theme ) )
				.addClass( themeClass );
		}

		if ( options.corners !== undefined ) {
			elem.toggleClass( "ui-corner-all", options.corners );
		}

		ret = this._super( options );
		this.element.children( ":mobile-collapsible" ).collapsible( "refresh" );
		return ret;
	},

	_destroy: function() {
		var el = this.element;

		this._removeFirstLastClasses( el.children( childCollapsiblesSelector ) );
		el
			.removeClass( "ui-collapsible-set ui-corner-all " +
				this._themeClassFromOption( "ui-group-theme", this.options.theme ) )
			.children( ":mobile-collapsible" )
			.collapsible( "destroy" );
	},

	_refresh: function( create ) {
		var collapsiblesInSet = this.element.children( childCollapsiblesSelector );

		$.mobile.collapsible.prototype.enhance( collapsiblesInSet.not( ".ui-collapsible" ) );

		this._addFirstLastClasses( collapsiblesInSet, this._getVisibles( collapsiblesInSet, create ), create );
	},

	refresh: function() {
		this._refresh( false );
	}
}, $.mobile.behaviors.addFirstLastClasses ) );

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>iD.Map = function(context) {
    var dimensions = [1, 1],
        dispatch = d3.dispatch('move', 'drawn'),
        projection = d3.geo.mercator().scale(512 / Math.PI),
        roundedProjection = iD.svg.RoundProjection(projection),
        zoom = d3.behavior.zoom()
            .translate(projection.translate())
            .scale(projection.scale() * 2 * Math.PI)
            .scaleExtent([1024, 256 * Math.pow(2, 24)])
            .on('zoom', zoomPan),
        dblclickEnabled = true,
        transformStart,
        minzoom = 0,
        layers = [
            iD.Background().projection(projection),
            iD.LocalGpx(context).projection(projection),
            iD.Background('overlay').projection(projection)
            ],
        transformProp = iD.util.prefixCSSProperty('Transform'),
        points = iD.svg.Points(roundedProjection, context),
        vertices = iD.svg.Vertices(roundedProjection, context),
        lines = iD.svg.Lines(projection),
        areas = iD.svg.Areas(roundedProjection),
        midpoints = iD.svg.Midpoints(roundedProjection, context),
        labels = iD.svg.Labels(roundedProjection, context),
        tail = iD.ui.Tail(),
        surface, layergroup;

    function map(selection) {
        context.history()
            .on('change.map', redraw);

        context.on('select.map', function() {
            redraw();
        });

        selection.call(zoom);

        layergroup = selection.append('div')
            .attr('id', 'layer-g');

        var supersurface = selection.append('div')
            .style('position', 'absolute');

        surface = supersurface.append('svg')
            .on('mousedown.zoom', function() {
                if (d3.event.button == 2) {
                    d3.event.stopPropagation();
                }
            }, true)
            .on('mouseup.zoom', function() {
                if (resetTransform()) redraw();
            })
            .attr('id', 'surface')
            .call(iD.svg.Surface(context));

        surface.on('mouseover.vertices', function() {
            vertices.hover(d3.event.target.__data__);
            surface.call(vertices, context.graph(), map.zoom());
        });

        surface.on('mouseout.vertices', function() {
            vertices.hover(d3.event.relatedTarget && d3.event.relatedTarget.__data__);
            surface.call(vertices, context.graph(), map.zoom());
        });

        map.size(selection.size());
        map.surface = surface;
        map.layersurface = layergroup;

        labels.supersurface(supersurface);

        supersurface
            .call(tail);
    }

    function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }

    function drawVector(difference) {
        var filter, all,
            extent = map.extent(),
            graph = context.graph();

        if (!difference) {
            all = context.intersects(extent);
            filter = d3.functor(true);
        } else {
            var complete = difference.complete(extent);
            all = _.compact(_.values(complete));
            filter = function(d) {
                if (d.type === 'midpoint') {

                    var a = d.edge[0],
                        b = d.edge[1];

                    // redraw a midpoint if it needs to be
                    // - moved (either edge node moved)
                    // - deleted (edge nodes not consecutive in any parent way)
                    if (a in complete || b in complete) return true;

                    var parentsWays = graph.parentWays({ id: a });
                    for (var i = 0; i < parentsWays.length; i++) {
                        var nodes = parentsWays[i].nodes;
                        for (var n = 0; n < nodes.length; n++) {
                            if (nodes[n] === a && (nodes[n - 1] === b || nodes[n + 1] === b)) return false;
                        }
                    }
                    return true;

                } else {
                    return d.id in complete;
                }
            };
        }

        if (all.length > 100000) {
            editOff();
        } else {
            surface
                .call(points, graph, all, filter)
                .call(vertices, graph, map.zoom())
                .call(lines, graph, all, filter, dimensions)
                .call(areas, graph, all, filter)
                .call(midpoints, graph, all, filter, extent)
                .call(labels, graph, all, filter, dimensions, !difference);
        }
        dispatch.drawn(map);
    }

    function editOff() {
        surface.selectAll('.layer *').remove();
    }

    function zoomPan() {
        if (d3.event && d3.event.sourceEvent.type === 'dblclick') {
            if (!dblclickEnabled) {
                zoom.scale(projection.scale() * 2 * Math.PI)
                    .translate(projection.translate());
                return d3.event.sourceEvent.preventDefault();
            }
        }

        if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
            iD.ui.flash(context.container())
                .select('.content')
                .text(t('cannot_zoom'));
            return setZoom(16, true);
        }

        projection
            .translate(d3.event.translate)
            .scale(d3.event.scale / (2 * Math.PI));

        var ascale = d3.event.scale;
        var bscale = transformStart[0];
        var scale = (ascale / bscale);

        var tX = Math.round((d3.event.translate[0] / scale) - (transformStart[1][0]));
        var tY = Math.round((d3.event.translate[1] / scale) - (transformStart[1][1]));

        var transform =
            'scale(' + scale + ')' +
            'translate(' + tX + 'px,' + tY + 'px) ';

        layergroup.style(transformProp, transform);
        surface.style(transformProp, transform);
        queueRedraw();

        dispatch.move(map);
    }

    function resetTransform() {
        var prop = surface.node().style[transformProp];
        if (!prop || prop === 'none') return false;
        surface.node().style[transformProp] = '';
        layergroup.node().style[transformProp] = '';
        return true;
    }

    function redraw(difference) {

        if (!surface) return;

        clearTimeout(timeoutId);

        // If we are in the middle of a zoom/pan, we can't do differenced redraws.
        // It would result in artifacts where differenced entities are redrawn with
        // one transform and unchanged entities with another.
        if (resetTransform()) {
            difference = undefined;
        }

        var zoom = String(~~map.zoom());
        if (surface.attr('data-zoom') !== zoom) {
            surface.attr('data-zoom', zoom);
        }

        if (!difference) {
            var sel = layergroup
                .selectAll('.layer-layer')
                .data(layers);

            sel.exit().remove();

            sel.enter().append('div')
                .attr('class', 'layer-layer');

            sel.each(function(layer) {
                    d3.select(this).call(layer);
                });
        }

        if (map.editable()) {
            context.connection().loadTiles(projection, dimensions);
            drawVector(difference);
        } else {
            editOff();
        }

        transformStart = [
            projection.scale() * 2 * Math.PI,
            projection.translate().slice()];

        return map;
    }

    var timeoutId;
    function queueRedraw() {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() { redraw(); }, 300);
    }

    function pointLocation(p) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
    }

    function locationPoint(l) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
    }

    map.mouseCoordinates = function() {
        try {
            return projection.invert(d3.mouse(surface.node()));
        } catch(e) {
            // when called with hidden elements, d3.mouse() will throw
            return [NaN, NaN];
        }
    };

    map.dblclickEnable = function(_) {
        if (!arguments.length) return dblclickEnabled;
        dblclickEnabled = _;
        return map;
    };

    function setZoom(z, force) {
        if (z === map.zoom() && !force)
            return false;
        var scale = 256 * Math.pow(2, z),
            center = pxCenter(),
            l = pointLocation(center);
        scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
        projection.scale(scale / (2 * Math.PI));
        zoom.scale(scale);
        var t = projection.translate();
        l = locationPoint(l);
        t[0] += center[0] - l[0];
        t[1] += center[1] - l[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        return true;
    }

    function setCenter(loc) {
        var t = projection.translate(),
            c = pxCenter(),
            ll = projection(loc);
        if (ll[0] === c[0] && ll[1] === c[1])
            return false;
        projection.translate([
            t[0] - ll[0] + c[0],
            t[1] - ll[1] + c[1]]);
        zoom.translate(projection.translate());
        return true;
    }

    map.pan = function(d) {
        var t = projection.translate();
        t[0] += d[0];
        t[1] += d[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        dispatch.move(map);
        return redraw();
    };

    map.size = function(_) {
        if (!arguments.length) return dimensions;
        var center = map.center();
        dimensions = _;
        surface.size(dimensions);
        layers.map(function(l) {
            l.size(dimensions);
        });
        projection.clipExtent([[0, 0], dimensions]);
        setCenter(center);
        return redraw();
    };

    map.zoomIn = function() { return map.zoom(Math.ceil(map.zoom() + 1)); };
    map.zoomOut = function() { return map.zoom(Math.floor(map.zoom() - 1)); };

    map.center = function(loc) {
        if (!arguments.length) {
            return projection.invert(pxCenter());
        }

        if (setCenter(loc)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoom = function(z) {
        if (!arguments.length) {
            return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
        }

        if (setZoom(z)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoomTo = function(entity) {
        var extent = entity.extent(context.graph()),
            zoom = map.extentZoom(extent);
        map.centerZoom(extent.center(), zoom);
    };

    map.centerZoom = function(loc, z) {
        var centered = setCenter(loc),
            zoomed   = setZoom(z);

        if (centered || zoomed) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.centerEase = function(loc) {
        var from = map.center().slice(),
            t = 0,
            stop;

        surface.one('mousedown.ease', function() {
            stop = true;
        });

        d3.timer(function() {
            if (stop) return true;
            map.center(iD.geo.interp(from, loc, (t += 1) / 10));
            return t == 10;
        }, 20);
        return map;
    };

    map.extent = function(_) {
        if (!arguments.length) {
            return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
                                 projection.invert([dimensions[0], 0]));
        } else {
            var extent = iD.geo.Extent(_);
            map.centerZoom(extent.center(), map.extentZoom(extent));
        }
    };

    map.extentZoom = function(_) {
        var extent = iD.geo.Extent(_),
            tl = projection([extent[0][0], extent[1][1]]),
            br = projection([extent[1][0], extent[0][1]]);

        // Calculate maximum zoom that fits extent
        var hFactor = (br[0] - tl[0]) / dimensions[0],
            vFactor = (br[1] - tl[1]) / dimensions[1],
            hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
            vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
            newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);

        return newZoom;
    };

    map.flush = function() {
        context.connection().flush();
        context.history().reset();
        return map;
    };

    var usedTails = {};
    map.tail = function(_) {
        if (!_ || usedTails[_] === undefined) {
            tail.text(_);
            usedTails[_] = true;
        }
        return map;
    };

    map.editable = function() {
        return map.zoom() >= 16;
    };

    map.minzoom = function(_) {
        if (!arguments.length) return minzoom;
        minzoom = _;
        return map;
    };

    map.layers = layers;
    map.projection = projection;
    map.redraw = redraw;

    return d3.rebind(map, dispatch, 'on');
};
<commit_msg>Don't redraw vertices during pan/zoom transform<commit_after>iD.Map = function(context) {
    var dimensions = [1, 1],
        dispatch = d3.dispatch('move', 'drawn'),
        projection = d3.geo.mercator().scale(512 / Math.PI),
        roundedProjection = iD.svg.RoundProjection(projection),
        zoom = d3.behavior.zoom()
            .translate(projection.translate())
            .scale(projection.scale() * 2 * Math.PI)
            .scaleExtent([1024, 256 * Math.pow(2, 24)])
            .on('zoom', zoomPan),
        dblclickEnabled = true,
        transformStart,
        minzoom = 0,
        layers = [
            iD.Background().projection(projection),
            iD.LocalGpx(context).projection(projection),
            iD.Background('overlay').projection(projection)
            ],
        transformProp = iD.util.prefixCSSProperty('Transform'),
        points = iD.svg.Points(roundedProjection, context),
        vertices = iD.svg.Vertices(roundedProjection, context),
        lines = iD.svg.Lines(projection),
        areas = iD.svg.Areas(roundedProjection),
        midpoints = iD.svg.Midpoints(roundedProjection, context),
        labels = iD.svg.Labels(roundedProjection, context),
        tail = iD.ui.Tail(),
        surface, layergroup;

    function map(selection) {
        context.history()
            .on('change.map', redraw);

        context.on('select.map', function() {
            redraw();
        });

        selection.call(zoom);

        layergroup = selection.append('div')
            .attr('id', 'layer-g');

        var supersurface = selection.append('div')
            .style('position', 'absolute');

        surface = supersurface.append('svg')
            .on('mousedown.zoom', function() {
                if (d3.event.button == 2) {
                    d3.event.stopPropagation();
                }
            }, true)
            .on('mouseup.zoom', function() {
                if (resetTransform()) redraw();
            })
            .attr('id', 'surface')
            .call(iD.svg.Surface(context));

        surface.on('mouseover.vertices', function() {
            vertices.hover(d3.event.target.__data__);
            if (!isTransformed()) surface.call(vertices, context.graph(), map.zoom());
        });

        surface.on('mouseout.vertices', function() {
            vertices.hover(d3.event.relatedTarget && d3.event.relatedTarget.__data__);
            if (!isTransformed()) surface.call(vertices, context.graph(), map.zoom());
        });

        map.size(selection.size());
        map.surface = surface;
        map.layersurface = layergroup;

        labels.supersurface(supersurface);

        supersurface
            .call(tail);
    }

    function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }

    function drawVector(difference) {
        var filter, all,
            extent = map.extent(),
            graph = context.graph();

        if (!difference) {
            all = context.intersects(extent);
            filter = d3.functor(true);
        } else {
            var complete = difference.complete(extent);
            all = _.compact(_.values(complete));
            filter = function(d) {
                if (d.type === 'midpoint') {

                    var a = d.edge[0],
                        b = d.edge[1];

                    // redraw a midpoint if it needs to be
                    // - moved (either edge node moved)
                    // - deleted (edge nodes not consecutive in any parent way)
                    if (a in complete || b in complete) return true;

                    var parentsWays = graph.parentWays({ id: a });
                    for (var i = 0; i < parentsWays.length; i++) {
                        var nodes = parentsWays[i].nodes;
                        for (var n = 0; n < nodes.length; n++) {
                            if (nodes[n] === a && (nodes[n - 1] === b || nodes[n + 1] === b)) return false;
                        }
                    }
                    return true;

                } else {
                    return d.id in complete;
                }
            };
        }

        if (all.length > 100000) {
            editOff();
        } else {
            surface
                .call(points, graph, all, filter)
                .call(vertices, graph, map.zoom())
                .call(lines, graph, all, filter, dimensions)
                .call(areas, graph, all, filter)
                .call(midpoints, graph, all, filter, extent)
                .call(labels, graph, all, filter, dimensions, !difference);
        }
        dispatch.drawn(map);
    }

    function editOff() {
        surface.selectAll('.layer *').remove();
    }

    function zoomPan() {
        if (d3.event && d3.event.sourceEvent.type === 'dblclick') {
            if (!dblclickEnabled) {
                zoom.scale(projection.scale() * 2 * Math.PI)
                    .translate(projection.translate());
                return d3.event.sourceEvent.preventDefault();
            }
        }

        if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
            iD.ui.flash(context.container())
                .select('.content')
                .text(t('cannot_zoom'));
            return setZoom(16, true);
        }

        projection
            .translate(d3.event.translate)
            .scale(d3.event.scale / (2 * Math.PI));

        var ascale = d3.event.scale;
        var bscale = transformStart[0];
        var scale = (ascale / bscale);

        var tX = Math.round((d3.event.translate[0] / scale) - (transformStart[1][0]));
        var tY = Math.round((d3.event.translate[1] / scale) - (transformStart[1][1]));

        var transform =
            'scale(' + scale + ')' +
            'translate(' + tX + 'px,' + tY + 'px) ';

        layergroup.style(transformProp, transform);
        surface.style(transformProp, transform);
        queueRedraw();

        dispatch.move(map);
    }

    function isTransformed() {
        var prop = surface.node().style[transformProp];
        return prop && prop !== 'none';
    }

    function resetTransform() {
        if (!isTransformed()) return false;
        surface.node().style[transformProp] = '';
        layergroup.node().style[transformProp] = '';
        return true;
    }

    function redraw(difference) {

        if (!surface) return;

        clearTimeout(timeoutId);

        // If we are in the middle of a zoom/pan, we can't do differenced redraws.
        // It would result in artifacts where differenced entities are redrawn with
        // one transform and unchanged entities with another.
        if (resetTransform()) {
            difference = undefined;
        }

        var zoom = String(~~map.zoom());
        if (surface.attr('data-zoom') !== zoom) {
            surface.attr('data-zoom', zoom);
        }

        if (!difference) {
            var sel = layergroup
                .selectAll('.layer-layer')
                .data(layers);

            sel.exit().remove();

            sel.enter().append('div')
                .attr('class', 'layer-layer');

            sel.each(function(layer) {
                    d3.select(this).call(layer);
                });
        }

        if (map.editable()) {
            context.connection().loadTiles(projection, dimensions);
            drawVector(difference);
        } else {
            editOff();
        }

        transformStart = [
            projection.scale() * 2 * Math.PI,
            projection.translate().slice()];

        return map;
    }

    var timeoutId;
    function queueRedraw() {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() { redraw(); }, 300);
    }

    function pointLocation(p) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
    }

    function locationPoint(l) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
    }

    map.mouseCoordinates = function() {
        try {
            return projection.invert(d3.mouse(surface.node()));
        } catch(e) {
            // when called with hidden elements, d3.mouse() will throw
            return [NaN, NaN];
        }
    };

    map.dblclickEnable = function(_) {
        if (!arguments.length) return dblclickEnabled;
        dblclickEnabled = _;
        return map;
    };

    function setZoom(z, force) {
        if (z === map.zoom() && !force)
            return false;
        var scale = 256 * Math.pow(2, z),
            center = pxCenter(),
            l = pointLocation(center);
        scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
        projection.scale(scale / (2 * Math.PI));
        zoom.scale(scale);
        var t = projection.translate();
        l = locationPoint(l);
        t[0] += center[0] - l[0];
        t[1] += center[1] - l[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        return true;
    }

    function setCenter(loc) {
        var t = projection.translate(),
            c = pxCenter(),
            ll = projection(loc);
        if (ll[0] === c[0] && ll[1] === c[1])
            return false;
        projection.translate([
            t[0] - ll[0] + c[0],
            t[1] - ll[1] + c[1]]);
        zoom.translate(projection.translate());
        return true;
    }

    map.pan = function(d) {
        var t = projection.translate();
        t[0] += d[0];
        t[1] += d[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        dispatch.move(map);
        return redraw();
    };

    map.size = function(_) {
        if (!arguments.length) return dimensions;
        var center = map.center();
        dimensions = _;
        surface.size(dimensions);
        layers.map(function(l) {
            l.size(dimensions);
        });
        projection.clipExtent([[0, 0], dimensions]);
        setCenter(center);
        return redraw();
    };

    map.zoomIn = function() { return map.zoom(Math.ceil(map.zoom() + 1)); };
    map.zoomOut = function() { return map.zoom(Math.floor(map.zoom() - 1)); };

    map.center = function(loc) {
        if (!arguments.length) {
            return projection.invert(pxCenter());
        }

        if (setCenter(loc)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoom = function(z) {
        if (!arguments.length) {
            return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
        }

        if (setZoom(z)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoomTo = function(entity) {
        var extent = entity.extent(context.graph()),
            zoom = map.extentZoom(extent);
        map.centerZoom(extent.center(), zoom);
    };

    map.centerZoom = function(loc, z) {
        var centered = setCenter(loc),
            zoomed   = setZoom(z);

        if (centered || zoomed) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.centerEase = function(loc) {
        var from = map.center().slice(),
            t = 0,
            stop;

        surface.one('mousedown.ease', function() {
            stop = true;
        });

        d3.timer(function() {
            if (stop) return true;
            map.center(iD.geo.interp(from, loc, (t += 1) / 10));
            return t == 10;
        }, 20);
        return map;
    };

    map.extent = function(_) {
        if (!arguments.length) {
            return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
                                 projection.invert([dimensions[0], 0]));
        } else {
            var extent = iD.geo.Extent(_);
            map.centerZoom(extent.center(), map.extentZoom(extent));
        }
    };

    map.extentZoom = function(_) {
        var extent = iD.geo.Extent(_),
            tl = projection([extent[0][0], extent[1][1]]),
            br = projection([extent[1][0], extent[0][1]]);

        // Calculate maximum zoom that fits extent
        var hFactor = (br[0] - tl[0]) / dimensions[0],
            vFactor = (br[1] - tl[1]) / dimensions[1],
            hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
            vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
            newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);

        return newZoom;
    };

    map.flush = function() {
        context.connection().flush();
        context.history().reset();
        return map;
    };

    var usedTails = {};
    map.tail = function(_) {
        if (!_ || usedTails[_] === undefined) {
            tail.text(_);
            usedTails[_] = true;
        }
        return map;
    };

    map.editable = function() {
        return map.zoom() >= 16;
    };

    map.minzoom = function(_) {
        if (!arguments.length) return minzoom;
        minzoom = _;
        return map;
    };

    map.layers = layers;
    map.projection = projection;
    map.redraw = redraw;

    return d3.rebind(map, dispatch, 'on');
};
<|endoftext|>


<commit_before>var Pool = require('../lib/pool.js'),
//	loader = require('floader'), // browserify aware file loader (xhr in browser)
	weblas = require('weblas'),
	tape = require('tape');

weblas.test = require('weblas/lib/test');

var RTOL = 1e-05,
	ATOL = 1e-07;

tape("pool: 2 x 2 x 4", function(t){
	t.plan(1);

	var input = new Float32Array([	1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
								1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]),
		expected = new Float32Array([	1.0, 2.0, 1.0, 1.0 ]);

	var size = 2, stride = 2;
	var pool = new Pool(size, stride);
	var M = 2, N = 2, C = 4;

	try{
		// adapted from weblas.sdwns test
		//result = weblas.sdwns(2, 2, 4, 2, 2, X);
		var result = pool.forward(input, M, N, C).transfer();
	}
	catch(ex){
		t.error(ex);
		return;
	}

	weblas.test.assert.allclose(t, result, expected, null, RTOL, ATOL);

});

var dataDirectory = 'test/data/relu/',
	testFile = 'small.json';

var matrixFiles = ['in.arr', 'out.arr'];

function generateTestCase(prefix, M, N, C, size, stride){
	return function(t){

		var expected; // typed arrays

		// directory containing matrix data files for current test
		var testDirectory = dataDirectory + prefix + '/';

		// load matrices from files
		weblas.test.load(testDirectory, matrixFiles, function(err, matrices){

			if(err){
				t.skip("Unable to load files: " + err.message);
				t.end();

				return;
			}

			t.plan(1);

			// matrices is an array which matches matrixFiles
			var input = matrices[0],
				expected = matrices[1];

			var len = (Math.floor((M - size) / stride) + 1) *
						(Math.floor((N - size) / stride) + 1) * C;

			if(!(input && input.length && input.length == M * N * C &&
				expected && expected.length && expected.length == len)){

				throw new Error("malformed data");
			}

			var pool = new Pool(size, stride);
			//var M = 2, N = 2, C = 4;

			try{
				//var result = relu.forward(inputT).transfer();

				// adapted from weblas.sdwns test
				//result = weblas.sdwns(2, 2, 4, 2, 2, X);
				var result = pool.forward(input, M, N, C).transfer();
			}
			catch(ex){
				t.assert(false, ex);
				return;
			}

			weblas.test.assert.allclose(t, result, expected, null, RTOL, ATOL);
		});
	};
}

directory = "0001";

// in [1,4096]
// out [1,999]
var test = {
	M :
	N : ,
	C : ,
	size : ,
	stride :
}

var testName = "pool: " + test.M + " x " + test.N + " x " + test.C;

tape(testName, generateTestCase(directory, test.M, test.N, test.C, test.size, test.stride));
<commit_msg>finish test for Pool layer<commit_after>var Pool = require('../lib/pool.js'),
//	loader = require('floader'), // browserify aware file loader (xhr in browser)
	weblas = require('weblas'),
	tape = require('tape');

weblas.test = require('weblas/lib/test');

var RTOL = 1e-05,
	ATOL = 1e-07;

tape("pool: 2 x 2 x 4", function(t){
	t.plan(1);

	var input = new Float32Array([	1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
								1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]),
		expected = new Float32Array([	1.0, 2.0, 1.0, 1.0 ]);

	var size = 2, stride = 2;
	var pool = new Pool(size, stride);
	var M = 2, N = 2, C = 4;

	try{
		// adapted from weblas.sdwns test
		//result = weblas.sdwns(2, 2, 4, 2, 2, X);
		var result = pool.forward(input, M, N, C).transfer();
	}
	catch(ex){
		t.error(ex);
		return;
	}

	weblas.test.assert.allclose(t, result, expected, null, RTOL, ATOL);

});

var dataDirectory = 'test/data/pool/',
	testFile = 'small.json';

var matrixFiles = ['in.arr', 'out.arr'];

function generateTestCase(prefix, M, N, C, size, stride){
	return function(t){

		var expected; // typed arrays

		// directory containing matrix data files for current test
		var testDirectory = dataDirectory + prefix + '/';

		// load matrices from files
		weblas.test.load(testDirectory, matrixFiles, function(err, matrices){

			if(err){
				t.skip("Unable to load files: " + err.message);
				t.end();

				return;
			}

			t.plan(1);

			// matrices is an array which matches matrixFiles
			var input = matrices[0],
				expected = matrices[1];

			var len = (Math.floor((M - size) / stride) + 1) *
						(Math.floor((N - size) / stride) + 1) * C;

			if(!(input && input.length && input.length == M * N * C &&
				expected && expected.length && expected.length == len)){

				throw new Error("malformed data");
			}

			var pool = new Pool(size, stride);
			//var M = 2, N = 2, C = 4;

			try{
				//var result = relu.forward(inputT).transfer();

				// adapted from weblas.sdwns test
				//result = weblas.sdwns(2, 2, 4, 2, 2, X);
				var result = pool.forward(input, M, N, C).transfer();
			}
			catch(ex){
				t.assert(false, ex);
				return;
			}

			weblas.test.assert.allclose(t, result, expected, null, RTOL, ATOL);
		});
	};
}

directory = "0001";

// in [1,4096]
// out [1,999]
var test = {
	M : 55,
	N : 55,
	C : 96,
	size : 3,
	stride : 2
}

var testName = "pool: " + test.M + " x " + test.N + " x " + test.C;

tape(testName, generateTestCase(directory, test.M, test.N, test.C, test.size, test.stride));
<|endoftext|>


<commit_before>class FetchInterceptor {
  constructor() {
    this.interceptors = [];

    this.fetch = (...args) => this.interceptorWrapper(fetch, ...args);
  }

  /**
   * add new interceptors
   * @param {(Object|Object[])} interceptors
   */
  addInterceptors(interceptors) {
    const removeIndex = [];

    if (Array.isArray(interceptors)) {
      interceptors.map(interceptor => {
        removeIndex.push(this.interceptors.length);
        return this.interceptors.push(interceptor);
      });
    } else if (interceptors instanceof Object) {
      removeIndex.push(this.interceptors.length);
      this.interceptors.push(interceptors);
    }

    this.updateInterceptors();

    return () => this.removeInterceptors(removeIndex);
  }

  removeInterceptors(indexes) {
    if (Array.isArray(indexes)) {
      indexes.map(index => this.interceptors.splice(index, 1));
      this.updateInterceptors();
    }
  }

  updateInterceptors() {
    this.reversedInterceptors = this.interceptors
      .reduce((array, interceptor) => [interceptor].concat(array), []);
  }

  /**
   * remove all interceptors
   */
  clearInterceptors() {
    this.interceptors = [];

    this.updateInterceptors();
  }

  interceptorWrapper(fetch, ...args) {
    let promise = Promise.resolve(args);

    this.reversedInterceptors.forEach(({ request, requestError }) => {
      if (request || requestError) {
        promise = promise.then(() => request(...args), requestError);
      }
    });

    promise = promise.then(() => fetch(...args));

    this.reversedInterceptors.forEach(({ response, responseError }) => {
      if (response || responseError) {
        promise = promise.then(response, responseError);
      }
    });

    return promise;
  }
}

class FetchQL extends FetchInterceptor {
  /**
   * FetchQL Class
   * @class
   * @param {String} url - the server address of GraphQL
   * @param {(Object|Object[])=} interceptors
   * @param {{}=} headers - request headers
   * @param {FetchQL~requestQueueChanged=} onStart - callback function of a new request queue
   * @param {FetchQL~requestQueueChanged=} onEnd - callback function of request queue finished
   * @param {Boolean=} omitEmptyVariables - remove null props(null or '') from the variables
   */
  constructor({ url, interceptors, headers, onStart, onEnd, omitEmptyVariables = false }) {
    super();

    this.requestObject = {
      method: 'POST',
      headers: Object.assign({}, {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      }, headers),
      credentials: 'same-origin',
    };

    this.url = url;

    this.omitEmptyVariables = omitEmptyVariables;

    // marker for request queue
    this.requestQueueLength = 0;

    // using for caching enums' type
    this.EnumMap = {};

    this.callbacks = {
      onStart,
      onEnd,
    };

    this.addInterceptors(interceptors);
  }

  /**
   * operate an query
   * @param {String} operationName
   * @param {String} query
   * @param {Object} variables
   * @param {Object} opts - addition options(will not be passed to server)
   * @param {Boolean=} options.omitEmptyVariables - remove null props(null or '') from the variables
   * @returns {Promise}
   * @memberOf FetchQL
   */
  query({ operationName, query, variables, opts = {} }) {
    const options = Object.assign({}, this.requestObject);
    let vars;
    if (this.omitEmptyVariables || opts.omitEmptyVariables) {
      vars = this.doOmitEmptyVariables(variables);
    } else {
      vars = variables;
    }
    const body = {
      operationName,
      query,
      variables: JSON.stringify(vars),
    };
    options.body = JSON.stringify(body);

    this.onStart();

    return this.fetch(this.url, options)
      .then(res => {
        if (res.ok) {
          return res.json();
        }
        // return an custom error stack if request error
        return {
          errors: [{
            message: res.statusText,
            stack: res,
          }],
        };
      })
      .then(({ data, errors }) => (
        new Promise((resolve, reject) => {
          this.onEnd();

          // if data in response is 'null'
          if (!data) {
            return reject(errors || [{}]);
          }
          // if all properties of data is 'null'
          const allDataKeyEmpty = Object.keys(data).every(key => !data[key]);
          if (allDataKeyEmpty) {
            return reject(errors);
          }
          return resolve({ data, errors });
        })
      ));
  }

  /**
   * get current server address
   * @returns {String}
   * @memberOf FetchQL
   */
  getUrl() {
    return this.url;
  }

  /**
   * setting a new server address
   * @param {String} url
   * @memberOf FetchQL
   */
  setUrl(url) {
    this.url = url;
  }

  /**
   * get information of enum type
   * @param {String[]} EnumNameList - array of enums' name
   * @returns {Promise}
   * @memberOf FetchQL
   */
  getEnumTypes(EnumNameList) {
    const fullData = {};

    // check cache status
    const unCachedEnumList = EnumNameList.filter(element => {
      if (this.EnumMap[element]) {
        // enum has been cached
        fullData[element] = this.EnumMap[element];
        return false;
      }
      return true;
    });

    // immediately return the data if all enums have been cached
    if (!unCachedEnumList.length) {
      return new Promise((resolve) => {
        resolve({ data: fullData });
      });
    }

    // build query string for uncached enums
    const EnumTypeQuery = unCachedEnumList.map(type => (
      `${type}: __type(name: "${type}") {
        ...EnumFragment
      }`
    ));

    const query = `
      query {
        ${EnumTypeQuery.join('\n')}
      }

      fragment EnumFragment on __Type {
        kind
        description
        enumValues {
          name
          description
        }
      }`;

    const options = Object.assign({}, this.requestObject);
    options.body = JSON.stringify({ query });

    this.onStart();

    return this.fetch(this.url, options)
      .then(res => {
        if (res.ok) {
          return res.json();
        }
        // return an custom error stack if request error
        return {
          errors: [{
            message: res.statusText,
            stack: res,
          }],
        };
      })
      .then(({ data, errors }) => (
        new Promise((resolve, reject) => {
          this.onEnd();

          // if data in response is 'null' and have any errors
          if (!data) {
            return reject(errors || [{}]);
          }
          // if all properties of data is 'null'
          const allDataKeyEmpty = Object.keys(data).every(key => !data[key]);
          if (allDataKeyEmpty && errors && errors.length) {
            return reject(errors);
          }
          // merge enums' data
          const passData = Object.assign(fullData, data);
          // cache new enums' data
          Object.keys(data).map(key => {
            this.EnumMap[key] = data[key];
            return key;
          });
          return resolve({ data: passData, errors });
        })
      ));
  }

  /**
   * calling on a request starting
   * if the request belong to a new queue, call the 'onStart' method
   */
  onStart() {
    this.requestQueueLength++;
    if (this.requestQueueLength > 1 || !this.callbacks.onStart) {
      return;
    }
    this.callbacks.onStart(this.requestQueueLength);
  }

  /**
   * calling on a request ending
   * if current queue finished, calling the 'onEnd' method
   */
  onEnd() {
    this.requestQueueLength--;
    if (this.requestQueueLength || !this.callbacks.onEnd) {
      return;
    }
    this.callbacks.onEnd(this.requestQueueLength);
  }

  /**
   * Callback of requests queue changes.(e.g. new queue or queue finished)
   * @callback FetchQL~requestQueueChanged
   * @param {number} queueLength - length of current request queue
   */

  /**
   * remove empty props(null or '') from object
   * @param {Object} input
   * @returns {Object}
   * @memberOf FetchQL
   * @private
   */
  doOmitEmptyVariables(input) {
    const nonEmptyObj = {};
    Object.keys(input).map(key => {
      if ((input[key] !== null && input[key] !== '')) {
        nonEmptyObj[key] = input[key];
      }
      return key;
    });
    return nonEmptyObj;
  }
}

export default FetchQL;
<commit_msg>fix(docs)<commit_after>class FetchInterceptor {
  constructor() {
    this.interceptors = [];

    this.fetch = (...args) => this.interceptorWrapper(fetch, ...args);
  }

  /**
   * add new interceptors
   * @param {(Object|Object[])} interceptors
   */
  addInterceptors(interceptors) {
    const removeIndex = [];

    if (Array.isArray(interceptors)) {
      interceptors.map(interceptor => {
        removeIndex.push(this.interceptors.length);
        return this.interceptors.push(interceptor);
      });
    } else if (interceptors instanceof Object) {
      removeIndex.push(this.interceptors.length);
      this.interceptors.push(interceptors);
    }

    this.updateInterceptors();

    return () => this.removeInterceptors(removeIndex);
  }

  removeInterceptors(indexes) {
    if (Array.isArray(indexes)) {
      indexes.map(index => this.interceptors.splice(index, 1));
      this.updateInterceptors();
    }
  }

  updateInterceptors() {
    this.reversedInterceptors = this.interceptors
      .reduce((array, interceptor) => [interceptor].concat(array), []);
  }

  /**
   * remove all interceptors
   */
  clearInterceptors() {
    this.interceptors = [];

    this.updateInterceptors();
  }

  interceptorWrapper(fetch, ...args) {
    let promise = Promise.resolve(args);

    this.reversedInterceptors.forEach(({ request, requestError }) => {
      if (request || requestError) {
        promise = promise.then(() => request(...args), requestError);
      }
    });

    promise = promise.then(() => fetch(...args));

    this.reversedInterceptors.forEach(({ response, responseError }) => {
      if (response || responseError) {
        promise = promise.then(response, responseError);
      }
    });

    return promise;
  }
}

class FetchQL extends FetchInterceptor {
  /**
   * FetchQL Class
   * @class
   * @param {String} url - the server address of GraphQL
   * @param {(Object|Object[])=} interceptors
   * @param {{}=} headers - request headers
   * @param {FetchQL~requestQueueChanged=} onStart - callback function of a new request queue
   * @param {FetchQL~requestQueueChanged=} onEnd - callback function of request queue finished
   * @param {Boolean=} omitEmptyVariables - remove null props(null or '') from the variables
   */
  constructor({ url, interceptors, headers, onStart, onEnd, omitEmptyVariables = false }) {
    super();

    this.requestObject = {
      method: 'POST',
      headers: Object.assign({}, {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      }, headers),
      credentials: 'same-origin',
    };

    this.url = url;

    this.omitEmptyVariables = omitEmptyVariables;

    // marker for request queue
    this.requestQueueLength = 0;

    // using for caching enums' type
    this.EnumMap = {};

    this.callbacks = {
      onStart,
      onEnd,
    };

    this.addInterceptors(interceptors);
  }

  /**
   * operate an query
   * @param {String} operationName
   * @param {String} query
   * @param {Object} variables
   * @param {Object} opts - addition options(will not be passed to server)
   * @param {Boolean=} opts.omitEmptyVariables - remove null props(null or '') from the variables
   * @returns {Promise}
   * @memberOf FetchQL
   */
  query({ operationName, query, variables, opts = {} }) {
    const options = Object.assign({}, this.requestObject);
    let vars;
    if (this.omitEmptyVariables || opts.omitEmptyVariables) {
      vars = this.doOmitEmptyVariables(variables);
    } else {
      vars = variables;
    }
    const body = {
      operationName,
      query,
      variables: JSON.stringify(vars),
    };
    options.body = JSON.stringify(body);

    this.onStart();

    return this.fetch(this.url, options)
      .then(res => {
        if (res.ok) {
          return res.json();
        }
        // return an custom error stack if request error
        return {
          errors: [{
            message: res.statusText,
            stack: res,
          }],
        };
      })
      .then(({ data, errors }) => (
        new Promise((resolve, reject) => {
          this.onEnd();

          // if data in response is 'null'
          if (!data) {
            return reject(errors || [{}]);
          }
          // if all properties of data is 'null'
          const allDataKeyEmpty = Object.keys(data).every(key => !data[key]);
          if (allDataKeyEmpty) {
            return reject(errors);
          }
          return resolve({ data, errors });
        })
      ));
  }

  /**
   * get current server address
   * @returns {String}
   * @memberOf FetchQL
   */
  getUrl() {
    return this.url;
  }

  /**
   * setting a new server address
   * @param {String} url
   * @memberOf FetchQL
   */
  setUrl(url) {
    this.url = url;
  }

  /**
   * get information of enum type
   * @param {String[]} EnumNameList - array of enums' name
   * @returns {Promise}
   * @memberOf FetchQL
   */
  getEnumTypes(EnumNameList) {
    const fullData = {};

    // check cache status
    const unCachedEnumList = EnumNameList.filter(element => {
      if (this.EnumMap[element]) {
        // enum has been cached
        fullData[element] = this.EnumMap[element];
        return false;
      }
      return true;
    });

    // immediately return the data if all enums have been cached
    if (!unCachedEnumList.length) {
      return new Promise((resolve) => {
        resolve({ data: fullData });
      });
    }

    // build query string for uncached enums
    const EnumTypeQuery = unCachedEnumList.map(type => (
      `${type}: __type(name: "${type}") {
        ...EnumFragment
      }`
    ));

    const query = `
      query {
        ${EnumTypeQuery.join('\n')}
      }

      fragment EnumFragment on __Type {
        kind
        description
        enumValues {
          name
          description
        }
      }`;

    const options = Object.assign({}, this.requestObject);
    options.body = JSON.stringify({ query });

    this.onStart();

    return this.fetch(this.url, options)
      .then(res => {
        if (res.ok) {
          return res.json();
        }
        // return an custom error stack if request error
        return {
          errors: [{
            message: res.statusText,
            stack: res,
          }],
        };
      })
      .then(({ data, errors }) => (
        new Promise((resolve, reject) => {
          this.onEnd();

          // if data in response is 'null' and have any errors
          if (!data) {
            return reject(errors || [{}]);
          }
          // if all properties of data is 'null'
          const allDataKeyEmpty = Object.keys(data).every(key => !data[key]);
          if (allDataKeyEmpty && errors && errors.length) {
            return reject(errors);
          }
          // merge enums' data
          const passData = Object.assign(fullData, data);
          // cache new enums' data
          Object.keys(data).map(key => {
            this.EnumMap[key] = data[key];
            return key;
          });
          return resolve({ data: passData, errors });
        })
      ));
  }

  /**
   * calling on a request starting
   * if the request belong to a new queue, call the 'onStart' method
   */
  onStart() {
    this.requestQueueLength++;
    if (this.requestQueueLength > 1 || !this.callbacks.onStart) {
      return;
    }
    this.callbacks.onStart(this.requestQueueLength);
  }

  /**
   * calling on a request ending
   * if current queue finished, calling the 'onEnd' method
   */
  onEnd() {
    this.requestQueueLength--;
    if (this.requestQueueLength || !this.callbacks.onEnd) {
      return;
    }
    this.callbacks.onEnd(this.requestQueueLength);
  }

  /**
   * Callback of requests queue changes.(e.g. new queue or queue finished)
   * @callback FetchQL~requestQueueChanged
   * @param {number} queueLength - length of current request queue
   */

  /**
   * remove empty props(null or '') from object
   * @param {Object} input
   * @returns {Object}
   * @memberOf FetchQL
   * @private
   */
  doOmitEmptyVariables(input) {
    const nonEmptyObj = {};
    Object.keys(input).map(key => {
      if ((input[key] !== null && input[key] !== '')) {
        nonEmptyObj[key] = input[key];
      }
      return key;
    });
    return nonEmptyObj;
  }
}

export default FetchQL;
<|endoftext|>


<commit_before>iD.Map = function(context) {
    var dimensions = [1, 1],
        dispatch = d3.dispatch('move', 'drawn'),
        projection = d3.geo.mercator().scale(512 / Math.PI),
        roundedProjection = iD.svg.RoundProjection(projection),
        zoom = d3.behavior.zoom()
            .translate(projection.translate())
            .scale(projection.scale() * 2 * Math.PI)
            .scaleExtent([1024, 256 * Math.pow(2, 24)])
            .on('zoom', zoomPan),
        dblclickEnabled = true,
        transformStart,
        transformed = false,
        minzoom = 0,
        layers = [
            iD.Background().projection(projection),
            iD.LocalGpx(context).projection(projection),
            iD.Background('overlay').projection(projection)
        ],
        transformProp = iD.util.prefixCSSProperty('Transform'),
        points = iD.svg.Points(roundedProjection, context),
        vertices = iD.svg.Vertices(roundedProjection, context),
        lines = iD.svg.Lines(projection),
        areas = iD.svg.Areas(roundedProjection),
        midpoints = iD.svg.Midpoints(roundedProjection, context),
        labels = iD.svg.Labels(roundedProjection, context),
        tail = iD.ui.Tail(),
        supersurface, surface,
        mouse;

    function map(selection) {
        context.history()
            .on('change.map', redraw);

        context.on('select.map', function() {
            redraw();
        });

        selection.call(zoom);

        supersurface = selection.append('div')
            .attr('id', 'supersurface');

        layers.forEach(function(layer) {
            supersurface.call(layer);
        });

        // Need a wrapper div because Opera can't cope with an absolutely positioned
        // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
        var dataLayer = supersurface.append('div')
            .attr('class', 'layer-layer layer-data');

        surface = dataLayer.append('svg')
            .on('mousedown.zoom', function() {
                if (d3.event.button == 2) {
                    d3.event.stopPropagation();
                }
            }, true)
            .on('mouseup.zoom', function() {
                if (resetTransform()) redraw();
            })
            .attr('id', 'surface')
            .call(iD.svg.Surface(context));

        surface.on('mouseover.vertices', function() {
            if (map.editable() && !transformed) {
                var hover = d3.event.target.__data__;
                surface.call(vertices.drawHover, context.graph(), hover, map.zoom());
            }
        });

        surface.on('mouseout.vertices', function() {
            if (map.editable() && !transformed) {
                var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
                surface.call(vertices.drawHover, context.graph(), hover, map.zoom());
            }
        });

        map.size(selection.size());
        map.surface = surface;

        labels.supersurface(supersurface);
        mouse = iD.util.fastMouse(supersurface.node());

        supersurface
            .call(tail);
    }

    function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }

    function drawVector(difference) {
        var filter, all,
            extent = map.extent(),
            graph = context.graph();

        if (!difference) {
            all = context.intersects(extent);
            filter = d3.functor(true);
        } else {
            var complete = difference.complete(extent);
            all = _.compact(_.values(complete));
            filter = function(d) {
                if (d.type === 'midpoint') {

                    var a = d.edge[0],
                        b = d.edge[1];

                    // redraw a midpoint if it needs to be
                    // - moved (either edge node moved)
                    // - deleted (edge nodes not consecutive in any parent way)
                    if (a in complete || b in complete) return true;

                    var parentsWays = graph.parentWays({ id: a });
                    for (var i = 0; i < parentsWays.length; i++) {
                        var nodes = parentsWays[i].nodes;
                        for (var n = 0; n < nodes.length; n++) {
                            if (nodes[n] === a && (nodes[n - 1] === b || nodes[n + 1] === b)) return false;
                        }
                    }
                    return true;

                } else {
                    return d.id in complete;
                }
            };
        }

        if (all.length > 100000) {
            editOff();
        } else {
            surface
                .call(points, graph, all, filter)
                .call(vertices, graph, all, filter, map.zoom())
                .call(lines, graph, all, filter)
                .call(areas, graph, all, filter)
                .call(midpoints, graph, all, filter, extent)
                .call(labels, graph, all, filter, dimensions, !difference);
        }
        dispatch.drawn(map);
    }

    function editOff() {
        surface.selectAll('.layer *').remove();
    }

    function zoomPan() {
        if (d3.event && d3.event.sourceEvent.type === 'dblclick') {
            if (!dblclickEnabled) {
                zoom.scale(projection.scale() * 2 * Math.PI)
                    .translate(projection.translate());
                return d3.event.sourceEvent.preventDefault();
            }
        }

        if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
            iD.ui.flash(context.container())
                .select('.content')
                .text(t('cannot_zoom'));
            return setZoom(16, true);
        }

        projection
            .translate(d3.event.translate)
            .scale(d3.event.scale / (2 * Math.PI));

        var ascale = d3.event.scale;
        var bscale = transformStart[0];
        var scale = (ascale / bscale);

        var tX = Math.round((d3.event.translate[0] / scale) - (transformStart[1][0]));
        var tY = Math.round((d3.event.translate[1] / scale) - (transformStart[1][1]));

        var transform =
            'scale(' + scale + ')' +
            (iD.detect().opera ?
                'translate(' + tX + 'px,' + tY + 'px)' :
                'translate3d(' + tX + 'px,' + tY + 'px, 0)');

        transformed = true;
        supersurface.style(transformProp, transform);
        queueRedraw();

        dispatch.move(map);
    }

    function resetTransform() {
        if (!transformed) return false;
        supersurface.style(transformProp, '');
        transformed = false;
        return true;
    }

    function redraw(difference) {

        if (!surface) return;

        clearTimeout(timeoutId);

        // If we are in the middle of a zoom/pan, we can't do differenced redraws.
        // It would result in artifacts where differenced entities are redrawn with
        // one transform and unchanged entities with another.
        if (resetTransform()) {
            difference = undefined;
        }

        var zoom = String(~~map.zoom());
        if (surface.attr('data-zoom') !== zoom) {
            surface.attr('data-zoom', zoom);
        }

        if (!difference) {
            layers.forEach(function(layer) {
                supersurface.call(layer);
            });
        }

        if (map.editable()) {
            context.connection().loadTiles(projection, dimensions);
            drawVector(difference);
        } else {
            editOff();
        }

        transformStart = [
            projection.scale() * 2 * Math.PI,
            projection.translate().slice()];

        return map;
    }

    var timeoutId;
    function queueRedraw() {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() { redraw(); }, 300);
    }

    function pointLocation(p) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
    }

    function locationPoint(l) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
    }

    map.mouse = function() {
        var e = d3.event, s;
        while (s = e.sourceEvent) e = s;
        return mouse(e);
    };

    map.mouseCoordinates = function() {
        return projection.invert(map.mouse());
    };

    map.dblclickEnable = function(_) {
        if (!arguments.length) return dblclickEnabled;
        dblclickEnabled = _;
        return map;
    };

    function setZoom(z, force) {
        if (z === map.zoom() && !force)
            return false;
        var scale = 256 * Math.pow(2, z),
            center = pxCenter(),
            l = pointLocation(center);
        scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
        projection.scale(scale / (2 * Math.PI));
        zoom.scale(scale);
        var t = projection.translate();
        l = locationPoint(l);
        t[0] += center[0] - l[0];
        t[1] += center[1] - l[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        return true;
    }

    function setCenter(loc) {
        var t = projection.translate(),
            c = pxCenter(),
            ll = projection(loc);
        if (ll[0] === c[0] && ll[1] === c[1])
            return false;
        projection.translate([
            t[0] - ll[0] + c[0],
            t[1] - ll[1] + c[1]]);
        zoom.translate(projection.translate());
        return true;
    }

    map.pan = function(d) {
        var t = projection.translate();
        t[0] += d[0];
        t[1] += d[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        dispatch.move(map);
        return redraw();
    };

    map.size = function(_) {
        if (!arguments.length) return dimensions;
        var center = map.center();
        dimensions = _;
        surface.size(dimensions);
        layers.forEach(function(layer) {
            layer.size(dimensions);
        });
        projection.clipExtent([[0, 0], dimensions]);
        setCenter(center);
        return redraw();
    };

    map.zoomIn = function() { return map.zoom(Math.ceil(map.zoom() + 1)); };
    map.zoomOut = function() { return map.zoom(Math.floor(map.zoom() - 1)); };

    map.center = function(loc) {
        if (!arguments.length) {
            return projection.invert(pxCenter());
        }

        if (setCenter(loc)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoom = function(z) {
        if (!arguments.length) {
            return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
        }

        if (setZoom(z)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoomTo = function(entity) {
        var extent = entity.extent(context.graph()),
            zoom = map.extentZoom(extent);
        map.centerZoom(extent.center(), zoom);
    };

    map.centerZoom = function(loc, z) {
        var centered = setCenter(loc),
            zoomed   = setZoom(z);

        if (centered || zoomed) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.centerEase = function(loc) {
        var from = map.center().slice(),
            t = 0,
            stop;

        surface.one('mousedown.ease', function() {
            stop = true;
        });

        d3.timer(function() {
            if (stop) return true;
            map.center(iD.geo.interp(from, loc, (t += 1) / 10));
            return t == 10;
        }, 20);
        return map;
    };

    map.extent = function(_) {
        if (!arguments.length) {
            return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
                                 projection.invert([dimensions[0], 0]));
        } else {
            var extent = iD.geo.Extent(_);
            map.centerZoom(extent.center(), map.extentZoom(extent));
        }
    };

    map.extentZoom = function(_) {
        var extent = iD.geo.Extent(_),
            tl = projection([extent[0][0], extent[1][1]]),
            br = projection([extent[1][0], extent[0][1]]);

        // Calculate maximum zoom that fits extent
        var hFactor = (br[0] - tl[0]) / dimensions[0],
            vFactor = (br[1] - tl[1]) / dimensions[1],
            hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
            vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
            newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);

        return newZoom;
    };

    map.flush = function() {
        context.connection().flush();
        context.history().reset();
        return map;
    };

    var usedTails = {};
    map.tail = function(_) {
        if (!_ || usedTails[_] === undefined) {
            tail.text(_);
            usedTails[_] = true;
        }
        return map;
    };

    map.editable = function() {
        return map.zoom() >= 16;
    };

    map.minzoom = function(_) {
        if (!arguments.length) return minzoom;
        minzoom = _;
        return map;
    };

    map.layers = layers;
    map.projection = projection;
    map.redraw = redraw;

    return d3.rebind(map, dispatch, 'on');
};
<commit_msg>Add URL flag for translate3d<commit_after>iD.Map = function(context) {
    var dimensions = [1, 1],
        dispatch = d3.dispatch('move', 'drawn'),
        projection = d3.geo.mercator().scale(512 / Math.PI),
        roundedProjection = iD.svg.RoundProjection(projection),
        zoom = d3.behavior.zoom()
            .translate(projection.translate())
            .scale(projection.scale() * 2 * Math.PI)
            .scaleExtent([1024, 256 * Math.pow(2, 24)])
            .on('zoom', zoomPan),
        dblclickEnabled = true,
        transformStart,
        transformed = false,
        minzoom = 0,
        layers = [
            iD.Background().projection(projection),
            iD.LocalGpx(context).projection(projection),
            iD.Background('overlay').projection(projection)
        ],
        transformProp = iD.util.prefixCSSProperty('Transform'),
        points = iD.svg.Points(roundedProjection, context),
        vertices = iD.svg.Vertices(roundedProjection, context),
        lines = iD.svg.Lines(projection),
        areas = iD.svg.Areas(roundedProjection),
        midpoints = iD.svg.Midpoints(roundedProjection, context),
        labels = iD.svg.Labels(roundedProjection, context),
        tail = iD.ui.Tail(),
        supersurface, surface,
        mouse;

    function map(selection) {
        context.history()
            .on('change.map', redraw);

        context.on('select.map', function() {
            redraw();
        });

        selection.call(zoom);

        supersurface = selection.append('div')
            .attr('id', 'supersurface');

        layers.forEach(function(layer) {
            supersurface.call(layer);
        });

        // Need a wrapper div because Opera can't cope with an absolutely positioned
        // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
        var dataLayer = supersurface.append('div')
            .attr('class', 'layer-layer layer-data');

        surface = dataLayer.append('svg')
            .on('mousedown.zoom', function() {
                if (d3.event.button == 2) {
                    d3.event.stopPropagation();
                }
            }, true)
            .on('mouseup.zoom', function() {
                if (resetTransform()) redraw();
            })
            .attr('id', 'surface')
            .call(iD.svg.Surface(context));

        surface.on('mouseover.vertices', function() {
            if (map.editable() && !transformed) {
                var hover = d3.event.target.__data__;
                surface.call(vertices.drawHover, context.graph(), hover, map.zoom());
            }
        });

        surface.on('mouseout.vertices', function() {
            if (map.editable() && !transformed) {
                var hover = d3.event.relatedTarget && d3.event.relatedTarget.__data__;
                surface.call(vertices.drawHover, context.graph(), hover, map.zoom());
            }
        });

        map.size(selection.size());
        map.surface = surface;

        labels.supersurface(supersurface);
        mouse = iD.util.fastMouse(supersurface.node());

        supersurface
            .call(tail);
    }

    function pxCenter() { return [dimensions[0] / 2, dimensions[1] / 2]; }

    function drawVector(difference) {
        var filter, all,
            extent = map.extent(),
            graph = context.graph();

        if (!difference) {
            all = context.intersects(extent);
            filter = d3.functor(true);
        } else {
            var complete = difference.complete(extent);
            all = _.compact(_.values(complete));
            filter = function(d) {
                if (d.type === 'midpoint') {

                    var a = d.edge[0],
                        b = d.edge[1];

                    // redraw a midpoint if it needs to be
                    // - moved (either edge node moved)
                    // - deleted (edge nodes not consecutive in any parent way)
                    if (a in complete || b in complete) return true;

                    var parentsWays = graph.parentWays({ id: a });
                    for (var i = 0; i < parentsWays.length; i++) {
                        var nodes = parentsWays[i].nodes;
                        for (var n = 0; n < nodes.length; n++) {
                            if (nodes[n] === a && (nodes[n - 1] === b || nodes[n + 1] === b)) return false;
                        }
                    }
                    return true;

                } else {
                    return d.id in complete;
                }
            };
        }

        if (all.length > 100000) {
            editOff();
        } else {
            surface
                .call(points, graph, all, filter)
                .call(vertices, graph, all, filter, map.zoom())
                .call(lines, graph, all, filter)
                .call(areas, graph, all, filter)
                .call(midpoints, graph, all, filter, extent)
                .call(labels, graph, all, filter, dimensions, !difference);
        }
        dispatch.drawn(map);
    }

    function editOff() {
        surface.selectAll('.layer *').remove();
    }

    var translate3d = !iD.detect().opera &&
        iD.util.stringQs(location.search.substring(1)).translate3d !== '0';

    function zoomPan() {
        if (d3.event && d3.event.sourceEvent.type === 'dblclick') {
            if (!dblclickEnabled) {
                zoom.scale(projection.scale() * 2 * Math.PI)
                    .translate(projection.translate());
                return d3.event.sourceEvent.preventDefault();
            }
        }

        if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
            iD.ui.flash(context.container())
                .select('.content')
                .text(t('cannot_zoom'));
            return setZoom(16, true);
        }

        projection
            .translate(d3.event.translate)
            .scale(d3.event.scale / (2 * Math.PI));

        var ascale = d3.event.scale;
        var bscale = transformStart[0];
        var scale = (ascale / bscale);

        var tX = Math.round((d3.event.translate[0] / scale) - (transformStart[1][0]));
        var tY = Math.round((d3.event.translate[1] / scale) - (transformStart[1][1]));

        var transform =
            'scale(' + scale + ')' +
            (translate3d ?
                'translate3d(' + tX + 'px,' + tY + 'px, 0)' :
                'translate(' + tX + 'px,' + tY + 'px)');

        transformed = true;
        supersurface.style(transformProp, transform);
        queueRedraw();

        dispatch.move(map);
    }

    function resetTransform() {
        if (!transformed) return false;
        supersurface.style(transformProp, '');
        transformed = false;
        return true;
    }

    function redraw(difference) {

        if (!surface) return;

        clearTimeout(timeoutId);

        // If we are in the middle of a zoom/pan, we can't do differenced redraws.
        // It would result in artifacts where differenced entities are redrawn with
        // one transform and unchanged entities with another.
        if (resetTransform()) {
            difference = undefined;
        }

        var zoom = String(~~map.zoom());
        if (surface.attr('data-zoom') !== zoom) {
            surface.attr('data-zoom', zoom);
        }

        if (!difference) {
            layers.forEach(function(layer) {
                supersurface.call(layer);
            });
        }

        if (map.editable()) {
            context.connection().loadTiles(projection, dimensions);
            drawVector(difference);
        } else {
            editOff();
        }

        transformStart = [
            projection.scale() * 2 * Math.PI,
            projection.translate().slice()];

        return map;
    }

    var timeoutId;
    function queueRedraw() {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function() { redraw(); }, 300);
    }

    function pointLocation(p) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [(p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale];
    }

    function locationPoint(l) {
        var translate = projection.translate(),
            scale = projection.scale() * 2 * Math.PI;
        return [l[0] * scale + translate[0], l[1] * scale + translate[1]];
    }

    map.mouse = function() {
        var e = d3.event, s;
        while (s = e.sourceEvent) e = s;
        return mouse(e);
    };

    map.mouseCoordinates = function() {
        return projection.invert(map.mouse());
    };

    map.dblclickEnable = function(_) {
        if (!arguments.length) return dblclickEnabled;
        dblclickEnabled = _;
        return map;
    };

    function setZoom(z, force) {
        if (z === map.zoom() && !force)
            return false;
        var scale = 256 * Math.pow(2, z),
            center = pxCenter(),
            l = pointLocation(center);
        scale = Math.max(1024, Math.min(256 * Math.pow(2, 24), scale));
        projection.scale(scale / (2 * Math.PI));
        zoom.scale(scale);
        var t = projection.translate();
        l = locationPoint(l);
        t[0] += center[0] - l[0];
        t[1] += center[1] - l[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        return true;
    }

    function setCenter(loc) {
        var t = projection.translate(),
            c = pxCenter(),
            ll = projection(loc);
        if (ll[0] === c[0] && ll[1] === c[1])
            return false;
        projection.translate([
            t[0] - ll[0] + c[0],
            t[1] - ll[1] + c[1]]);
        zoom.translate(projection.translate());
        return true;
    }

    map.pan = function(d) {
        var t = projection.translate();
        t[0] += d[0];
        t[1] += d[1];
        projection.translate(t);
        zoom.translate(projection.translate());
        dispatch.move(map);
        return redraw();
    };

    map.size = function(_) {
        if (!arguments.length) return dimensions;
        var center = map.center();
        dimensions = _;
        surface.size(dimensions);
        layers.forEach(function(layer) {
            layer.size(dimensions);
        });
        projection.clipExtent([[0, 0], dimensions]);
        setCenter(center);
        return redraw();
    };

    map.zoomIn = function() { return map.zoom(Math.ceil(map.zoom() + 1)); };
    map.zoomOut = function() { return map.zoom(Math.floor(map.zoom() - 1)); };

    map.center = function(loc) {
        if (!arguments.length) {
            return projection.invert(pxCenter());
        }

        if (setCenter(loc)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoom = function(z) {
        if (!arguments.length) {
            return Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.LN2 - 8, 0);
        }

        if (setZoom(z)) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.zoomTo = function(entity) {
        var extent = entity.extent(context.graph()),
            zoom = map.extentZoom(extent);
        map.centerZoom(extent.center(), zoom);
    };

    map.centerZoom = function(loc, z) {
        var centered = setCenter(loc),
            zoomed   = setZoom(z);

        if (centered || zoomed) {
            dispatch.move(map);
        }

        return redraw();
    };

    map.centerEase = function(loc) {
        var from = map.center().slice(),
            t = 0,
            stop;

        surface.one('mousedown.ease', function() {
            stop = true;
        });

        d3.timer(function() {
            if (stop) return true;
            map.center(iD.geo.interp(from, loc, (t += 1) / 10));
            return t == 10;
        }, 20);
        return map;
    };

    map.extent = function(_) {
        if (!arguments.length) {
            return new iD.geo.Extent(projection.invert([0, dimensions[1]]),
                                 projection.invert([dimensions[0], 0]));
        } else {
            var extent = iD.geo.Extent(_);
            map.centerZoom(extent.center(), map.extentZoom(extent));
        }
    };

    map.extentZoom = function(_) {
        var extent = iD.geo.Extent(_),
            tl = projection([extent[0][0], extent[1][1]]),
            br = projection([extent[1][0], extent[0][1]]);

        // Calculate maximum zoom that fits extent
        var hFactor = (br[0] - tl[0]) / dimensions[0],
            vFactor = (br[1] - tl[1]) / dimensions[1],
            hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2,
            vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2,
            newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);

        return newZoom;
    };

    map.flush = function() {
        context.connection().flush();
        context.history().reset();
        return map;
    };

    var usedTails = {};
    map.tail = function(_) {
        if (!_ || usedTails[_] === undefined) {
            tail.text(_);
            usedTails[_] = true;
        }
        return map;
    };

    map.editable = function() {
        return map.zoom() >= 16;
    };

    map.minzoom = function(_) {
        if (!arguments.length) return minzoom;
        minzoom = _;
        return map;
    };

    map.layers = layers;
    map.projection = projection;
    map.redraw = redraw;

    return d3.rebind(map, dispatch, 'on');
};
<|endoftext|>


<commit_before>/*
 * raja browser client
 */
(function() {

window.raja = new Raja();

script('/socket.io/socket.io.js', function(err) {
	if (err) throw err;
	var proto = io.Manager.prototype;
	raja._on = proto.on;
	raja._emit = proto.emit;
	raja.off = proto.off;
	raja.listeners = proto.listeners;
	raja.ready();
});

function Raja() {
	this.delays = [];
	this.base = document.getElementById('raja-io').content;
}

Raja.prototype.updateLink = function(resource, mtime) {
	var link = $('head > link[rel="resource"][href="'+resource.url+'"]');
	if (!link) {
		link = document.createElement('link');
		link.rel = "resource";
		link.href = resource.url;
		var tn = document.createTextNode("\n");
		document.head.insertBefore(tn, document.head.firstChild);
		document.head.insertBefore(link, tn);
	}
	if (mtime != null) {
		if (!resource.mtime || mtime > resource.mtime) resource.mtime = mtime;
		link.setAttribute("last-modified", resource.mtime.getTime());
	} else {
		console.warn("empty mtime", resource, mtime);
	}
};

Raja.prototype.delay = function(url, listener) {
	this.delays.push([url, listener]);
	return this;
};

Raja.prototype.ready = function() {
	this.resources = {};
	var links = document.head.querySelectorAll('link[rel="resource"]');
	for (var i=0; i < links.length; i++) {
		var link = links.item(i);
		this.resources[link.href] = {
			mtime: new Date(parseInt(link.getAttribute('last-modified'))),
			url: link.href
		};
	}
	var list = this.delays;
	delete this.delays;
	for (var i=0; i < list.length; i++) {
		this.on(list[i][0], list[i][1]);
	}
};

Raja.prototype.emit = function(what) {
	var args = Array.prototype.slice.call(arguments, 0);
	this._emit.apply(this, args);
	if (what == "error" && this.listeners("error").length == 0) {
		args.shift();
		console.error.apply(console, args);
	}
};

Raja.prototype.on = function(url, listener) {
	if (!this.resources) {
		return this.delay(url, listener);
	}
	if (!this.io) this.setio();
	var self = this;
	if (url == "error") return this._on(url, listener);
	var plistener = function() {
		try {
			listener.apply(null, Array.prototype.slice.call(arguments));
		} catch(e) {
			self.emit('error', e);
		}
	};
	url = this.absolute(url);
	var murl = url.split('?').shift();
	this._on(murl, plistener);
	var resources = this.resources;
	var resource = resources[murl];
	(function(next) {
		if (resource) {
			if (resource.data !== undefined) {
				listener(resource.data, {method:"get", url: url, mtime: resource.mtime});
			}
			return next();
		}
		resource = resources[murl] = {url: murl};
		xhr(url, function(err, txt, mtime) {
			var obj = tryJSON(txt);
			if (err) return next(err);
			resource.data = obj;
			listener(obj, {method:"get", url: url, mtime: mtime});
			self.updateLink(resource, mtime);
			next();
		});
	})(function(err) {
		if (err) self.emit('error', err);
		if (!resource.room) {
			resource.room = murl;
			self.io.emit('join', {room: murl, mtime: resource.mtime.getTime()});
		}
	});
	return this;
};

Raja.prototype.setio = function() {
	this.io = io(this.base);
	var self = this;
	this.io.on('reconnect_failed', function(err) {
		if (err) self.emit('error', err);
	});
	this.io.on('message', function(msg) {
		if (!msg.url) return;
		var data = msg.data;
		if (data) delete msg.data;
		var resource = self.resources[msg.url];
		if (resource && msg.mtime) self.updateLink(resource, msg.mtime);
		self.emit(msg.url, data, msg);
	});
};

Raja.prototype.absolute = function(url) {
	if (/^https?/i.test(url)) return url;
	var path = document.location.pathname;
	if (url.indexOf('..') == 0) {
		path = path.split('/');
		path.pop();
		url = path.join('/') + url.substring(2);
	} else if (url.indexOf('.') == 0) {
		url = path + url.substring(1);
	}
	// regular interpretation of url
	if (!this._a) this._a = document.createElement('a');
	this._a.href = url;
	return this._a.href;
};

function xhr(url, cb) {
	var req = new XMLHttpRequest();
	req.open('GET', url, true);
	req.onreadystatechange = function (e) {
		if (req.readyState == 4) {
			if (req.status >= 200 && req.status < 300) {
				cb(null, req.responseText, tryDate(req.getResponseHeader("Last-Modified")));
			} else {
				cb(req.status, req.responseText);
			}
		}
	};
	req.send(null);
}

function script(url, cb) {
	var script = document.createElement("script");
	script.async = true;
	script.type = 'text/javascript';
	script.charset = 'utf-8';
	script.src = url;
	function done() {
		script.onload = null;
		script.onerror = null;
		if (script.parentNode) {
			script.parentNode.removeChild(script);
		}
		script = null;
	}
	script.onload = function() {
		done();
		cb();
	};
	script.onerror = function(e) {
		done();
		cb(e);
	};
	// Circumvent IE6 bugs with base elements
	document.head.insertBefore(script, document.head.firstChild);
}

function tryJSON(txt) {
	var obj;
	try {
		obj = JSON.parse(txt);
	} catch(e) {
		console.error(e.toString());
		return txt;
	}
	return obj;
}

function tryDate(txt) {
	if (!txt) return;
	var date = new Date(txt);
	if (isNaN(date.getTime())) return;
	else return date;
}

function $(str) {
	return document.querySelector(str);
}

})();
<commit_msg>Always use numeric mtime<commit_after>/*
 * raja browser client
 */
(function() {

window.raja = new Raja();

script('/socket.io/socket.io.js', function(err) {
	if (err) throw err;
	var proto = io.Manager.prototype;
	raja._on = proto.on;
	raja._emit = proto.emit;
	raja.off = proto.off;
	raja.listeners = proto.listeners;
	raja.ready();
});

function Raja() {
	this.delays = [];
	this.base = document.getElementById('raja-io').content;
}

Raja.prototype.updateLink = function(resource, mtime) {
	var link = $('head > link[rel="resource"][href="'+resource.url+'"]');
	if (!link) {
		link = document.createElement('link');
		link.rel = "resource";
		link.href = resource.url;
		var tn = document.createTextNode("\n");
		document.head.insertBefore(tn, document.head.firstChild);
		document.head.insertBefore(link, tn);
	}
	if (mtime != null) {
		if (!resource.mtime || mtime > resource.mtime) resource.mtime = mtime;
		link.setAttribute("last-modified", resource.mtime);
	} else {
		console.warn("empty mtime", resource, mtime);
	}
};

Raja.prototype.delay = function(url, listener) {
	this.delays.push([url, listener]);
	return this;
};

Raja.prototype.ready = function() {
	this.resources = {};
	var links = document.head.querySelectorAll('link[rel="resource"]');
	for (var i=0; i < links.length; i++) {
		var link = links.item(i);
		this.resources[link.href] = {
			mtime: parseInt(link.getAttribute('last-modified')),
			url: link.href
		};
	}
	var list = this.delays;
	delete this.delays;
	for (var i=0; i < list.length; i++) {
		this.on(list[i][0], list[i][1]);
	}
};

Raja.prototype.emit = function(what) {
	var args = Array.prototype.slice.call(arguments, 0);
	this._emit.apply(this, args);
	if (what == "error" && this.listeners("error").length == 0) {
		args.shift();
		console.error.apply(console, args);
	}
};

Raja.prototype.on = function(url, listener) {
	if (!this.resources) {
		return this.delay(url, listener);
	}
	if (!this.io) this.setio();
	var self = this;
	if (url == "error") return this._on(url, listener);
	var plistener = function() {
		try {
			listener.apply(null, Array.prototype.slice.call(arguments));
		} catch(e) {
			self.emit('error', e);
		}
	};
	url = this.absolute(url);
	var murl = url.split('?').shift();
	this._on(murl, plistener);
	var resources = this.resources;
	var resource = resources[murl];
	(function(next) {
		if (resource) {
			if (resource.data !== undefined) {
				listener(resource.data, {method:"get", url: url, mtime: resource.mtime});
			}
			return next();
		}
		resource = resources[murl] = {url: murl};
		xhr(url, function(err, txt, mtime) {
			var obj = tryJSON(txt);
			if (err) return next(err);
			resource.data = obj;
			listener(obj, {method:"get", url: url, mtime: mtime});
			self.updateLink(resource, mtime);
			next();
		});
	})(function(err) {
		if (err) self.emit('error', err);
		if (!resource.room) {
			resource.room = murl;
			self.io.emit('join', {room: murl, mtime: resource.mtime});
		}
	});
	return this;
};

Raja.prototype.setio = function() {
	this.io = io(this.base);
	var self = this;
	this.io.on('reconnect_failed', function(err) {
		if (err) self.emit('error', err);
	});
	this.io.on('message', function(msg) {
		if (!msg.url) return;
		var data = msg.data;
		if (data) delete msg.data;
		var resource = self.resources[msg.url];
		if (resource && msg.mtime) self.updateLink(resource, msg.mtime);
		self.emit(msg.url, data, msg);
	});
};

Raja.prototype.absolute = function(url) {
	if (/^https?/i.test(url)) return url;
	var path = document.location.pathname;
	if (url.indexOf('..') == 0) {
		path = path.split('/');
		path.pop();
		url = path.join('/') + url.substring(2);
	} else if (url.indexOf('.') == 0) {
		url = path + url.substring(1);
	}
	// regular interpretation of url
	if (!this._a) this._a = document.createElement('a');
	this._a.href = url;
	return this._a.href;
};

function xhr(url, cb) {
	var req = new XMLHttpRequest();
	req.open('GET', url, true);
	req.onreadystatechange = function (e) {
		if (req.readyState == 4) {
			if (req.status >= 200 && req.status < 300) {
				cb(null, req.responseText, tryDate(req.getResponseHeader("Last-Modified")));
			} else {
				cb(req.status, req.responseText);
			}
		}
	};
	req.send(null);
}

function script(url, cb) {
	var script = document.createElement("script");
	script.async = true;
	script.type = 'text/javascript';
	script.charset = 'utf-8';
	script.src = url;
	function done() {
		script.onload = null;
		script.onerror = null;
		if (script.parentNode) {
			script.parentNode.removeChild(script);
		}
		script = null;
	}
	script.onload = function() {
		done();
		cb();
	};
	script.onerror = function(e) {
		done();
		cb(e);
	};
	// Circumvent IE6 bugs with base elements
	document.head.insertBefore(script, document.head.firstChild);
}

function tryJSON(txt) {
	var obj;
	try {
		obj = JSON.parse(txt);
	} catch(e) {
		console.error(e.toString());
		return txt;
	}
	return obj;
}

function tryDate(txt) {
	if (!txt) return;
	var date = new Date(txt);
	var time = date.getTime();
	if (isNaN(time)) return;
	else return time;
}

function $(str) {
	return document.querySelector(str);
}

})();
<|endoftext|>