<commit_before>// Ideas from:
// https://github.com/ScanEx/Leaflet.imageTransform/blob/master/src/L.ImageTransform.js
// https://github.com/BenjaminVadant/leaflet-ugeojson

// Depends on:
// https://github.com/santilland/plotty
// https://github.com/constantinius/geotiff.js

// Note this will only work with ESPG:4326 tiffs

if(typeof(plotty)=='undefined'){
    throw new Error("plotty not defined");
};

if(typeof(GeoTIFF)=='undefined'){
    throw new Error("GeoTIFF not defined");
};

L.LeafletGeotiff = L.ImageOverlay.extend({
    initialize: function (url, options) {
        this.raster = {};
        if (options.bounds) {
            this._rasterBounds = L.latLngBounds(options.bounds);
        }
        L.Util.setOptions(this, options);

        this.options.colorScale = (options.colorScale==undefined) ? 'viridis' : options.colorScale;
        this.options.clampLow = (options.clampLow==undefined) ? true : options.clampLow;
        this.options.clampHigh = (options.clampHigh==undefined) ? true : options.clampHigh;
        this.options.arrowSize = (options.arrowSize==undefined) ? 20 : options.arrowSize;

        this._preLoadColorScale(); //Make sure colorScale is ready even if image takes a while to load
        this._getData(url);
    },
    setURL: function(newURL) {
        this._getData(newURL);
    },
    onAdd: function (map) {
        this._map = map;
        if (!this._image) {
            this._initImage();
        }

        map._panes.overlayPane.appendChild(this._image);

        map.on('moveend', this._reset, this);

        if (map.options.zoomAnimation && L.Browser.any3d) {
            map.on('zoomanim', this._animateZoom, this);
        }

        this._reset();
    },
    onRemove: function (map) {
        map.getPanes().overlayPane.removeChild(this._image);

        map.off('moveend', this._reset, this);

        if (map.options.zoomAnimation) {
            map.off('zoomanim', this._animateZoom, this);
        }
    },
    _getData: function(url) {
        var self = this;
        var request = new XMLHttpRequest();
        request.onload = function() {
            if (this.status >= 200 && this.status < 400) {
                self._parseTIFF(this.response);
            } //TODO else handle error
        };
        request.open("GET", url, true);
        request.responseType = "arraybuffer";
        request.send();
    },
    _parseTIFF: function (arrayBuffer) {
        this.tiff = GeoTIFF.parse(arrayBuffer);

        if (typeof(this.options.image)=='undefined') {
            this.options.image = 0;
        }
        if (typeof(this.options.band)=='undefined') {
            this.options.band = 0;
        }
        this.setBand(this.options.band);

        if (!this.options.bounds) {
            var image = this.tiff.getImage(this.options.image);
            var meta = image.getFileDirectory();
            var x_min = meta.ModelTiepoint[3];
            var x_max = x_min + meta.ModelPixelScale[0]*meta.ImageWidth;
            var y_min = meta.ModelTiepoint[4];
            var y_max = y_min - meta.ModelPixelScale[1]*meta.ImageLength;
            this._rasterBounds = L.latLngBounds([[y_min,x_min],[y_max,x_max]]);
            this._reset()
        }
    },
    setBand: function (band) {
        this.options.band = band

        var image = this.tiff.getImage(this.options.image);
        this.raster.data = image.readRasters({samples: [band]})[0];
        this.raster.width = image.getWidth();
        this.raster.height = image.getHeight();

        this._reset()
    },
    getRasterArray: function () {
        return this.raster.data;
    },
    getRasterCols: function () {
        return this.raster.width;
    },
    getRasterRows: function () {
        return this.raster.height;
    },
    setColorScale: function (colorScale) {
        this.options.colorScale = colorScale;
        this._reset();
    },
    setDisplayRange: function (min,max) {
        this.options.displayMin = min;
        this.options.displayMax = max;
        this._reset();
    },
    getValueAtLatLng: function (lat, lng) {
        try {
            var x = Math.floor(this.raster.width*(lng - this._rasterBounds._southWest.lng)/(this._rasterBounds._northEast.lng - this._rasterBounds._southWest.lng));
            var y = this.raster.height-Math.ceil(this.raster.height*(lat - this._rasterBounds._southWest.lat)/(this._rasterBounds._northEast.lat - this._rasterBounds._southWest.lat));
            var i = y*this.raster.width+x
            return this.raster.data[i];
        }
        catch(err) {
            return undefined;
        }
    },
	_animateZoom: function (e) {
        if (L.version >= "1.0") {
            var scale = this._map.getZoomScale(e.zoom),
                offset = this._map._latLngBoundsToNewLayerBounds(this._map.getBounds(), e.zoom, e.center).min;
            L.DomUtil.setTransform(this._image, offset, scale);
        } else {
            var scale = this._map.getZoomScale(e.zoom),
                nw = this._map.getBounds().getNorthWest(),
                se = this._map.getBounds().getSouthEast(),
                topLeft = this._map._latLngToNewLayerPoint(nw, e.zoom, e.center),
                size = this._map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft);
            this._image.style[L.DomUtil.TRANSFORM] =
		        L.DomUtil.getTranslateString(topLeft) + ' scale(' + scale + ') ';
        }
	},
    _reset: function () {
        if (this.hasOwnProperty('_map')) {
            if (this._rasterBounds) {
                topLeft = this._map.latLngToLayerPoint(this._map.getBounds().getNorthWest()),
                size = this._map.latLngToLayerPoint(this._map.getBounds().getSouthEast())._subtract(topLeft);

                L.DomUtil.setPosition(this._image, topLeft);
                this._image.style.width  = size.x + 'px';
                this._image.style.height = size.y + 'px';

                this._drawImage();
            };
        };
    },
    _preLoadColorScale: function () {
        var canvas = document.createElement('canvas');
        var plot = new plotty.plot({
            canvas: canvas, data: [0],
            width: 1, height: 1,
            domain: [this.options.displayMin, this.options.displayMax],
            colorScale: this.options.colorScale,
            clampLow: this.options.clampLow,
            clampHigh: this.options.clampHigh,
        });
        this.colorScaleData = plot.colorScaleCanvas.toDataURL();
    },
    setClip: function(clipLatLngs) {
        this.options.clip = clipLatLngs;
        this._reset();
    },
    _clipMaskToPixelPoints: function() {
        if (this.options.clip) {
            var topLeft = this._map.latLngToLayerPoint(this._map.getBounds().getNorthWest());
            var pixelClipPoints = [];
            for (var p = 0; p < this.options.clip.length; p++) {
                var mercPoint = this._map.latLngToLayerPoint(this.options.clip[p]),
                    pixel = L.point(mercPoint.x - topLeft.x, mercPoint.y - topLeft.y);
                pixelClipPoints.push(pixel);
            }
            this._pixelClipPoints = pixelClipPoints;
        } else {
            this._pixelClipPoints = undefined;
        }
    },
    _drawImage: function () {
        var self = this;
        if (self.raster.hasOwnProperty('data')) {
            topLeft = this._map.latLngToLayerPoint(this._map.getBounds().getNorthWest()),
            size = this._map.latLngToLayerPoint(this._map.getBounds().getSouthEast())._subtract(topLeft);
            var rasterPixelBounds = L.bounds(this._map.latLngToContainerPoint(this._rasterBounds.getNorthWest()),this._map.latLngToContainerPoint(this._rasterBounds.getSouthEast()))
            var xStart = (rasterPixelBounds.min.x>0 ? rasterPixelBounds.min.x : 0);
            var xFinish = (rasterPixelBounds.max.x<size.x ? rasterPixelBounds.max.x : size.x);
            var yStart = (rasterPixelBounds.min.y>0 ? rasterPixelBounds.min.y : 0);
            var yFinish = (rasterPixelBounds.max.y<size.y ? rasterPixelBounds.max.y : size.y);
            var plotWidth = xFinish-xStart;
            var plotHeight = yFinish-yStart;

            if ((plotWidth<=0) || (plotHeight<=0)) {
                console.log(this.options.name,' is off screen.');
                var plotCanvas = document.createElement("canvas");
                plotCanvas.width = size.x;
                plotCanvas.height = size.y;
                var ctx = plotCanvas.getContext("2d");
                ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height);
                this._image.src = plotCanvas.toDataURL();
                return;
            }

            var xOrigin = this._map.getPixelBounds().min.x+xStart;
            var yOrigin = this._map.getPixelBounds().min.y+yStart;
            var lngSpan = (this._rasterBounds._northEast.lng - this._rasterBounds._southWest.lng)/this.raster.width;
            var latSpan = (this._rasterBounds._northEast.lat - this._rasterBounds._southWest.lat)/this.raster.height;

            //Draw image data to canvas and pass to image element
            var plotCanvas = document.createElement("canvas");
            plotCanvas.width = size.x;
            plotCanvas.height = size.y;
            var ctx = plotCanvas.getContext("2d");
            ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height);

            if (this.options.vector==true) {
                var arrowSize = this.options.arrowSize;
                var zoom = this._map.getZoom();
                var gridPxelSize = (rasterPixelBounds.max.x - rasterPixelBounds.min.x) / self.raster.width;
                var stride = Math.max(1,Math.floor(1.2*arrowSize/gridPxelSize));

                for (var y=0;y<self.raster.height;y=y+stride) {
                    for (var x=0;x<self.raster.width;x=x+stride) {
                        var rasterIndex = (y*this.raster.width+x);
                        if (self.raster.data[rasterIndex]>=0) { //Ignore missing values
                            //calculate lat-lon of of this point
                            var currentLng = this._rasterBounds._southWest.lng + (x+0.5)*lngSpan;
                            var currentLat = this._rasterBounds._northEast.lat - (y+0.5)*latSpan;

                            //convert lat-lon to pixel cordinates
                            var projected = this._map.latLngToContainerPoint(L.latLng(currentLat,currentLng)); //If slow could unpack this calculation
                            var xProjected = projected.x;
                            var yProjected = projected.y;

                            //draw an arrow
                            ctx.save();
                            ctx.translate(xProjected, yProjected);
                            ctx.rotate((90+self.raster.data[rasterIndex])*Math.PI/180);
                            ctx.beginPath();
                            ctx.moveTo(-arrowSize/2, 0);
                            ctx.lineTo(+arrowSize/2, 0);
                            ctx.moveTo(arrowSize*0.25, -arrowSize*0.25);
                            ctx.lineTo(+arrowSize/2, 0);
                            ctx.lineTo(arrowSize*0.25, arrowSize*0.25);
                            ctx.stroke();
                            ctx.restore();
                        }
                    }
                }
            } else {
                var plottyCanvas = document.createElement("canvas");
                var plot = new plotty.plot({
                    data: self.raster.data,
                    width: self.raster.width, height: self.raster.height,
                    domain: [self.options.displayMin, self.options.displayMax],
                    colorScale: this.options.colorScale,
                    clampLow: this.options.clampLow,
                    clampHigh: this.options.clampHigh,
                    canvas: plottyCanvas,
                    useWebGL: false,
                });
                plot.setNoDataValue(-9999);
                plot.render();

                this.colorScaleData = plot.colorScaleCanvas.toDataURL();
                var rasterImageData = plottyCanvas.getContext("2d").getImageData(0,0,plottyCanvas.width, plottyCanvas.height);

                //Create image data and Uint32 views of data to speed up copying
                var imageData = new ImageData(plotWidth, plotHeight);
                var outData = imageData.data;
                var outPixelsU32 = new Uint32Array(outData.buffer);
                var inData = rasterImageData.data;
                var inPixelsU32 = new Uint32Array(inData.buffer);

                var zoom = this._map.getZoom();
                var scale = this._map.options.crs.scale(zoom);
                var d = 57.29577951308232; //L.LatLng.RAD_TO_DEG;

                var transformationA = this._map.options.crs.transformation._a;
                var transformationB = this._map.options.crs.transformation._b;
                var transformationC = this._map.options.crs.transformation._c;
                var transformationD = this._map.options.crs.transformation._d;
                if (L.version >= "1.0") {
                    transformationA = transformationA*this._map.options.crs.projection.R;
                    transformationC = transformationC*this._map.options.crs.projection.R;
                }

                for (var y=0;y<plotHeight;y++) {
                    var yUntransformed = ((yOrigin+y) / scale - transformationD) / transformationC;
                    var currentLat = (2 * Math.atan(Math.exp(yUntransformed)) - (Math.PI / 2)) * d;
                    var rasterY = this.raster.height-Math.ceil((currentLat - this._rasterBounds._southWest.lat)/latSpan);

                    for (var x=0;x<plotWidth;x++) {
                        //Location to draw to
                        var index = (y*plotWidth+x);

                        //Calculate lat-lng of (x,y)
                        //This code is based on leaflet code, unpacked to run as fast as possible
                        //Used to deal with TIF being EPSG:4326 (lat,lon) and map being EPSG:3857 (m E,m N)
                        var xUntransformed = ((xOrigin+x) / scale - transformationB) / transformationA;
                        var currentLng = xUntransformed * d;
                        var rasterX = Math.floor((currentLng - this._rasterBounds._southWest.lng)/lngSpan);

                        var rasterIndex = (rasterY*this.raster.width+rasterX);

                        //Copy pixel value
                        outPixelsU32[index] = inPixelsU32[rasterIndex];
                    }
                }
                ctx.putImageData(imageData, xStart, yStart);
            }

            //Draw clipping polygon
            if (this.options.clip) {
                this._clipMaskToPixelPoints();
                ctx.globalCompositeOperation = 'destination-out'
                ctx.rect(xStart-10,yStart-10,plotWidth+20,plotHeight+20);
                //Draw vertices in reverse order
                for (var i = this._pixelClipPoints.length-1; i >= 0; i--) {
                    var pix = this._pixelClipPoints[i];
                    ctx['lineTo'](pix.x, pix.y);
                }
                ctx.closePath();
                ctx.fill();
            }

            this._image.src = String(plotCanvas.toDataURL());
        }
    },
});

L.leafletGeotiff = function (url, bounds, options) {
    return new L.LeafletGeotiff(url, bounds, options);
};
<commit_msg>Move depenency checks inside init.<commit_after>// Ideas from:
// https://github.com/ScanEx/Leaflet.imageTransform/blob/master/src/L.ImageTransform.js
// https://github.com/BenjaminVadant/leaflet-ugeojson

// Depends on:
// https://github.com/santilland/plotty
// https://github.com/constantinius/geotiff.js

// Note this will only work with ESPG:4326 tiffs

L.LeafletGeotiff = L.ImageOverlay.extend({
    initialize: function (url, options) {
        if(typeof(plotty)=='undefined'){
            throw new Error("plotty not defined");
        };

        if(typeof(GeoTIFF)=='undefined'){
            throw new Error("GeoTIFF not defined");
        };

        this.raster = {};
        if (options.bounds) {
            this._rasterBounds = L.latLngBounds(options.bounds);
        }
        L.Util.setOptions(this, options);

        this.options.colorScale = (options.colorScale==undefined) ? 'viridis' : options.colorScale;
        this.options.clampLow = (options.clampLow==undefined) ? true : options.clampLow;
        this.options.clampHigh = (options.clampHigh==undefined) ? true : options.clampHigh;
        this.options.arrowSize = (options.arrowSize==undefined) ? 20 : options.arrowSize;

        this._preLoadColorScale(); //Make sure colorScale is ready even if image takes a while to load
        this._getData(url);
    },
    setURL: function(newURL) {
        this._getData(newURL);
    },
    onAdd: function (map) {
        this._map = map;
        if (!this._image) {
            this._initImage();
        }

        map._panes.overlayPane.appendChild(this._image);

        map.on('moveend', this._reset, this);

        if (map.options.zoomAnimation && L.Browser.any3d) {
            map.on('zoomanim', this._animateZoom, this);
        }

        this._reset();
    },
    onRemove: function (map) {
        map.getPanes().overlayPane.removeChild(this._image);

        map.off('moveend', this._reset, this);

        if (map.options.zoomAnimation) {
            map.off('zoomanim', this._animateZoom, this);
        }
    },
    _getData: function(url) {
        var self = this;
        var request = new XMLHttpRequest();
        request.onload = function() {
            if (this.status >= 200 && this.status < 400) {
                self._parseTIFF(this.response);
            } //TODO else handle error
        };
        request.open("GET", url, true);
        request.responseType = "arraybuffer";
        request.send();
    },
    _parseTIFF: function (arrayBuffer) {
        this.tiff = GeoTIFF.parse(arrayBuffer);

        if (typeof(this.options.image)=='undefined') {
            this.options.image = 0;
        }
        if (typeof(this.options.band)=='undefined') {
            this.options.band = 0;
        }
        this.setBand(this.options.band);

        if (!this.options.bounds) {
            var image = this.tiff.getImage(this.options.image);
            var meta = image.getFileDirectory();
            var x_min = meta.ModelTiepoint[3];
            var x_max = x_min + meta.ModelPixelScale[0]*meta.ImageWidth;
            var y_min = meta.ModelTiepoint[4];
            var y_max = y_min - meta.ModelPixelScale[1]*meta.ImageLength;
            this._rasterBounds = L.latLngBounds([[y_min,x_min],[y_max,x_max]]);
            this._reset()
        }
    },
    setBand: function (band) {
        this.options.band = band

        var image = this.tiff.getImage(this.options.image);
        this.raster.data = image.readRasters({samples: [band]})[0];
        this.raster.width = image.getWidth();
        this.raster.height = image.getHeight();

        this._reset()
    },
    getRasterArray: function () {
        return this.raster.data;
    },
    getRasterCols: function () {
        return this.raster.width;
    },
    getRasterRows: function () {
        return this.raster.height;
    },
    setColorScale: function (colorScale) {
        this.options.colorScale = colorScale;
        this._reset();
    },
    setDisplayRange: function (min,max) {
        this.options.displayMin = min;
        this.options.displayMax = max;
        this._reset();
    },
    getValueAtLatLng: function (lat, lng) {
        try {
            var x = Math.floor(this.raster.width*(lng - this._rasterBounds._southWest.lng)/(this._rasterBounds._northEast.lng - this._rasterBounds._southWest.lng));
            var y = this.raster.height-Math.ceil(this.raster.height*(lat - this._rasterBounds._southWest.lat)/(this._rasterBounds._northEast.lat - this._rasterBounds._southWest.lat));
            var i = y*this.raster.width+x
            return this.raster.data[i];
        }
        catch(err) {
            return undefined;
        }
    },
	_animateZoom: function (e) {
        if (L.version >= "1.0") {
            var scale = this._map.getZoomScale(e.zoom),
                offset = this._map._latLngBoundsToNewLayerBounds(this._map.getBounds(), e.zoom, e.center).min;
            L.DomUtil.setTransform(this._image, offset, scale);
        } else {
            var scale = this._map.getZoomScale(e.zoom),
                nw = this._map.getBounds().getNorthWest(),
                se = this._map.getBounds().getSouthEast(),
                topLeft = this._map._latLngToNewLayerPoint(nw, e.zoom, e.center),
                size = this._map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft);
            this._image.style[L.DomUtil.TRANSFORM] =
		        L.DomUtil.getTranslateString(topLeft) + ' scale(' + scale + ') ';
        }
	},
    _reset: function () {
        if (this.hasOwnProperty('_map')) {
            if (this._rasterBounds) {
                topLeft = this._map.latLngToLayerPoint(this._map.getBounds().getNorthWest()),
                size = this._map.latLngToLayerPoint(this._map.getBounds().getSouthEast())._subtract(topLeft);

                L.DomUtil.setPosition(this._image, topLeft);
                this._image.style.width  = size.x + 'px';
                this._image.style.height = size.y + 'px';

                this._drawImage();
            };
        };
    },
    _preLoadColorScale: function () {
        var canvas = document.createElement('canvas');
        var plot = new plotty.plot({
            canvas: canvas, data: [0],
            width: 1, height: 1,
            domain: [this.options.displayMin, this.options.displayMax],
            colorScale: this.options.colorScale,
            clampLow: this.options.clampLow,
            clampHigh: this.options.clampHigh,
        });
        this.colorScaleData = plot.colorScaleCanvas.toDataURL();
    },
    setClip: function(clipLatLngs) {
        this.options.clip = clipLatLngs;
        this._reset();
    },
    _clipMaskToPixelPoints: function() {
        if (this.options.clip) {
            var topLeft = this._map.latLngToLayerPoint(this._map.getBounds().getNorthWest());
            var pixelClipPoints = [];
            for (var p = 0; p < this.options.clip.length; p++) {
                var mercPoint = this._map.latLngToLayerPoint(this.options.clip[p]),
                    pixel = L.point(mercPoint.x - topLeft.x, mercPoint.y - topLeft.y);
                pixelClipPoints.push(pixel);
            }
            this._pixelClipPoints = pixelClipPoints;
        } else {
            this._pixelClipPoints = undefined;
        }
    },
    _drawImage: function () {
        var self = this;
        if (self.raster.hasOwnProperty('data')) {
            topLeft = this._map.latLngToLayerPoint(this._map.getBounds().getNorthWest()),
            size = this._map.latLngToLayerPoint(this._map.getBounds().getSouthEast())._subtract(topLeft);
            var rasterPixelBounds = L.bounds(this._map.latLngToContainerPoint(this._rasterBounds.getNorthWest()),this._map.latLngToContainerPoint(this._rasterBounds.getSouthEast()))
            var xStart = (rasterPixelBounds.min.x>0 ? rasterPixelBounds.min.x : 0);
            var xFinish = (rasterPixelBounds.max.x<size.x ? rasterPixelBounds.max.x : size.x);
            var yStart = (rasterPixelBounds.min.y>0 ? rasterPixelBounds.min.y : 0);
            var yFinish = (rasterPixelBounds.max.y<size.y ? rasterPixelBounds.max.y : size.y);
            var plotWidth = xFinish-xStart;
            var plotHeight = yFinish-yStart;

            if ((plotWidth<=0) || (plotHeight<=0)) {
                console.log(this.options.name,' is off screen.');
                var plotCanvas = document.createElement("canvas");
                plotCanvas.width = size.x;
                plotCanvas.height = size.y;
                var ctx = plotCanvas.getContext("2d");
                ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height);
                this._image.src = plotCanvas.toDataURL();
                return;
            }

            var xOrigin = this._map.getPixelBounds().min.x+xStart;
            var yOrigin = this._map.getPixelBounds().min.y+yStart;
            var lngSpan = (this._rasterBounds._northEast.lng - this._rasterBounds._southWest.lng)/this.raster.width;
            var latSpan = (this._rasterBounds._northEast.lat - this._rasterBounds._southWest.lat)/this.raster.height;

            //Draw image data to canvas and pass to image element
            var plotCanvas = document.createElement("canvas");
            plotCanvas.width = size.x;
            plotCanvas.height = size.y;
            var ctx = plotCanvas.getContext("2d");
            ctx.clearRect(0, 0, plotCanvas.width, plotCanvas.height);

            if (this.options.vector==true) {
                var arrowSize = this.options.arrowSize;
                var zoom = this._map.getZoom();
                var gridPxelSize = (rasterPixelBounds.max.x - rasterPixelBounds.min.x) / self.raster.width;
                var stride = Math.max(1,Math.floor(1.2*arrowSize/gridPxelSize));

                for (var y=0;y<self.raster.height;y=y+stride) {
                    for (var x=0;x<self.raster.width;x=x+stride) {
                        var rasterIndex = (y*this.raster.width+x);
                        if (self.raster.data[rasterIndex]>=0) { //Ignore missing values
                            //calculate lat-lon of of this point
                            var currentLng = this._rasterBounds._southWest.lng + (x+0.5)*lngSpan;
                            var currentLat = this._rasterBounds._northEast.lat - (y+0.5)*latSpan;

                            //convert lat-lon to pixel cordinates
                            var projected = this._map.latLngToContainerPoint(L.latLng(currentLat,currentLng)); //If slow could unpack this calculation
                            var xProjected = projected.x;
                            var yProjected = projected.y;

                            //draw an arrow
                            ctx.save();
                            ctx.translate(xProjected, yProjected);
                            ctx.rotate((90+self.raster.data[rasterIndex])*Math.PI/180);
                            ctx.beginPath();
                            ctx.moveTo(-arrowSize/2, 0);
                            ctx.lineTo(+arrowSize/2, 0);
                            ctx.moveTo(arrowSize*0.25, -arrowSize*0.25);
                            ctx.lineTo(+arrowSize/2, 0);
                            ctx.lineTo(arrowSize*0.25, arrowSize*0.25);
                            ctx.stroke();
                            ctx.restore();
                        }
                    }
                }
            } else {
                var plottyCanvas = document.createElement("canvas");
                var plot = new plotty.plot({
                    data: self.raster.data,
                    width: self.raster.width, height: self.raster.height,
                    domain: [self.options.displayMin, self.options.displayMax],
                    colorScale: this.options.colorScale,
                    clampLow: this.options.clampLow,
                    clampHigh: this.options.clampHigh,
                    canvas: plottyCanvas,
                    useWebGL: false,
                });
                plot.setNoDataValue(-9999);
                plot.render();

                this.colorScaleData = plot.colorScaleCanvas.toDataURL();
                var rasterImageData = plottyCanvas.getContext("2d").getImageData(0,0,plottyCanvas.width, plottyCanvas.height);

                //Create image data and Uint32 views of data to speed up copying
                var imageData = new ImageData(plotWidth, plotHeight);
                var outData = imageData.data;
                var outPixelsU32 = new Uint32Array(outData.buffer);
                var inData = rasterImageData.data;
                var inPixelsU32 = new Uint32Array(inData.buffer);

                var zoom = this._map.getZoom();
                var scale = this._map.options.crs.scale(zoom);
                var d = 57.29577951308232; //L.LatLng.RAD_TO_DEG;

                var transformationA = this._map.options.crs.transformation._a;
                var transformationB = this._map.options.crs.transformation._b;
                var transformationC = this._map.options.crs.transformation._c;
                var transformationD = this._map.options.crs.transformation._d;
                if (L.version >= "1.0") {
                    transformationA = transformationA*this._map.options.crs.projection.R;
                    transformationC = transformationC*this._map.options.crs.projection.R;
                }

                for (var y=0;y<plotHeight;y++) {
                    var yUntransformed = ((yOrigin+y) / scale - transformationD) / transformationC;
                    var currentLat = (2 * Math.atan(Math.exp(yUntransformed)) - (Math.PI / 2)) * d;
                    var rasterY = this.raster.height-Math.ceil((currentLat - this._rasterBounds._southWest.lat)/latSpan);

                    for (var x=0;x<plotWidth;x++) {
                        //Location to draw to
                        var index = (y*plotWidth+x);

                        //Calculate lat-lng of (x,y)
                        //This code is based on leaflet code, unpacked to run as fast as possible
                        //Used to deal with TIF being EPSG:4326 (lat,lon) and map being EPSG:3857 (m E,m N)
                        var xUntransformed = ((xOrigin+x) / scale - transformationB) / transformationA;
                        var currentLng = xUntransformed * d;
                        var rasterX = Math.floor((currentLng - this._rasterBounds._southWest.lng)/lngSpan);

                        var rasterIndex = (rasterY*this.raster.width+rasterX);

                        //Copy pixel value
                        outPixelsU32[index] = inPixelsU32[rasterIndex];
                    }
                }
                ctx.putImageData(imageData, xStart, yStart);
            }

            //Draw clipping polygon
            if (this.options.clip) {
                this._clipMaskToPixelPoints();
                ctx.globalCompositeOperation = 'destination-out'
                ctx.rect(xStart-10,yStart-10,plotWidth+20,plotHeight+20);
                //Draw vertices in reverse order
                for (var i = this._pixelClipPoints.length-1; i >= 0; i--) {
                    var pix = this._pixelClipPoints[i];
                    ctx['lineTo'](pix.x, pix.y);
                }
                ctx.closePath();
                ctx.fill();
            }

            this._image.src = String(plotCanvas.toDataURL());
        }
    },
});

L.leafletGeotiff = function (url, bounds, options) {
    return new L.LeafletGeotiff(url, bounds, options);
};
<|endoftext|>


<commit_before>/* global d3, _ */
var url = hqImport('hqwebapp/js/initial_page_data').reverse;

function ImmunizationCoverageController($scope, $routeParams, $location, $filter, maternalChildService,
    locationsService, dateHelperService, navigationService, userLocationId, storageService, genders,
    haveAccessToAllLocations, baseControllersService, isAlertActive, isMobile, haveAccessToFeatures) {
    baseControllersService.BaseController.call(this, $scope, $routeParams, $location, locationsService,
        dateHelperService, navigationService, userLocationId, storageService, haveAccessToAllLocations,
        haveAccessToFeatures, isMobile);
    var vm = this;
    vm.isAlertActive = isAlertActive;
    vm.serviceDataFunction = maternalChildService.getImmunizationCoverageData;
    vm.haveAccessToFeatures = haveAccessToFeatures;
    var genderIndex = _.findIndex(genders, function (x) {
        return x.id === vm.filtersData.gender;
    });
    if (genderIndex !== -1) {
        vm.genderLabel = genders[genderIndex].name;
    }

    vm.label = "Immunization coverage (at age 1 year)";
    vm.steps = vm.getSteps('/maternal_and_child/immunization_coverage/');
    vm.data = {
        legendTitle: 'Percentage Children',
    };
    vm.filters = ['age', 'data_period'];
    vm.rightLegend = {
        info : 'Of the total number of children enrolled for Anganwadi Services who are between 1-2 years old, the percentage of children who have received the complete immunization as per the National Immunization Schedule of India that is required by age 1.

This includes the following immunizations:
If Pentavalent path: Penta1/2/3, OPV1/2/3, BCG, Measles, VitA1
If DPT/HepB path: DPT1/2/3, HepB1/2/3, OPV1/2/3, BCG, Measles, VitA1'
    };

    vm.getPopupData = function (row) {
        var gender = genderIndex > 0 ? genders[genderIndex].name : '';
        var chosenFilters = gender ? ' (' + gender + ') ' : '';
        var total = row ? $filter('indiaNumbers')(row.all) : 'N/A';
        var children = row ? $filter('indiaNumbers')(row.children) : 'N/A';
        var percent = row ? d3.format('.2%')(row.children / (row.all || 1)) : 'N/A';

        return [
            {
                indicator_name: 'Total number of ICDS Child beneficiaries between 1-2 years old' + chosenFilters + ': ',
                indicator_value: total,
            },
            {
                indicator_name: 'Total number of children between 1-2 years old who have received complete immunizations required by age 1' + chosenFilters + ': ',
                indicator_value: children,
            },
            {
                indicator_name: '% of children between 1-2 years old who have received complete immunizations required by age 1' + chosenFilters + ': ',
                indicator_value: percent,
            },
        ];
    };

    vm.init();

    vm.resetAdditionalFilter = function () {
        vm.filtersData.gender = '';
        $location.search('gender', null);
    };

    var options = {
        'xAxisTickFormat': '%b %Y',
        'yAxisTickFormat': ".2%",
        'captionContent': ' Of the total number of children enrolled for Anganwadi Services who are between 1-2 years old, the percentage of children who have received the complete immunization as per the National Immunization Schedule of India that is required by age 1.

This includes the following immunizations:
If Pentavalent path: Penta1/2/3, OPV1/2/3, BCG, Measles, VitA1
If DPT/HepB path: DPT1/2/3, HepB1/2/3, OPV1/2/3, BCG, Measles, VitA1',
    };
    vm.chartOptions = vm.getChartOptions(options);
    vm.chartOptions.chart.color = d3.scale.category10().range();

    vm.tooltipContent = function (monthName, dataInMonth) {
        return vm.createTooltipContent(
            monthName,
            [{
                indicator_name: 'Total number of ICDS Child beneficiaries between 1-2 years old: ',
                indicator_value: $filter('indiaNumbers')(dataInMonth.all),
            },
            {
                indicator_name: 'Total number of children between 1-2 years old who have received complete immunizations required by age 1: ',
                indicator_value: $filter('indiaNumbers')(dataInMonth.in_month),
            },
            {
                indicator_name: '% of children between 1-2 years old who have received complete immunizations required by age 1: ',
                indicator_value: d3.format('.2%')(dataInMonth.y),
            }]
        );
    };
}

ImmunizationCoverageController.$inject = [
    '$scope', '$routeParams', '$location', '$filter',
    'maternalChildService', 'locationsService', 'dateHelperService', 'navigationService',
    'userLocationId', 'storageService', 'genders', 'haveAccessToAllLocations', 'baseControllersService',
    'isAlertActive', 'isMobile', 'haveAccessToFeatures',
];

window.angular.module('icdsApp').directive('immunizationCoverage', ['templateProviderService', function (templateProviderService) {
    return {
        restrict: 'E',
        templateUrl: templateProviderService.getMapChartTemplate,
        bindToController: true,
        scope: {
            data: '=',
        },
        controller: ImmunizationCoverageController,
        controllerAs: '$ctrl',
    };
}]);
<commit_msg>lint changes<commit_after>/* global d3, _ */
var url = hqImport('hqwebapp/js/initial_page_data').reverse;

function ImmunizationCoverageController($scope, $routeParams, $location, $filter, maternalChildService,
    locationsService, dateHelperService, navigationService, userLocationId, storageService, genders,
    haveAccessToAllLocations, baseControllersService, isAlertActive, isMobile, haveAccessToFeatures) {
    baseControllersService.BaseController.call(this, $scope, $routeParams, $location, locationsService,
        dateHelperService, navigationService, userLocationId, storageService, haveAccessToAllLocations,
        haveAccessToFeatures, isMobile);
    var vm = this;
    vm.isAlertActive = isAlertActive;
    vm.serviceDataFunction = maternalChildService.getImmunizationCoverageData;
    vm.haveAccessToFeatures = haveAccessToFeatures;
    var genderIndex = _.findIndex(genders, function (x) {
        return x.id === vm.filtersData.gender;
    });
    if (genderIndex !== -1) {
        vm.genderLabel = genders[genderIndex].name;
    }

    vm.label = "Immunization coverage (at age 1 year)";
    vm.steps = vm.getSteps('/maternal_and_child/immunization_coverage/');
    vm.data = {
        legendTitle: 'Percentage Children',
    };
    vm.filters = ['age', 'data_period'];
    vm.rightLegend = {
        info: 'Of the total number of children enrolled for Anganwadi Services who are between 1-2 years old, the percentage of children who have received the complete immunization as per the National Immunization Schedule of India that is required by age 1.

This includes the following immunizations:
If Pentavalent path: Penta1/2/3, OPV1/2/3, BCG, Measles, VitA1
If DPT/HepB path: DPT1/2/3, HepB1/2/3, OPV1/2/3, BCG, Measles, VitA1',
    };

    vm.getPopupData = function (row) {
        var gender = genderIndex > 0 ? genders[genderIndex].name : '';
        var chosenFilters = gender ? ' (' + gender + ') ' : '';
        var total = row ? $filter('indiaNumbers')(row.all) : 'N/A';
        var children = row ? $filter('indiaNumbers')(row.children) : 'N/A';
        var percent = row ? d3.format('.2%')(row.children / (row.all || 1)) : 'N/A';

        return [
            {
                indicator_name: 'Total number of ICDS Child beneficiaries between 1-2 years old' + chosenFilters + ': ',
                indicator_value: total,
            },
            {
                indicator_name: 'Total number of children between 1-2 years old who have received complete immunizations required by age 1' + chosenFilters + ': ',
                indicator_value: children,
            },
            {
                indicator_name: '% of children between 1-2 years old who have received complete immunizations required by age 1' + chosenFilters + ': ',
                indicator_value: percent,
            },
        ];
    };

    vm.init();

    vm.resetAdditionalFilter = function () {
        vm.filtersData.gender = '';
        $location.search('gender', null);
    };

    var options = {
        'xAxisTickFormat': '%b %Y',
        'yAxisTickFormat': ".2%",
        'captionContent': ' Of the total number of children enrolled for Anganwadi Services who are between 1-2 years old, the percentage of children who have received the complete immunization as per the National Immunization Schedule of India that is required by age 1.

This includes the following immunizations:
If Pentavalent path: Penta1/2/3, OPV1/2/3, BCG, Measles, VitA1
If DPT/HepB path: DPT1/2/3, HepB1/2/3, OPV1/2/3, BCG, Measles, VitA1',
    };
    vm.chartOptions = vm.getChartOptions(options);
    vm.chartOptions.chart.color = d3.scale.category10().range();

    vm.tooltipContent = function (monthName, dataInMonth) {
        return vm.createTooltipContent(
            monthName,
            [{
                indicator_name: 'Total number of ICDS Child beneficiaries between 1-2 years old: ',
                indicator_value: $filter('indiaNumbers')(dataInMonth.all),
            },
            {
                indicator_name: 'Total number of children between 1-2 years old who have received complete immunizations required by age 1: ',
                indicator_value: $filter('indiaNumbers')(dataInMonth.in_month),
            },
            {
                indicator_name: '% of children between 1-2 years old who have received complete immunizations required by age 1: ',
                indicator_value: d3.format('.2%')(dataInMonth.y),
            }]
        );
    };
}

ImmunizationCoverageController.$inject = [
    '$scope', '$routeParams', '$location', '$filter',
    'maternalChildService', 'locationsService', 'dateHelperService', 'navigationService',
    'userLocationId', 'storageService', 'genders', 'haveAccessToAllLocations', 'baseControllersService',
    'isAlertActive', 'isMobile', 'haveAccessToFeatures',
];

window.angular.module('icdsApp').directive('immunizationCoverage', ['templateProviderService', function (templateProviderService) {
    return {
        restrict: 'E',
        templateUrl: templateProviderService.getMapChartTemplate,
        bindToController: true,
        scope: {
            data: '=',
        },
        controller: ImmunizationCoverageController,
        controllerAs: '$ctrl',
    };
}]);
<|endoftext|>


<commit_before>//////
// TODO
// Simple machine learning algo: Based on types -> give statistically most likely category
// Example: Calcium has types
// [ 'substance',
// 'Biologically Active Substance',
// 'Element, Ion, or Isotope',
// 'Chemical/Ingredient',
// 'Pharmacologic Substance',
// 'medication' ]
//
// And given Chemical/Ingredient and substance it's most likely a labresult.

// Default type: `keyword`
module.exports = function getCategoryByTypes(types) {
    if (!types || typeof types === "undefined" || types.length === 0) {
        return "keyword";
***REMOVED***

    var dbc        = ["DBC", "dbc", "zorgproduct"];
    var diagnosis  = ["disorder", "Disease or Syndrome", "diagnosis", "Disease/Finding"];
    var medication = ["medication", "Pharmacologic Substance", "product"];
    var labresult  = ["Finding", "Laboratory Procedure", "Laboratory or Test Result", "Chemical/Ingredient"];

    return inList(types, dbc, "dbc")               ||
           inList(types, diagnosis, "diagnosis")   ||
           inList(types, medication, "medication") ||
           inList(types, labresult, "labresult")   ||
           "keyword";
***REMOVED***

function inList(haystack, search, type) {
    for (var i=0; i<search.length; i++) {
        if (haystack.indexOf(search[i]) >= 0) {
            return type;
    ***REMOVED***
***REMOVED***

    return false;
***REMOVED***
<commit_msg>No diagnosis category<commit_after>//////
// TODO
// Simple machine learning algo: Based on types -> give statistically most likely category
// Example: Calcium has types
// [ 'substance',
// 'Biologically Active Substance',
// 'Element, Ion, or Isotope',
// 'Chemical/Ingredient',
// 'Pharmacologic Substance',
// 'medication' ]
//
// And given Chemical/Ingredient and substance it's most likely a labresult.

// Default type: `keyword`
module.exports = function getCategoryByTypes(types) {
    if (!types || typeof types === "undefined" || types.length === 0) {
        return "keyword";
***REMOVED***

    var dbc        = ["DBC", "dbc", "zorgproduct"];
    var medication = ["medication", "Pharmacologic Substance", "product"];
    var labresult  = ["Finding", "Laboratory Procedure", "Laboratory or Test Result", "Chemical/Ingredient"];

    return inList(types, dbc, "dbc")               ||
           inList(types, medication, "medication") ||
           inList(types, labresult, "labresult")   ||
           "keyword";
***REMOVED***

function inList(haystack, search, type) {
    for (var i=0; i<search.length; i++) {
        if (haystack.indexOf(search[i]) >= 0) {
            return type;
    ***REMOVED***
***REMOVED***

    return false;
***REMOVED***
<|endoftext|>


<commit_before>/**
 * Default settings
 */
window.settings = window.settings || {
    SERVER: "localhost:8000",
    TILES: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
};

/** Until the 42 issues and pull requests are fixed in Leaflet core */
L.extend(L.GeoJSON, {
    latLngsToCoords: function (layer) {
        var coords = function coords (latlng) {
            return [latlng.lng, latlng.lat];
        };
        if (layer.getLatLng) {
            return coords(layer.getLatLng());
        }
        else if (layer instanceof L.Polygon) {
            // A polygon can theorically have holes, thus list of rings.
            return [_.map(layer.getLatLngs(), function (latlng) {
                return coords(latlng);
            })];
        }
        else if (layer instanceof L.Polyline) {
            return _.map(layer.getLatLngs(), function (latlng) {return coords(latlng);});
        }
        else throw "Could not export layer coordinates";
    },
});

var Item = Backbone.Model.extend({
    popup: function () {
        var tpl = this.definition.templatePopup();
        return tpl(this.toJSON());
    },

    getLayer: function () {
        if (!this.layer) {
            var geomfield = this.definition.geomField();
            if (!geomfield) return;

            var factories = {
                'point': function (coords) {return L.circleMarker([coords[1], coords[0]]);},
                'line': function (coords) {return L.polyline(L.GeoJSON.coordsToLatLngs(coords));},
                'polygon': function (coords) {return L.polygon(L.GeoJSON.coordsToLatLngs(coords[0]));},
            };
            var coords = this.get(geomfield.name);
            if (typeof coords === 'string') {
                coords = JSON.parse(coords);
            }
            this.layer = factories[geomfield.type](coords);
        }
        return this.layer;
    },

    setLayer: function (layer) {
        var geomfield = this.definition.geomField();
        if (!geomfield) return;

        var coords = L.GeoJSON.latLngsToCoords(layer)
          , attrs = {};
        attrs[geomfield.name] = JSON.stringify(coords);
        this.set(attrs);
    },
});

var ItemList = Backbone.Collection.extend({
    model: Item,

    initialize: function (definition) {
        this.definition = definition;
    },

    url: function () {
        return URI.build({hostname:settings.SERVER, path: '/data/' + this.definition.id});
    },

    parse: function(response) {
        return response.data;
    },

    /**
     * Override instanciation to link with Definition instance.
     */
    _prepareModel: function() {
        var m = Backbone.Collection.prototype._prepareModel.apply(this, arguments);
        m.definition = this.definition;
        return m;
    },
});

var Definition = Backbone.Model.extend({
    url: function () {
        return URI.build({hostname:settings.SERVER, path: 'definitions/' + this.id});
    },

    schema: {
        id:  { type: 'Hidden', title: '' },
        title: 'Text',
        description: 'Text',
        fields: { type: 'List', itemType: 'Object', subSchema: {
            name: { validators: ['required'] },
            description: { validators: ['required'] },
            type: { type: 'Select', options: ['int', 'string', 'decimal', 'boolean',
                                              'email', 'url', 'point', 'line', 'polygon'] }
        }}
    },

    isReady: function () {
        return this.attributes.fields && this.attributes.fields.length > 0;
    },

    whenReady: function (cb) {
        if (this.isReady())
            cb();
        else {
            this.once('change', function () {
                this.whenReady(cb);
            }, this);
        }
    },

    itemSchema: function () {
        if (!this.isReady())
            throw "Definition is not ready. Fetch it first.";
        var typeMapping = {
            'int': 'Number',
            'string': 'Text',
            'boolean': 'Checkbox'
        }
        var geom = function (f) {
            return {type: 'TextArea',
                    editorAttrs: {style: 'display: none'},
                    help: f.description + ' (on map)'};
        };
        var fieldMapping = {
            'default': function (f) {
                var d = {help: f.description}
                  , t = typeMapping[f.type];
                if (t) d.type = t;
                return d;
            },
            'decimal': function (f, d) {
                d.editorAttrs = {pattern: '[-+]?[0-9]*\.?[0-9]+'};
                return d;
            },
            'email': function (f, d) {
                d.validators = ['required', 'email'];
                return d;
            },
            'url': function (f, d) {
                d.validators = ['required', 'url'];
                return d;
            },
            'point': geom,
            'line': geom,
            'polygon': geom,
        };
        var self = this
          , schema = {};
        // Add Backbone.Forms fields from Daybed definition
        $(this.attributes.fields).each(function (i, field) {
            var defaultschema = fieldMapping['default']
              , build = fieldMapping[field.type] || defaultschema;
            schema[field.name] = build(field);
        });
        return schema;
    },

    mainFields: function () {
        var geomField = this.geomField();
        if (!geomField)
            return this.attributes.fields;
        return this.attributes.fields.filter(function (f) {
            return f.name != geomField.name;
        });
    },

    /**
     * Returns the first field whose type is Geometry.
     */
    geomField: function () {
        for (i in this.attributes.fields) {
            var f = this.attributes.fields[i];
            if (f.type == 'point' || f.type == 'line' || f.type == 'polygon')
                return f;
        }
        return null;
    },

    templatePopup: function () {
        var c = '
';
        $(this.mainFields()).each(function (i, f) {
            c += '
{{ ' + f.name + ' }}
'
        });
        c += '
';
        return Mustache.compile(c);
    },

    tableContent: function () {
        var tpl = '

' +
                  '{{#fields}}{{name}}	{{/fields}}'+
                  '

';
        return Mustache.compile(tpl)({fields: this.mainFields()});
    },

    templateRow: function () {
        var c = '
';
        $(this.mainFields()).each(function (i, f) {
            c += '{{ ' + f.name + ' }}	'
        });
        c += '
';
        return Mustache.compile(c);
    },
});

var FormView = Backbone.View.extend({
    model: null,
    tagName: "div",
    className: "well",

    template: Mustache.compile('
{{ title }}
' +
                               '

' +
                               'Cancel ' +
                               'Save'),
    templateError: Mustache.compile('{{ msg }}'),

    events: {
        "click #submit": "submit",
        "click #cancel": "cancel",
    },

    initialize: function () {
        this.instance = new this.model({});
        if (!this.instance.schema && this.options.definition) {
            this.instance.definition = this.options.definition;
            this.instance.schema = this.options.definition.itemSchema();
        }
        this.instance.on('change', this.refresh.bind(this));
        this.instance.on('sync', this.success.bind(this));
        this.instance.on('error', this.showErrors.bind(this));
        this.form = new Backbone.Form({
            model: this.instance
        });
    },

    render: function () {
        this.$el.html(this.template(this));
        this.$('#form').html(this.form.render().el);
        this.delegateEvents();
        return this;
    },

    cancel: function (e) {
        e.preventDefault();
        return false;
    },

    submit: function(e) {
        e.preventDefault();
        this.$el.find('.field-error').remove();
        this.form.commit();
        return false;
    },

    success: function (model, response, options) {
        return false;
    },

    showErrors: function (model, xhr, options) {
        try {
            var descriptions = JSON.parse(xhr.responseText),
                self = this;
            $(descriptions.errors).each(function (i, e) {
                var name = e.name.split('.')[0];
                self.$el.find("[name='" + name + "']")
                    .after(self.templateError({msg: e.description}));
            });
        }
        catch (e) {
            this.$el.html(this.templateError({msg: xhr.responseText}));
        }
    },

    /**
     * Refresh field values from instance attributes.
     */
    refresh: function () {
        for (f in this.instance.changed) {
            if (f == 'id') continue;
            var formfield = this.$('[name='+ f + ']');
            if (formfield.length == 0)
                console.warn("Could not find form field '" + f + "'");
            formfield.val(this.instance.get(f));
        }
    }
});
<commit_msg>Clearer calls of field mapping<commit_after>/**
 * Default settings
 */
window.settings = window.settings || {
    SERVER: "localhost:8000",
    TILES: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
};

/** Until the 42 issues and pull requests are fixed in Leaflet core */
L.extend(L.GeoJSON, {
    latLngsToCoords: function (layer) {
        var coords = function coords (latlng) {
            return [latlng.lng, latlng.lat];
        };
        if (layer.getLatLng) {
            return coords(layer.getLatLng());
        }
        else if (layer instanceof L.Polygon) {
            // A polygon can theorically have holes, thus list of rings.
            return [_.map(layer.getLatLngs(), function (latlng) {
                return coords(latlng);
            })];
        }
        else if (layer instanceof L.Polyline) {
            return _.map(layer.getLatLngs(), function (latlng) {return coords(latlng);});
        }
        else throw "Could not export layer coordinates";
    },
});

var Item = Backbone.Model.extend({
    popup: function () {
        var tpl = this.definition.templatePopup();
        return tpl(this.toJSON());
    },

    getLayer: function () {
        if (!this.layer) {
            var geomfield = this.definition.geomField();
            if (!geomfield) return;

            var factories = {
                'point': function (coords) {return L.circleMarker([coords[1], coords[0]]);},
                'line': function (coords) {return L.polyline(L.GeoJSON.coordsToLatLngs(coords));},
                'polygon': function (coords) {return L.polygon(L.GeoJSON.coordsToLatLngs(coords[0]));},
            };
            var coords = this.get(geomfield.name);
            if (typeof coords === 'string') {
                coords = JSON.parse(coords);
            }
            this.layer = factories[geomfield.type](coords);
        }
        return this.layer;
    },

    setLayer: function (layer) {
        var geomfield = this.definition.geomField();
        if (!geomfield) return;

        var coords = L.GeoJSON.latLngsToCoords(layer)
          , attrs = {};
        attrs[geomfield.name] = JSON.stringify(coords);
        this.set(attrs);
    },
});

var ItemList = Backbone.Collection.extend({
    model: Item,

    initialize: function (definition) {
        this.definition = definition;
    },

    url: function () {
        return URI.build({hostname:settings.SERVER, path: '/data/' + this.definition.id});
    },

    parse: function(response) {
        return response.data;
    },

    /**
     * Override instanciation to link with Definition instance.
     */
    _prepareModel: function() {
        var m = Backbone.Collection.prototype._prepareModel.apply(this, arguments);
        m.definition = this.definition;
        return m;
    },
});

var Definition = Backbone.Model.extend({
    url: function () {
        return URI.build({hostname:settings.SERVER, path: 'definitions/' + this.id});
    },

    schema: {
        id:  { type: 'Hidden', title: '' },
        title: 'Text',
        description: 'Text',
        fields: { type: 'List', itemType: 'Object', subSchema: {
            name: { validators: ['required'] },
            description: { validators: ['required'] },
            type: { type: 'Select', options: ['int', 'string', 'decimal', 'boolean',
                                              'email', 'url', 'point', 'line', 'polygon'] }
        }}
    },

    isReady: function () {
        return this.attributes.fields && this.attributes.fields.length > 0;
    },

    whenReady: function (cb) {
        if (this.isReady())
            cb();
        else {
            this.once('change', function () {
                this.whenReady(cb);
            }, this);
        }
    },

    itemSchema: function () {
        if (!this.isReady())
            throw "Definition is not ready. Fetch it first.";
        var typeMapping = {
            'int': 'Number',
            'string': 'Text',
            'boolean': 'Checkbox'
        }
        var geom = function (f) {
            return {type: 'TextArea',
                    editorAttrs: {style: 'display: none'},
                    help: f.description + ' (on map)'};
        };
        var fieldMapping = {
            'default': function (f) {
                var d = {help: f.description}
                  , t = typeMapping[f.type];
                if (t) d.type = t;
                return d;
            },
            'decimal': function (f) {
                var d = fieldMapping['default'](field);
                d.editorAttrs = {pattern: '[-+]?[0-9]*\.?[0-9]+'};
                return d;
            },
            'email': function (f) {
                var d = fieldMapping['default'](field);
                d.validators = ['required', 'email'];
                return d;
            },
            'url': function (f) {
                var d = fieldMapping['default'](field);
                d.validators = ['required', 'url'];
                return d;
            },
            'point': geom,
            'line': geom,
            'polygon': geom,
        };
        var self = this
          , schema = {};
        // Add Backbone.Forms fields from Daybed definition
        $(this.attributes.fields).each(function (i, field) {
            var defaultschema = fieldMapping['default']
              , build = fieldMapping[field.type] || defaultschema;
            schema[field.name] = build(field);
        });
        return schema;
    },

    mainFields: function () {
        var geomField = this.geomField();
        if (!geomField)
            return this.attributes.fields;
        return this.attributes.fields.filter(function (f) {
            return f.name != geomField.name;
        });
    },

    /**
     * Returns the first field whose type is Geometry.
     */
    geomField: function () {
        for (i in this.attributes.fields) {
            var f = this.attributes.fields[i];
            if (f.type == 'point' || f.type == 'line' || f.type == 'polygon')
                return f;
        }
        return null;
    },

    templatePopup: function () {
        var c = '
';
        $(this.mainFields()).each(function (i, f) {
            c += '
{{ ' + f.name + ' }}
'
        });
        c += '
';
        return Mustache.compile(c);
    },

    tableContent: function () {
        var tpl = '

' +
                  '{{#fields}}{{name}}	{{/fields}}'+
                  '

';
        return Mustache.compile(tpl)({fields: this.mainFields()});
    },

    templateRow: function () {
        var c = '
';
        $(this.mainFields()).each(function (i, f) {
            c += '{{ ' + f.name + ' }}	'
        });
        c += '
';
        return Mustache.compile(c);
    },
});

var FormView = Backbone.View.extend({
    model: null,
    tagName: "div",
    className: "well",

    template: Mustache.compile('
{{ title }}
' +
                               '

' +
                               'Cancel ' +
                               'Save'),
    templateError: Mustache.compile('{{ msg }}'),

    events: {
        "click #submit": "submit",
        "click #cancel": "cancel",
    },

    initialize: function () {
        this.instance = new this.model({});
        if (!this.instance.schema && this.options.definition) {
            this.instance.definition = this.options.definition;
            this.instance.schema = this.options.definition.itemSchema();
        }
        this.instance.on('change', this.refresh.bind(this));
        this.instance.on('sync', this.success.bind(this));
        this.instance.on('error', this.showErrors.bind(this));
        this.form = new Backbone.Form({
            model: this.instance
        });
    },

    render: function () {
        this.$el.html(this.template(this));
        this.$('#form').html(this.form.render().el);
        this.delegateEvents();
        return this;
    },

    cancel: function (e) {
        e.preventDefault();
        return false;
    },

    submit: function(e) {
        e.preventDefault();
        this.$el.find('.field-error').remove();
        this.form.commit();
        return false;
    },

    success: function (model, response, options) {
        return false;
    },

    showErrors: function (model, xhr, options) {
        try {
            var descriptions = JSON.parse(xhr.responseText),
                self = this;
            $(descriptions.errors).each(function (i, e) {
                var name = e.name.split('.')[0];
                self.$el.find("[name='" + name + "']")
                    .after(self.templateError({msg: e.description}));
            });
        }
        catch (e) {
            this.$el.html(this.templateError({msg: xhr.responseText}));
        }
    },

    /**
     * Refresh field values from instance attributes.
     */
    refresh: function () {
        for (f in this.instance.changed) {
            if (f == 'id') continue;
            var formfield = this.$('[name='+ f + ']');
            if (formfield.length == 0)
                console.warn("Could not find form field '" + f + "'");
            formfield.val(this.instance.get(f));
        }
    }
});
<|endoftext|>


<commit_before>const TipoEnte = require("../src/TipoEnte");
const CatalogoOuvidorias = artifacts.require("./CatalogoOuvidorias.sol");

/* FUNCOES UTILITARIAS */

function clonarDebugConvertendoBytes32(log) {
    function bytes32ToString(hexx) {
        const hex = hexx.toString();
        let str = '';
        for (let i = 0; i < hex.length; i += 2)
            str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
        return str.replace(/\u0000/g, '');
    }
    let retorno = Object.assign({}, log.args);
    retorno.texto = bytes32ToString(retorno.texto);
    return retorno;
}

//noinspection JSUnusedLocalSymbols
function printDebugs(resultadoTransacao) {
    resultadoTransacao.logs.forEach((log) => console.log(clonarDebugConvertendoBytes32(log)));
}

function getPrimeiroDebug(resultadoTransacao) {
    return clonarDebugConvertendoBytes32(resultadoTransacao.logs[0]);
}

function assertPrimeiraOuvidoria(contractInstance, _account, _nome, _enteTipo, _enteNome, _endpoint) {
    return contractInstance.then((co) => {
        let primeiraOuvidoriaAccount = _account;
        let primeiraOuvidoria = {};
        return co.getOuvidoriaNome(primeiraOuvidoriaAccount).then((nome) => {
            primeiraOuvidoria.nome = nome;
            return co.getOuvidoriaEnteTipo(primeiraOuvidoriaAccount);
        }).then((enteTipo) => {
            primeiraOuvidoria.enteTipo = enteTipo;
            return co.getOuvidoriaEnteNome(primeiraOuvidoriaAccount);
        }).then((enteNome) => {
            primeiraOuvidoria.enteNome = enteNome;
            return co.getOuvidoriaEndpoint(primeiraOuvidoriaAccount);
        }).then((ouvidoriaEndpoint) => {
            primeiraOuvidoria.ouvidoriaEndpoint = ouvidoriaEndpoint;

            assert.equal(primeiraOuvidoria.nome, _nome);
            assert.equal(TipoEnte[primeiraOuvidoria.enteTipo.c[0]], _enteTipo);
            assert.equal(primeiraOuvidoria.enteNome, _enteNome);
            assert.equal(primeiraOuvidoria.ouvidoriaEndpoint, _endpoint);
        });
    });
}

contract('CatalogoOuvidorias', (accounts) => {

    it("script de deploy padrao constroi corretamente o contrato inicial", () => {
        let contractInstance = CatalogoOuvidorias.deployed();

        return assertPrimeiraOuvidoria(
            contractInstance,
            accounts[0],
            'CGU',
            'Uniao',
            'Uniao',
            'http://cgu.gov.br/ouv'
        );
    });

    describe("CatalogoOuvidorias criado em testes", () => {
        let nomeOuvidoria = "CGU-DF";
        let enteTipoCodigo = 1;
        let enteTipoPorExtenso = 'Estado/DF';
        let enteNome = "DF";
        let endpoint = "http://cgu.gov.br/ouv-df";

        let catalogoOuvidoriasPromise;

        let primeiraAccount = accounts[0];
        let segundaAccount = accounts[1];

        beforeEach(() => {
            catalogoOuvidoriasPromise = CatalogoOuvidorias.new(nomeOuvidoria, enteTipoCodigo, enteNome, endpoint);
        });

        it("construtor cria corretamente catalogo de testes", () => {
            return assertPrimeiraOuvidoria(
                catalogoOuvidoriasPromise,
                primeiraAccount,
                nomeOuvidoria,
                enteTipoPorExtenso,
                enteNome,
                endpoint
            );
        });

        it("ouvidoria jah cadastrada pode chamar autorizar", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount});
            }).then((resultadoTransacao) => {
                assert.deepEqual(
                    getPrimeiroDebug(resultadoTransacao),
                    {
                        endereco: primeiraAccount,
                        texto: "autorizar",
                        booleano: false
                    }
                );
            });
        });

        it("ouvidoria nao cadastrada NAO pode chamar autorizar", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(primeiraAccount, {from: segundaAccount});
            }).then(() => {
                assert.fail("segunda account nao estah cadastrada, portanto deveria dar erro")
            }).catch((erro) => {
                assert.equal(erro.message, 'VM Exception while processing transaction: invalid opcode');
            });
        });

        it("ouvidoria jah cadastrada nao pode autorizar uma outra ouvidoria mais de uma vez", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount}).then(() => {
                    return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount});
                }).then(() => {
                    assert.fail("1a account jah autorizou a 2a account, entao deveria dar erro")
                }).catch((erro) => {
                    assert.equal(erro.message, 'VM Exception while processing transaction: invalid opcode');
                });
            })
        });

        xit("ouvidoria jah cadastrada pode autorizar duas outras ouvidorias diferentes", () => {});
        xit("ouvidoria jah cadastrada nao pode autorizar uma outra ouvidoria jah cadastrada", () => {});

    });

});
<commit_msg>test(catalogo): incluir teste em que ouvidoria cadastrada consegue autorizar duas candidatas diferentes<commit_after>const TipoEnte = require("../src/TipoEnte");
const CatalogoOuvidorias = artifacts.require("./CatalogoOuvidorias.sol");

/* FUNCOES UTILITARIAS */

function clonarDebugConvertendoBytes32(log) {
    function bytes32ToString(hexx) {
        const hex = hexx.toString();
        let str = '';
        for (let i = 0; i < hex.length; i += 2)
            str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
        return str.replace(/\u0000/g, '');
    }
    let retorno = Object.assign({}, log.args);
    retorno.texto = bytes32ToString(retorno.texto);
    return retorno;
}

//noinspection JSUnusedLocalSymbols
function printDebugs(resultadoTransacao) {
    resultadoTransacao.logs.forEach((log) => console.log(clonarDebugConvertendoBytes32(log)));
}

function getPrimeiroDebug(resultadoTransacao) {
    return clonarDebugConvertendoBytes32(resultadoTransacao.logs[0]);
}

function assertPrimeiraOuvidoria(contractInstance, _account, _nome, _enteTipo, _enteNome, _endpoint) {
    return contractInstance.then((co) => {
        let primeiraOuvidoriaAccount = _account;
        let primeiraOuvidoria = {};
        return co.getOuvidoriaNome(primeiraOuvidoriaAccount).then((nome) => {
            primeiraOuvidoria.nome = nome;
            return co.getOuvidoriaEnteTipo(primeiraOuvidoriaAccount);
        }).then((enteTipo) => {
            primeiraOuvidoria.enteTipo = enteTipo;
            return co.getOuvidoriaEnteNome(primeiraOuvidoriaAccount);
        }).then((enteNome) => {
            primeiraOuvidoria.enteNome = enteNome;
            return co.getOuvidoriaEndpoint(primeiraOuvidoriaAccount);
        }).then((ouvidoriaEndpoint) => {
            primeiraOuvidoria.ouvidoriaEndpoint = ouvidoriaEndpoint;

            assert.equal(primeiraOuvidoria.nome, _nome);
            assert.equal(TipoEnte[primeiraOuvidoria.enteTipo.c[0]], _enteTipo);
            assert.equal(primeiraOuvidoria.enteNome, _enteNome);
            assert.equal(primeiraOuvidoria.ouvidoriaEndpoint, _endpoint);
        });
    });
}

function assertPrimeiroDebug(resultadoTransacao, debugEsperado) {
    assert.deepEqual(
        getPrimeiroDebug(resultadoTransacao),
        debugEsperado
    );
}
contract('CatalogoOuvidorias', (accounts) => {

    it("script de deploy padrao constroi corretamente o contrato inicial", () => {
        let contractInstance = CatalogoOuvidorias.deployed();

        return assertPrimeiraOuvidoria(
            contractInstance,
            accounts[0],
            'CGU',
            'Uniao',
            'Uniao',
            'http://cgu.gov.br/ouv'
        );
    });

    describe("CatalogoOuvidorias criado em testes", () => {
        let nomeOuvidoria = "CGU-DF";
        let enteTipoCodigo = 1;
        let enteTipoPorExtenso = 'Estado/DF';
        let enteNome = "DF";
        let endpoint = "http://cgu.gov.br/ouv-df";

        let catalogoOuvidoriasPromise;

        let primeiraAccount = accounts[0];
        let segundaAccount = accounts[1];
        let terceiraAccount = accounts[2];

        beforeEach(() => {
            catalogoOuvidoriasPromise = CatalogoOuvidorias.new(nomeOuvidoria, enteTipoCodigo, enteNome, endpoint);
        });

        it("construtor cria corretamente catalogo de testes", () => {
            return assertPrimeiraOuvidoria(
                catalogoOuvidoriasPromise,
                primeiraAccount,
                nomeOuvidoria,
                enteTipoPorExtenso,
                enteNome,
                endpoint
            );
        });

        it("ouvidoria jah cadastrada pode chamar autorizar", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount});
            }).then((resultadoTransacao) => {
                assertPrimeiroDebug(
                    resultadoTransacao,
                    {
                        endereco: primeiraAccount,
                        texto: "autorizar",
                        booleano: false
                    }
                );
            });
        });

        it("ouvidoria nao cadastrada NAO pode chamar autorizar", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(primeiraAccount, {from: segundaAccount});
            }).then(() => {
                assert.fail("segunda account nao estah cadastrada, portanto deveria dar erro")
            }).catch((erro) => {
                assert.equal(erro.message, 'VM Exception while processing transaction: invalid opcode');
            });
        });

        it("ouvidoria jah cadastrada nao pode autorizar uma outra ouvidoria mais de uma vez", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount}).then(() => {
                    return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount});
                }).then(() => {
                    assert.fail("1a account jah autorizou a 2a account, entao deveria dar erro")
                }).catch((erro) => {
                    assert.equal(erro.message, 'VM Exception while processing transaction: invalid opcode');
                });
            })
        });

        it("ouvidoria jah cadastrada pode autorizar duas outras ouvidorias diferentes", () => {
            return catalogoOuvidoriasPromise.then((catalogoOuvidorias) => {
                return catalogoOuvidorias.autorizar(segundaAccount, {from: primeiraAccount}).then(() => {
                    return catalogoOuvidorias.autorizar(terceiraAccount, {from: primeiraAccount});
                }).then((resultadoTransacao) => {
                    assertPrimeiroDebug(
                        resultadoTransacao,
                        {
                            endereco: primeiraAccount,
                            texto: "autorizar",
                            booleano: false
                        }
                    );
                });
            })
        });

        xit("ouvidoria jah cadastrada nao pode autorizar uma outra ouvidoria jah cadastrada", () => {});

    });

});
<|endoftext|>


<commit_before>import * as actions from './actionConstants';
import {browserHistory} from 'react-router';

export const addEmail = (email) => {
  return {
    type: actions.LOGIN_EMAIL,
    email
  }
};

export const addPassword = (password) => {
  return {
    type: actions.LOGIN_PASSWORD,
    password
  }
};

export const submitLogin = () => {
  return {
    type: actions.LOGIN_SUBMIT
  }
}

export const loginSuccess = (user) => {
  browserHistory.push('/dashboard');
  return {
    type: actions.LOGIN_SUCCESS,
    user
  }
};

export const loginFailure = (error) => {
  return {
    type: actions.LOGIN_FAILURE,
    error
  }
};

export const postLogin = (user) => {
  return (dispatch, getState) => {
    let state = getState();
    if(!state.postingLogin){
      dispatch(submitLogin());
      fetch('/auth/login', {
        method: 'post',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(user)
      })
      .then((response) => {
        if (response.status >= 200 && response.status < 300){
          dispatch(loginSuccess(response.user));
        }
        else {
          let error = new Error(response.statusText);
          dispatch(loginFailure(error));
        }
      })
    }
  }
}

export const addSignupFirstName = (firstName) => {
  return {
    type: actions.SIGNUP_FIRST_NAME,
    firstName
  }
};

export const addSignupLastName = (lastName) => {
  return {
    type: actions.SIGNUP_LAST_NAME,
    lastName
  }
};

export const submitSignup = () => {
  return {
    type: actions.SIGNUP_SUBMIT
  }
};

export const signupSuccess = (user) => {
  browserHistory.push('/dashboard');
  return {
    type: actions.SIGNUP_SUCCESS,
    user
  }
};

export const signupFailure = (error) => {
  return {
    type: actions.SIGNUP_FAILURE,
    error
  }
};

export const postSignup = (user) => {
  return (dispatch, getState) => {
    let state = getState();
    if(!state.postingSignup){
      dispatch(submitSignup());
      fetch('/auth/signup', {
        method: 'post',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(user)
      })
      .then((response) => {
        if (response.status >= 200 && response.status < 300){
          dispatch(signupSuccess(response.user));
        }
        else {
          let error = new Error(response.statusText);
          dispatch(signupFailure(error));
        }
      })
    }
  }
}

export const submitLogout = () => {
  return {
    type: actions.LOGOUT_SUBMIT
  }
};

export const logoutSuccess = () => {
  return {
    type: actions.LOGOUT_SUCCESS
  }
};

export const logoutFailure = (error) => {
  return {
    type: actions.LOGOUT_FAILURE,
    error
  }
}

export const postLogout = () => {
  return (dispatch) => {
    dispatch(submitLogout());
    fetch('/auth/logout', {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    })
    .then((response) => {
      if (response.status >= 200 && response.status < 300){
        dispatch(logoutSuccess());
      }
      else {
        let error = new Error(response.statusText);
        dispatch(logoutFailure(error));
      }
    })
  }
}
<commit_msg>refac: fixes error with add email and password actions<commit_after>import * as actions from './actionConstants';
import {browserHistory} from 'react-router';

export const addEmail = (email) => {
  return {
    type: actions.ADD_EMAIL,
    email
  }
};

export const addPassword = (password) => {
  return {
    type: actions.ADD_PASSWORD,
    password
  }
};

export const submitLogin = () => {
  return {
    type: actions.LOGIN_SUBMIT
  }
}

export const loginSuccess = (user) => {
  browserHistory.push('/dashboard');
  return {
    type: actions.LOGIN_SUCCESS,
    user
  }
};

export const loginFailure = (error) => {
  return {
    type: actions.LOGIN_FAILURE,
    error
  }
};

export const postLogin = (user) => {
  return (dispatch, getState) => {
    let state = getState();
    if(!state.postingLogin){
      dispatch(submitLogin());
      fetch('/auth/login', {
        method: 'post',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(user)
      })
      .then((response) => {
        if (response.status >= 200 && response.status < 300){
          dispatch(loginSuccess(response.user));
        }
        else {
          let error = new Error(response.statusText);
          dispatch(loginFailure(error));
        }
      })
    }
  }
}

export const addSignupFirstName = (firstName) => {
  return {
    type: actions.SIGNUP_FIRST_NAME,
    firstName
  }
};

export const addSignupLastName = (lastName) => {
  return {
    type: actions.SIGNUP_LAST_NAME,
    lastName
  }
};

export const submitSignup = () => {
  return {
    type: actions.SIGNUP_SUBMIT
  }
};

export const signupSuccess = (user) => {
  browserHistory.push('/dashboard');
  return {
    type: actions.SIGNUP_SUCCESS,
    user
  }
};

export const signupFailure = (error) => {
  return {
    type: actions.SIGNUP_FAILURE,
    error
  }
};

export const postSignup = (user) => {
  return (dispatch, getState) => {
    let state = getState();
    if(!state.postingSignup){
      dispatch(submitSignup());
      fetch('/auth/signup', {
        method: 'post',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(user)
      })
      .then((response) => {
        if (response.status >= 200 && response.status < 300){
          dispatch(signupSuccess(response.user));
        }
        else {
          let error = new Error(response.statusText);
          dispatch(signupFailure(error));
        }
      })
    }
  }
}

export const submitLogout = () => {
  return {
    type: actions.LOGOUT_SUBMIT
  }
};

export const logoutSuccess = () => {
  return {
    type: actions.LOGOUT_SUCCESS
  }
};

export const logoutFailure = (error) => {
  return {
    type: actions.LOGOUT_FAILURE,
    error
  }
}

export const postLogout = () => {
  return (dispatch) => {
    dispatch(submitLogout());
    fetch('/auth/logout', {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      }
    })
    .then((response) => {
      if (response.status >= 200 && response.status < 300){
        dispatch(logoutSuccess());
      }
      else {
        let error = new Error(response.statusText);
        dispatch(logoutFailure(error));
      }
    })
  }
}
<|endoftext|>


<commit_before>/**
 * @file Center align table cell content
 * @url https://webapps.stackexchange.com/questions/133249
 */

/**
 * Run the snippet
 */
function run() {
  var doc = DocumentApp.getActiveDocument();
  setСenterAlignmentForAllTables2_(doc);
}

/**
 *
 * @param {GoogleAppsScript.Document.Document} doc
 */
function setСenterAlignmentForAllTables_(doc) {
  var style = {};
  style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =
    DocumentApp.HorizontalAlignment.CENTER;
  style[DocumentApp.Attribute.VERTICAL_ALIGNMENT] =
    DocumentApp.VerticalAlignment.CENTER;
  var tables = doc.getBody().getTables();
  tables.forEach(function(table) {
    var numRows = table.getNumRows();
    var indexRow = 0;
    while (indexRow < numRows) {
      var row = table.getRow(indexRow);
      var numCells = row.getNumCells();
      var indexCell = 0;
      while (indexCell < numCells) {
        var cell = row.getCell(indexCell);
        cell.setAttributes(style);
        indexCell++;
      }
      indexRow++;
    }
  });
}

/**
 *
 * @param {GoogleAppsScript.Document.Document} doc
 */
function setСenterAlignmentForAllTables2_(doc) {
  var style = {};
  style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =
    DocumentApp.HorizontalAlignment.CENTER;
  style[DocumentApp.Attribute.VERTICAL_ALIGNMENT] =
    DocumentApp.VerticalAlignment.CENTER;
  var text = doc.getBody();
  for (var i = 0; i < text.getNumChildren(); i++) {
    if (text.getChild(i).getType() == 'TABLE') {
      var table = text.getChild(i).asTable();
      var rows = table.getNumRows();
      var cols = table
        .getChild(0)
        .asTableRow()
        .getNumChildren();
      for (var j = 0; j < rows; j++) {
        for (var k = 0; k < cols; k++) {
          text
            .getChild(i)
            .asTable()
            .getCell(j, k)
            .setAttributes(style);
        }
      }
    }
  }
}
<commit_msg>Updates<commit_after>/**
 * @file Center align table cell content
 * @url https://webapps.stackexchange.com/questions/133249
 */

/**
 * Run the snippet
 */
function run() {
  var doc = DocumentApp.getActiveDocument();
  setСenterAlignmentForAllTables2_(doc);
}

/**
 *
 * @param {GoogleAppsScript.Document.Document} doc
 */
function setСenterAlignmentForAllTables_(doc) {
  var style = {};
  style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =
    DocumentApp.HorizontalAlignment.CENTER;
  style[DocumentApp.Attribute.VERTICAL_ALIGNMENT] =
    DocumentApp.VerticalAlignment.CENTER;
  var tables = doc.getBody().getTables();
  tables.forEach(function(table) {
    var numRows = table.getNumRows();
    var indexRow = 0;
    while (indexRow < numRows) {
      var row = table.getRow(indexRow);
      var numCells = row.getNumCells();
      var indexCell = 0;
      while (indexCell < numCells) {
        var cell = row.getCell(indexCell);
        cell.setAttributes(style);
        indexCell++;
      }
      indexRow++;
    }
  });
}

/**
 *
 * @param {GoogleAppsScript.Document.Document} doc
 */
function setСenterAlignmentForAllTables2_(doc) {
  var style = {};
  style[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =
    DocumentApp.HorizontalAlignment.CENTER;
  style[DocumentApp.Attribute.VERTICAL_ALIGNMENT] =
    DocumentApp.VerticalAlignment.CENTER;
  var body = doc.getBody();
  for (var i = 0; i < body.getNumChildren(); i++) {
    if (body.getChild(i).getType() == 'TABLE') {
      var table = body.getChild(i).asTable();
      var rows = table.getNumRows();
      var cols = table
        .getChild(0)
        .asTableRow()
        .getNumChildren();
      for (var j = 0; j < rows; j++) {
        for (var k = 0; k < cols; k++) {
          body
            .getChild(i)
            .asTable()
            .getCell(j, k)
            .setAttributes(style);
        }
      }
    }
  }
}
<|endoftext|>