<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const url = require('url');
const mapnik = require('@carto/mapnik');
const normalizeURI = require('./uri');
const createMetatileCache = require('./metatile-cache');
const createMapPool = require('./map-pool');
const timeoutDecorator = require('./utils/timeout-decorator');
const mime = require('mime');

mapnik.register_default_input_plugins();

function MapnikSource(uri, callback) {
    this.open = false;

    // cache used to skip encoding of solid images
    this.solidCache = {};

    this._uri = normalizeURI(uri);

    // Public API to announce how we're metatiling.
    this.metatile = this._uri.query.metatile;
    this.bufferSize = this._uri.query.bufferSize;

    if (this._uri.protocol && this._uri.protocol !== 'mapnik:') {
        return callback(new Error('Only the mapnik protocol is supported'));
    }

    this._stats = {
        render: 0,          // # of times a render is requested from mapnik
        total: 0,           // # of tiles returned from source
        encoded: 0,         // # of tiles encoded
        solid: 0,           // # of tiles isSolid
        solidPainted: 0     // # of tiles isSolid && painted
    };

    if (this._uri.query.limits && this._uri.query.limits.render > 0) {
        this.getTile = timeoutDecorator(this.getTile.bind(this), this._uri.query.limits.render);
        this.getGrid = timeoutDecorator(this.getGrid.bind(this), this._uri.query.limits.render);
    }

    this._open(callback);
}

MapnikSource.mapnik = mapnik;

// Finds all XML files in the filepath and returns their tilesource URI.
MapnikSource.list = function (filepath, callback) {
    filepath = path.resolve(filepath);
    fs.readdir(filepath, (err, files) => {
        if (err) {
            return callback(err);
        }

        const result = {};

        for (i = 0; i < files.length; i++) {
            const name = files[i].match(/^([\w-]+)\.xml$/);

            if (name) {
                result[name[1]] = 'mapnik://' + path.join(filepath, name[0]);
            }
        }

        return callback(null, result);
    });
};

// Finds an XML file with the given ID in the filepath and returns a
// tilesource URI.
MapnikSource.findID = function (filepath, id, callback) {
    filepath = path.resolve(filepath);
    const file = path.join(filepath, id + '.xml');

    fs.stat(file, (err, stats) => {
        if (err) {
            return callback(err);
        }

        return callback(null, 'mapnik://' + file);
    });
};

MapnikSource.registerProtocols = function (tilelive) {
    tilelive.protocols['mapnik:'] = MapnikSource;
};

MapnikSource.prototype.toJSON = function () {
    return url.format(this._uri);
};

MapnikSource.prototype.close = function (callback) {
    if (this._metatileCache) {
        this._metatileCache.clear();
    }

    if (this._mapPool) {
        return this._mapPool.drain()
            .then(() => {
                this._mapPool.clear();
                this.open = false;
                return callback();
            });
    }
};

// Render handler for a given tile request.
MapnikSource.prototype.getTile = function (z, x, y, callback) {
    z = +z;
    x = +x;
    y = +y;

    try {
        areValidCoords({ z, x, y });
    } catch (err) {
        return callback(err);
    }

    const format = (this._info && this._info.format) || 'png';
    const key = [format, z, x, y].join(',');
    this._metatileCache.get(key, (err, tile, headers, stats) => {
        if (err) {
            return callback(err);
        }

        return callback(null, tile, headers, stats);
    });
};

MapnikSource.prototype.getGrid = function (z, x, y, callback) {
    z = +z;
    x = +x;
    y = +y;

    try {
        areValidCoords({ z, x, y });
    } catch (err) {
        return callback(err);
    }

    if (!this._info) {
        return callback(new Error('Tilesource info is missing, cannot rendering interactivity'));
    }

    if (!this._info.interactivity_fields || !this._info.interactivity_layer) {
        return callback(new Error('Tileset has no interactivity'));
    }

    if (!mapnik.supports.grid) {
        return callback(new Error('Mapnik is missing grid support'));
    }

    const key = ['utf', z, x, y].join(',');
    this._metatileCache.get(key, (err, grid, headers, stats) => {
        if (err) {
            return callback(err);
        }

        delete grid.solid;

        return callback(null, grid, headers, stats);
    });
};

function areValidCoords ({ z, x, y }) {
    areCoordsNumbers({ z, x, y });
    areCoordsInRange({ z, x, y });
}

function areCoordsNumbers ({ z, x, y }) {
    if (isNaN(z) || isNaN(x) || isNaN(y)) {
        throw new Error('Invalid coordinates: '+z+'/'+x+'/'+y);
    }
}

function areCoordsInRange ({ z, x, y }) {
    const max = Math.pow(2, z);

    if (!isFinite(max) || x >= max || x < 0 || y >= max || y < 0) {
        throw new Error('Coordinates out of range: '+z+'/'+x+'/'+y);
    }
}

MapnikSource.prototype.getInfo = function(callback) {
    if (!this._info) {
        return callback(new Error('Info is unavailable'));
    }

    return callback(null, this._info);
};

MapnikSource.prototype._open = function (callback) {
    if (this.open) {
        return callback(null, this);
    }

    this._loadXML((err, xml) => {
        if (err) {
            return callback(err);
        }

        this._metatileCache = createMetatileCache(this, this._uri.query.tileSize, this._uri.query.metatile, this._uri.query.metatileCache);
        this._mapPool = createMapPool(this._uri, xml);

        this._populateInfo((err) => {
            if (err) {
                return callback(err);
            }

            this.open = true;

            return callback(null, this);
        });
    });
};

// Loads the XML file from the specified path. Calls `callback` when the mapfile
// can be expected to be in `mapfile`. If this isn't successful, `callback` gets
// an error as its first argument.
MapnikSource.prototype._loadXML = function (callback) {
    if (this._uri.xml) {
        return callback(null, this._uri.xml);
    }

    fs.readFile(path.resolve(this._uri.pathname), 'utf8', callback);
};

MapnikSource.prototype._populateInfo = function (callback) {
    const id = path.basename(this._uri.pathname, path.extname(this._uri.pathname));

    this._mapPool.acquire()
        .then((resource) => {
            if (!(resource instanceof mapnik.Map)) {
                const err = resource;
                this._mapPool.release(resource);

                throw err;
            }

            const map = resource;
            const info = { id: id, name: id, minzoom: 0, maxzoom: 22, autoscale: true };

            const p = map.parameters;
            for (const key in p) {
                info[key] = p[key];
            }

            if (p.bounds) {
                info.bounds = p.bounds.split(',').map(parseFloat);
            }

            if (p.center) {
                info.center = p.center.split(',').map(parseFloat);
            }

            if (p.minzoom) {
                info.minzoom = parseInt(p.minzoom, 10);
            }

            if (p.maxzoom) {
                info.maxzoom = parseInt(p.maxzoom, 10);
            }

            if (p.interactivity_fields) {
                info.interactivity_fields = p.interactivity_fields.split(',');
            }

            if (!info.bounds || info.bounds.length !== 4) {
                info.bounds = [ -180, -85.05112877980659, 180, 85.05112877980659 ];
            }

            if (!info.center || info.center.length !== 3) {
                info.center = [
                    (info.bounds[2] - info.bounds[0]) / 2 + info.bounds[0],
                    (info.bounds[3] - info.bounds[1]) / 2 + info.bounds[1],
                    2
                ];
            }

            this._info = info;
            this._mapPool.release(map);

            return callback(null);
        })
        .catch((err) => {
            return callback(err);
        });
};

// Render png/jpg/tif image or a utf grid and return an encoded buffer
MapnikSource.prototype._renderMetatile = function (format, z, x, y, metatile, callback) {
    this._mapPool.acquire()
        .then((resource) => {
            if (!(resource instanceof mapnik.Map)) {
                const err = resource;
                this._mapPool.release(resource)

                throw err;
            }

            const map = resource;

            try {
                const options = {
                    tileSize: this._uri.query.tileSize,
                    buffer_size: this._uri.query.bufferSize,
                    format: format,
                    z: z,
                    x: metatile.x,
                    y: metatile.y,
                    metrics: this._uri.query.metrics,
                    variables: this._uri.query.variables || {},
                    scale: this._uri.query.scale
                };

                // Set x, y, z based on the metatile boundary
                options.variables.zoom = options.z;

                // Set default options.
                if (options.format === 'utf') {
                    options.layer = map.parameters.interactivity_layer;
                    options.fields = map.parameters.interactivity_fields.split(',');
                    options.resolution = this._uri.query.resolution;
                }

                const renderStartTime = Date.now();

                this._stats.render++;

                const image = new mapnik[format === 'utf' ? 'Grid' : 'Image'](metatile.width, metatile.height);

                image.metrics_enabled = options.metrics;

                map.resize(metatile.width, metatile.height);
                map.extent = metatile.bbox;

                map.render(image, options, (err, image) => {
                    this._mapPool.release(map);

                    if (err) {
                        return callback(err);
                    }

                    const renderStats = {};
                    renderStats.render = Math.round((Date.now() - renderStartTime) / metatile.tiles.length);

                    return this._sliceMetatile(image, options, metatile, renderStats, callback);
                });
            } catch(err) {
                this._mapPool.release(map);
                return callback(err);
            }
        })
        .catch((err) => {
            return callback(err);
        });
};

MapnikSource.prototype._sliceMetatile = function (source_image, options, meta, stats, callback) {
    if (meta.tiles.length === 0) {
        return callback(null, {});
    }

    // TODO: handle async in a standard way
    const tiles = {};
    const err_num = 0;
    let tile_num = 0;

    meta.tiles.forEach((coords) => {
        const key = [ options.format, coords[0], coords[1], coords[2] ].join(',');
        const encodeStartTime = Date.now();
        const x = (coords[1] - meta.x) * options.tileSize;
        const y = (coords[2] - meta.y) * options.tileSize;

        this._getImage(source_image, options, x, y, (err, image) => {
            tile_num++;
            if (err) {
                if (!err_num) {
                    return callback(err);
                }

                err_num++;

                return;
            }

            tiles[key] = {
                image: image,
                headers: this._getHeaders(options.format),
                stats: Object.assign(stats, { encode: Date.now() - encodeStartTime }, source_image.get_metrics())
            };

            if (tile_num === meta.tiles.length) {
                return callback(null, tiles);
            }
        });
    });
};

MapnikSource.prototype._getHeaders = function (format) {
    const headers = {};

    if (format === 'utf') {
        headers['Content-Type'] = 'application/json';
        return headers;
    }

    // NOTE: formats use mapnik syntax like `png8:m=h` or `jpeg80`
    // so we need custom handling for png/jpeg
    if (format.indexOf('png') !== -1) {
        headers['Content-Type'] = 'image/png';
        return headers;
    }

    if (format.indexOf('jpeg') !== -1 || format.indexOf('jpg') !== -1) {
        headers['Content-Type'] = 'image/jpeg';
        return headers;
    }

    // will default to 'application/octet-stream' if unable to detect
    headers['Content-Type'] = mime.getType(options.format.split(':')[0]);
    return headers;
};

MapnikSource.prototype._getImage = function (image, options, x, y, callback) {
    const view = image.view(x, y, options.tileSize, options.tileSize);

    view.isSolid((err, solid, pixel) => {
        if (err) {
            return callback(err);
        }

        let pixel_key = '';
        if (solid) {
            if (options.format === 'utf') {
                // TODO https://github.com/mapbox/tilelive-mapnik/issues/56
                pixel_key = pixel.toString();
            } else {
                // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
                const a = (pixel>>>24) & 0xff;
                const r = pixel & 0xff;
                const g = (pixel>>>8) & 0xff;
                const b = (pixel>>>16) & 0xff;
                pixel_key = options.format + r +','+ g + ',' + b + ',' + a;
            }
        }

        // Add stats.
        this._stats.total++;

        if (solid !== false) {
            this._stats.solid++;
        }

        if (solid !== false && image.painted()) {
            this._stats.solidPainted++;
        }

        // If solid and image buffer is cached skip image encoding.
        if (solid && this.solidCache[pixel_key]) {
            return callback(null, this.solidCache[pixel_key]);
        }

        this._stats.encoded++;

        try {
            const solidCache = this.solidCache;
            function encodeCallback (err, buffer) {
                if (err) {
                    return callback(err);
                }

                if (solid !== false) {
                    // @TODO for 'utf' this attaches an extra, bogus 'solid' key to
                    // to the grid as it is not a buffer but an actual JS object.
                    // Fix is to propagate a third parameter through callbacks all
                    // the way back to tilelive source #getGrid.
                    buffer.solid = pixel_key;

                    if (options.format !== 'utf') {
                        solidCache[pixel_key] = buffer;
                    }
                }

                return callback(null, buffer);
            }

            if (options.format === 'utf') {
                return view.encode(options, encodeCallback);
            }

            view.encode(options.format, options, encodeCallback);
        } catch (err) {
            return callback(err);
        }
    });
}

module.exports = MapnikSource;
<commit_msg>Unify encode callbacks<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const url = require('url');
const mapnik = require('@carto/mapnik');
const normalizeURI = require('./uri');
const createMetatileCache = require('./metatile-cache');
const createMapPool = require('./map-pool');
const timeoutDecorator = require('./utils/timeout-decorator');
const mime = require('mime');

mapnik.register_default_input_plugins();

function MapnikSource(uri, callback) {
    this.open = false;

    // cache used to skip encoding of solid images
    this.solidCache = {};

    this._uri = normalizeURI(uri);

    // Public API to announce how we're metatiling.
    this.metatile = this._uri.query.metatile;
    this.bufferSize = this._uri.query.bufferSize;

    if (this._uri.protocol && this._uri.protocol !== 'mapnik:') {
        return callback(new Error('Only the mapnik protocol is supported'));
    }

    this._stats = {
        render: 0,          // # of times a render is requested from mapnik
        total: 0,           // # of tiles returned from source
        encoded: 0,         // # of tiles encoded
        solid: 0,           // # of tiles isSolid
        solidPainted: 0     // # of tiles isSolid && painted
    };

    if (this._uri.query.limits && this._uri.query.limits.render > 0) {
        this.getTile = timeoutDecorator(this.getTile.bind(this), this._uri.query.limits.render);
        this.getGrid = timeoutDecorator(this.getGrid.bind(this), this._uri.query.limits.render);
    }

    this._open(callback);
}

MapnikSource.mapnik = mapnik;

// Finds all XML files in the filepath and returns their tilesource URI.
MapnikSource.list = function (filepath, callback) {
    filepath = path.resolve(filepath);
    fs.readdir(filepath, (err, files) => {
        if (err) {
            return callback(err);
        }

        const result = {};

        for (i = 0; i < files.length; i++) {
            const name = files[i].match(/^([\w-]+)\.xml$/);

            if (name) {
                result[name[1]] = 'mapnik://' + path.join(filepath, name[0]);
            }
        }

        return callback(null, result);
    });
};

// Finds an XML file with the given ID in the filepath and returns a
// tilesource URI.
MapnikSource.findID = function (filepath, id, callback) {
    filepath = path.resolve(filepath);
    const file = path.join(filepath, id + '.xml');

    fs.stat(file, (err, stats) => {
        if (err) {
            return callback(err);
        }

        return callback(null, 'mapnik://' + file);
    });
};

MapnikSource.registerProtocols = function (tilelive) {
    tilelive.protocols['mapnik:'] = MapnikSource;
};

MapnikSource.prototype.toJSON = function () {
    return url.format(this._uri);
};

MapnikSource.prototype.close = function (callback) {
    if (this._metatileCache) {
        this._metatileCache.clear();
    }

    if (this._mapPool) {
        return this._mapPool.drain()
            .then(() => {
                this._mapPool.clear();
                this.open = false;
                return callback();
            });
    }
};

// Render handler for a given tile request.
MapnikSource.prototype.getTile = function (z, x, y, callback) {
    z = +z;
    x = +x;
    y = +y;

    try {
        areValidCoords({ z, x, y });
    } catch (err) {
        return callback(err);
    }

    const format = (this._info && this._info.format) || 'png';
    const key = [format, z, x, y].join(',');
    this._metatileCache.get(key, (err, tile, headers, stats) => {
        if (err) {
            return callback(err);
        }

        return callback(null, tile, headers, stats);
    });
};

MapnikSource.prototype.getGrid = function (z, x, y, callback) {
    z = +z;
    x = +x;
    y = +y;

    try {
        areValidCoords({ z, x, y });
    } catch (err) {
        return callback(err);
    }

    if (!this._info) {
        return callback(new Error('Tilesource info is missing, cannot rendering interactivity'));
    }

    if (!this._info.interactivity_fields || !this._info.interactivity_layer) {
        return callback(new Error('Tileset has no interactivity'));
    }

    if (!mapnik.supports.grid) {
        return callback(new Error('Mapnik is missing grid support'));
    }

    const key = ['utf', z, x, y].join(',');
    this._metatileCache.get(key, (err, grid, headers, stats) => {
        if (err) {
            return callback(err);
        }

        delete grid.solid;

        return callback(null, grid, headers, stats);
    });
};

function areValidCoords ({ z, x, y }) {
    areCoordsNumbers({ z, x, y });
    areCoordsInRange({ z, x, y });
}

function areCoordsNumbers ({ z, x, y }) {
    if (isNaN(z) || isNaN(x) || isNaN(y)) {
        throw new Error('Invalid coordinates: '+z+'/'+x+'/'+y);
    }
}

function areCoordsInRange ({ z, x, y }) {
    const max = Math.pow(2, z);

    if (!isFinite(max) || x >= max || x < 0 || y >= max || y < 0) {
        throw new Error('Coordinates out of range: '+z+'/'+x+'/'+y);
    }
}

MapnikSource.prototype.getInfo = function(callback) {
    if (!this._info) {
        return callback(new Error('Info is unavailable'));
    }

    return callback(null, this._info);
};

MapnikSource.prototype._open = function (callback) {
    if (this.open) {
        return callback(null, this);
    }

    this._loadXML((err, xml) => {
        if (err) {
            return callback(err);
        }

        this._metatileCache = createMetatileCache(this, this._uri.query.tileSize, this._uri.query.metatile, this._uri.query.metatileCache);
        this._mapPool = createMapPool(this._uri, xml);

        this._populateInfo((err) => {
            if (err) {
                return callback(err);
            }

            this.open = true;

            return callback(null, this);
        });
    });
};

// Loads the XML file from the specified path. Calls `callback` when the mapfile
// can be expected to be in `mapfile`. If this isn't successful, `callback` gets
// an error as its first argument.
MapnikSource.prototype._loadXML = function (callback) {
    if (this._uri.xml) {
        return callback(null, this._uri.xml);
    }

    fs.readFile(path.resolve(this._uri.pathname), 'utf8', callback);
};

MapnikSource.prototype._populateInfo = function (callback) {
    const id = path.basename(this._uri.pathname, path.extname(this._uri.pathname));

    this._mapPool.acquire()
        .then((resource) => {
            if (!(resource instanceof mapnik.Map)) {
                const err = resource;
                this._mapPool.release(resource);

                throw err;
            }

            const map = resource;
            const info = { id: id, name: id, minzoom: 0, maxzoom: 22, autoscale: true };

            const p = map.parameters;
            for (const key in p) {
                info[key] = p[key];
            }

            if (p.bounds) {
                info.bounds = p.bounds.split(',').map(parseFloat);
            }

            if (p.center) {
                info.center = p.center.split(',').map(parseFloat);
            }

            if (p.minzoom) {
                info.minzoom = parseInt(p.minzoom, 10);
            }

            if (p.maxzoom) {
                info.maxzoom = parseInt(p.maxzoom, 10);
            }

            if (p.interactivity_fields) {
                info.interactivity_fields = p.interactivity_fields.split(',');
            }

            if (!info.bounds || info.bounds.length !== 4) {
                info.bounds = [ -180, -85.05112877980659, 180, 85.05112877980659 ];
            }

            if (!info.center || info.center.length !== 3) {
                info.center = [
                    (info.bounds[2] - info.bounds[0]) / 2 + info.bounds[0],
                    (info.bounds[3] - info.bounds[1]) / 2 + info.bounds[1],
                    2
                ];
            }

            this._info = info;
            this._mapPool.release(map);

            return callback(null);
        })
        .catch((err) => {
            return callback(err);
        });
};

// Render png/jpg/tif image or a utf grid and return an encoded buffer
MapnikSource.prototype._renderMetatile = function (format, z, x, y, metatile, callback) {
    this._mapPool.acquire()
        .then((resource) => {
            if (!(resource instanceof mapnik.Map)) {
                const err = resource;
                this._mapPool.release(resource)

                throw err;
            }

            const map = resource;

            try {
                const options = {
                    tileSize: this._uri.query.tileSize,
                    buffer_size: this._uri.query.bufferSize,
                    format: format,
                    z: z,
                    x: metatile.x,
                    y: metatile.y,
                    metrics: this._uri.query.metrics,
                    variables: this._uri.query.variables || {},
                    scale: this._uri.query.scale
                };

                // Set x, y, z based on the metatile boundary
                options.variables.zoom = options.z;

                // Set default options.
                if (options.format === 'utf') {
                    options.layer = map.parameters.interactivity_layer;
                    options.fields = map.parameters.interactivity_fields.split(',');
                    options.resolution = this._uri.query.resolution;
                }

                const renderStartTime = Date.now();

                this._stats.render++;

                const image = new mapnik[format === 'utf' ? 'Grid' : 'Image'](metatile.width, metatile.height);

                image.metrics_enabled = options.metrics;

                map.resize(metatile.width, metatile.height);
                map.extent = metatile.bbox;

                map.render(image, options, (err, image) => {
                    this._mapPool.release(map);

                    if (err) {
                        return callback(err);
                    }

                    const renderStats = {};
                    renderStats.render = Math.round((Date.now() - renderStartTime) / metatile.tiles.length);

                    return this._sliceMetatile(image, options, metatile, renderStats, callback);
                });
            } catch(err) {
                this._mapPool.release(map);
                return callback(err);
            }
        })
        .catch((err) => {
            return callback(err);
        });
};

MapnikSource.prototype._sliceMetatile = function (source_image, options, meta, stats, callback) {
    if (meta.tiles.length === 0) {
        return callback(null, {});
    }

    // TODO: handle async in a standard way
    const tiles = {};
    const err_num = 0;
    let tile_num = 0;

    meta.tiles.forEach((coords) => {
        const key = [ options.format, coords[0], coords[1], coords[2] ].join(',');
        const encodeStartTime = Date.now();
        const x = (coords[1] - meta.x) * options.tileSize;
        const y = (coords[2] - meta.y) * options.tileSize;

        this._getImage(source_image, options, x, y, (err, image) => {
            tile_num++;
            if (err) {
                if (!err_num) {
                    return callback(err);
                }

                err_num++;

                return;
            }

            tiles[key] = {
                image: image,
                headers: this._getHeaders(options.format),
                stats: Object.assign(stats, { encode: Date.now() - encodeStartTime }, source_image.get_metrics())
            };

            if (tile_num === meta.tiles.length) {
                return callback(null, tiles);
            }
        });
    });
};

MapnikSource.prototype._getHeaders = function (format) {
    const headers = {};

    if (format === 'utf') {
        headers['Content-Type'] = 'application/json';
        return headers;
    }

    // NOTE: formats use mapnik syntax like `png8:m=h` or `jpeg80`
    // so we need custom handling for png/jpeg
    if (format.indexOf('png') !== -1) {
        headers['Content-Type'] = 'image/png';
        return headers;
    }

    if (format.indexOf('jpeg') !== -1 || format.indexOf('jpg') !== -1) {
        headers['Content-Type'] = 'image/jpeg';
        return headers;
    }

    // will default to 'application/octet-stream' if unable to detect
    headers['Content-Type'] = mime.getType(options.format.split(':')[0]);
    return headers;
};

MapnikSource.prototype._getImage = function (image, options, x, y, callback) {
    const view = image.view(x, y, options.tileSize, options.tileSize);

    view.isSolid((err, solid, pixel) => {
        if (err) {
            return callback(err);
        }

        let pixel_key = '';
        if (solid) {
            if (options.format === 'utf') {
                // TODO https://github.com/mapbox/tilelive-mapnik/issues/56
                pixel_key = pixel.toString();
            } else {
                // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
                const a = (pixel>>>24) & 0xff;
                const r = pixel & 0xff;
                const g = (pixel>>>8) & 0xff;
                const b = (pixel>>>16) & 0xff;
                pixel_key = options.format + r +','+ g + ',' + b + ',' + a;
            }
        }

        // Add stats.
        this._stats.total++;

        if (solid !== false) {
            this._stats.solid++;
        }

        if (solid !== false && image.painted()) {
            this._stats.solidPainted++;
        }

        // If solid and image buffer is cached skip image encoding.
        if (solid && this.solidCache[pixel_key]) {
            return callback(null, this.solidCache[pixel_key]);
        }

        this._stats.encoded++;

        try {
            const params = options.format === 'utf' ? [ options ] : [ options.format, options ];

            view.encode(...params, (err, buffer) => {
                if (err) {
                    return callback(err);
                }

                if (solid !== false) {
                    // @TODO for 'utf' this attaches an extra, bogus 'solid' key to
                    // to the grid as it is not a buffer but an actual JS object.
                    // Fix is to propagate a third parameter through callbacks all
                    // the way back to tilelive source #getGrid.
                    buffer.solid = pixel_key;

                    if (options.format !== 'utf') {
                        this.solidCache[pixel_key] = buffer;
                    }
                }

                return callback(null, buffer);
            });
        } catch (err) {
            return callback(err);
        }
    });
}

module.exports = MapnikSource;
<|endoftext|>


<commit_before>// TODO: Move mappings to a saparate project

'use strict';

var segment = require('./segment');

var floor = Math.floor;

exports.parse = parse;
exports.stringify = stringify;
exports.slice = slice;
exports.add = add;
exports.index = index;
exports.find = find;
exports.findGenerated = findGenerated;
exports.original = original;
exports.optimize = optimize;
exports.apply = apply;

exports.compareGeneratedSort = compareGeneratedSort;
exports.compareGenerated = compareGenerated;
exports.compareOriginalSort = compareOriginalSort;
exports.compareOriginal = compareOriginal;

function slice(mappings, from, to) {
    return to
        ? mappings.slice(index(mappings, from), index(mappings, to))
        : mappings.slice(index(mappings, from));
}

function add(mappings, items) {
    var index_;
    var i = items.length;

    for (;i--;) {
        index_ = index(mappings, items[i]);
        mappings.splice(index_, 0, items[i]);
    }
}

function apply(mappings, target) {
    var i, line, column, item;
    var result = [];

    for (i = target.length; i--;) {
        if (target[i][2] == null) {
            result.unshift(target[i]);
        } else {
            line = target[i][3];
            column = target[i][4];

            item = find(mappings, [ line, column ]);

            if (item) {
                target[i][2] = item[2];
                target[i][3] = item[3];
                target[i][4] = item[4];

                if (target[i][5] != null) {
                    target[i][5] = item[5];
                }

                result.unshift(target[i]);
            }
        }
    }

    return result;
}

function optimize(mappings) {
    var last, item, sl, nl;
    var result = [];
    var removed = [];
    var sources = [];
    var names = [];
    var i = mappings.length;

    for (; i--;) {
        item = mappings[i];
        last = result[0];

        if (last
            && last[0] === item[0]
            && last[1] === item[1]) {
            removed.unshift(item);
        } else if (last
            && last[0] === item[0]
            && last[2] === item[2]
            && last[3] === item[3]
            && last[4] === item[4]
            && last[5] === item[5]) {
            removed.unshift(last);
            result[0] = item;
        } else {
            result.unshift(item);

            if (item[2] != null) {
                sources[item[2]] = item[2];
            }

            if (item[5] != null) {
                names[item[5]] = item[5];
            }
        }
    }

    sl = sources.length;
    nl = names.length;

    sources = sources.filter(isFinite);
    names = names.filter(isFinite);

    if (sl !== sources.length || nl !== names.length) {
        for (i = result.length; i--;) {
            item = result[i];

            if (item[2] != null) {
                item[2] = sources.indexOf(item[2]);
            }

            if (item[5] != null) {
                item[5] = names.indexOf(item[5]);
            }
        }
    }

    return {
        mappings: result,
        sources: sources,
        names: names,
        removed: removed
    };
}

function index(mappings, needle) {
    return sortedIndex(mappings, needle, compareGeneratedSort);
}

function find(mappings, needle, upper) {
    var result = _find(
                    mappings,
                    needle,
                    compareGeneratedSort,
                    compareGenerated,
                    ! upper);

    return result
        && result[0] === needle[0]
        && result;
}

function original(mappings) {
    return mappings.filter(hasOriginal).sort(compareOriginalSort);
}

function findGenerated(original, needle, upper) {
    var result = _find(
                    original,
                    needle,
                    compareOriginalSort,
                    compareOriginal,
                    ! upper);

    return result
        && result[2] === needle[2]
        && result[3] === needle[3]
        && result;
}

function _find(mappings, needle, compareSort, compare, lower) {
    var index = sortedIndex(mappings, needle, compareSort);
    var result = mappings[index];

    if (lower && ( ! result || compare(result, needle) > 0)) {
        result = mappings[--index];
    }

    return result;
}

function parse(mappings) {
    var i, l, c, p, segment_, mapping;
    var gline = 0;
    var gcolumn = 0;
    var oline = 0;
    var ocolumn = 0;
    var source = 0;
    var name = 0;
    var result = [];
    var LINE_SEP = ';';
    var SEGMENT_SEP = ',';

    if ( ! mappings) {
        return result;
    }

    for (i = 0, p = 0, l = mappings.length; i <= l; i++) {
        c = mappings[i];

        if ( ! c || c === LINE_SEP || c === SEGMENT_SEP) {
            if (i !== p) {
                segment_ = segment(mappings.slice(p, i));

                mapping = [ gline, gcolumn += segment_[0], null, null, null, null ];

                if (segment_[1] != null) {
                    mapping[2] = source += segment_[1];
                    mapping[3] = oline += segment_[2];
                    mapping[4] = ocolumn += segment_[3];
                }

                if (segment_[4] != null) {
                    mapping[5] = name += segment_[4];
                }

                result.push(mapping);
            }

            p = i + 1;
        }

        if (c === LINE_SEP) {
            gline++;
            gcolumn = 0;
        }
    }

    result.sort(compareGeneratedSort);

    return result;
}

function stringify(mappings) {
    var i, l, mapping, line, segment_, k;
    var last = [ 0, 0, 0, 0, 0, 0 ];
    var diff = [ 0, 0, 0, 0, 0, 0 ];
    var lines = [];

    mappings.sort(compareGeneratedSort);

    for (i = 0, l = mappings.length; i < l; i++) {
        mapping = mappings[i];
        line = lines[mapping[0]];

        if ( ! line) {
            line = [];
            lines[mapping[0]] = line;
            last[1] = 0;
        }

        for (k = 1; mapping[k] != null; k++) {
            diff[k] = mapping[k] - last[k];
            last[k] = mapping[k];
        }

        if (mapping[2] == null) {
            segment_ = segment(diff[1]);
        } else if (mapping[5] == null) {
            segment_ = segment(diff[1], diff[2], diff[3], diff[4]);
        } else {
            segment_ = segment(diff[1], diff[2], diff[3], diff[4], diff[5]);
        }

        line.push(segment_[5]);
    }

    return lines.map(joinSegments).join(';');
}

function compareGeneratedSort(a, b) {
    return compareGenerated(a, b)
        || ((a[2] == null && b[2] == null)
            ? 0 : compareOriginal(a, b));
}

function compareOriginalSort(a, b) {
    return compareOriginal(a, b)
        || compareGenerated(a, b);
}

function compareGenerated(a, b) {
    return compareIndex(a, b, 0)
        || compareIndex(a, b, 1);
}

function compareOriginal(a, b) {
    return compareIndex(a, b, 2)
        || compareIndex(a, b, 3)
        || compareIndex(a, b, 4)
        || compareIndex(a, b, 5);
}

function compareIndex(a, b, i) {
    return (a[i] == null ? -1 : a[i]) - (b[i] == null ? -1 : b[i]);
}

function hasOriginal(item) {
    return item[2] != null;
}

function joinSegments(item) {
    return item ? item.join(',') : '';
}

function sortedIndex(array, value, compare) {
    var mid;
    var low = 0;
    var high = array.length;

    while (low < high) {
        mid = floor((low + high) / 2);

        if (compare(array[mid], value) < 0) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }

    return low;
}
<commit_msg>Expose mappings segment and vlq<commit_after>// TODO: Move mappings to a saparate project

'use strict';

var vlq = require('./vlq');
var segment = require('./segment');

var floor = Math.floor;

exports.vlq = vlq;
exports.segment = segment;
exports.parse = parse;
exports.stringify = stringify;
exports.slice = slice;
exports.add = add;
exports.index = index;
exports.find = find;
exports.findGenerated = findGenerated;
exports.original = original;
exports.optimize = optimize;
exports.apply = apply;

exports.compareGeneratedSort = compareGeneratedSort;
exports.compareGenerated = compareGenerated;
exports.compareOriginalSort = compareOriginalSort;
exports.compareOriginal = compareOriginal;

function slice(mappings, from, to) {
    return to
        ? mappings.slice(index(mappings, from), index(mappings, to))
        : mappings.slice(index(mappings, from));
}

function add(mappings, items) {
    var index_;
    var i = items.length;

    for (;i--;) {
        index_ = index(mappings, items[i]);
        mappings.splice(index_, 0, items[i]);
    }
}

function apply(mappings, target) {
    var i, line, column, item;
    var result = [];

    for (i = target.length; i--;) {
        if (target[i][2] == null) {
            result.unshift(target[i]);
        } else {
            line = target[i][3];
            column = target[i][4];

            item = find(mappings, [ line, column ]);

            if (item) {
                target[i][2] = item[2];
                target[i][3] = item[3];
                target[i][4] = item[4];

                if (target[i][5] != null) {
                    target[i][5] = item[5];
                }

                result.unshift(target[i]);
            }
        }
    }

    return result;
}

function optimize(mappings) {
    var last, item, sl, nl;
    var result = [];
    var removed = [];
    var sources = [];
    var names = [];
    var i = mappings.length;

    for (; i--;) {
        item = mappings[i];
        last = result[0];

        if (last
            && last[0] === item[0]
            && last[1] === item[1]) {
            removed.unshift(item);
        } else if (last
            && last[0] === item[0]
            && last[2] === item[2]
            && last[3] === item[3]
            && last[4] === item[4]
            && last[5] === item[5]) {
            removed.unshift(last);
            result[0] = item;
        } else {
            result.unshift(item);

            if (item[2] != null) {
                sources[item[2]] = item[2];
            }

            if (item[5] != null) {
                names[item[5]] = item[5];
            }
        }
    }

    sl = sources.length;
    nl = names.length;

    sources = sources.filter(isFinite);
    names = names.filter(isFinite);

    if (sl !== sources.length || nl !== names.length) {
        for (i = result.length; i--;) {
            item = result[i];

            if (item[2] != null) {
                item[2] = sources.indexOf(item[2]);
            }

            if (item[5] != null) {
                item[5] = names.indexOf(item[5]);
            }
        }
    }

    return {
        mappings: result,
        sources: sources,
        names: names,
        removed: removed
    };
}

function index(mappings, needle) {
    return sortedIndex(mappings, needle, compareGeneratedSort);
}

function find(mappings, needle, upper) {
    var result = _find(
                    mappings,
                    needle,
                    compareGeneratedSort,
                    compareGenerated,
                    ! upper);

    return result
        && result[0] === needle[0]
        && result;
}

function original(mappings) {
    return mappings.filter(hasOriginal).sort(compareOriginalSort);
}

function findGenerated(original, needle, upper) {
    var result = _find(
                    original,
                    needle,
                    compareOriginalSort,
                    compareOriginal,
                    ! upper);

    return result
        && result[2] === needle[2]
        && result[3] === needle[3]
        && result;
}

function _find(mappings, needle, compareSort, compare, lower) {
    var index = sortedIndex(mappings, needle, compareSort);
    var result = mappings[index];

    if (lower && ( ! result || compare(result, needle) > 0)) {
        result = mappings[--index];
    }

    return result;
}

function parse(mappings) {
    var i, l, c, p, segment_, mapping;
    var gline = 0;
    var gcolumn = 0;
    var oline = 0;
    var ocolumn = 0;
    var source = 0;
    var name = 0;
    var result = [];
    var LINE_SEP = ';';
    var SEGMENT_SEP = ',';

    if ( ! mappings) {
        return result;
    }

    for (i = 0, p = 0, l = mappings.length; i <= l; i++) {
        c = mappings[i];

        if ( ! c || c === LINE_SEP || c === SEGMENT_SEP) {
            if (i !== p) {
                segment_ = segment(mappings.slice(p, i));

                mapping = [ gline, gcolumn += segment_[0], null, null, null, null ];

                if (segment_[1] != null) {
                    mapping[2] = source += segment_[1];
                    mapping[3] = oline += segment_[2];
                    mapping[4] = ocolumn += segment_[3];
                }

                if (segment_[4] != null) {
                    mapping[5] = name += segment_[4];
                }

                result.push(mapping);
            }

            p = i + 1;
        }

        if (c === LINE_SEP) {
            gline++;
            gcolumn = 0;
        }
    }

    result.sort(compareGeneratedSort);

    return result;
}

function stringify(mappings) {
    var i, l, mapping, line, segment_, k;
    var last = [ 0, 0, 0, 0, 0, 0 ];
    var diff = [ 0, 0, 0, 0, 0, 0 ];
    var lines = [];

    mappings.sort(compareGeneratedSort);

    for (i = 0, l = mappings.length; i < l; i++) {
        mapping = mappings[i];
        line = lines[mapping[0]];

        if ( ! line) {
            line = [];
            lines[mapping[0]] = line;
            last[1] = 0;
        }

        for (k = 1; mapping[k] != null; k++) {
            diff[k] = mapping[k] - last[k];
            last[k] = mapping[k];
        }

        if (mapping[2] == null) {
            segment_ = segment(diff[1]);
        } else if (mapping[5] == null) {
            segment_ = segment(diff[1], diff[2], diff[3], diff[4]);
        } else {
            segment_ = segment(diff[1], diff[2], diff[3], diff[4], diff[5]);
        }

        line.push(segment_[5]);
    }

    return lines.map(joinSegments).join(';');
}

function compareGeneratedSort(a, b) {
    return compareGenerated(a, b)
        || ((a[2] == null && b[2] == null)
            ? 0 : compareOriginal(a, b));
}

function compareOriginalSort(a, b) {
    return compareOriginal(a, b)
        || compareGenerated(a, b);
}

function compareGenerated(a, b) {
    return compareIndex(a, b, 0)
        || compareIndex(a, b, 1);
}

function compareOriginal(a, b) {
    return compareIndex(a, b, 2)
        || compareIndex(a, b, 3)
        || compareIndex(a, b, 4)
        || compareIndex(a, b, 5);
}

function compareIndex(a, b, i) {
    return (a[i] == null ? -1 : a[i]) - (b[i] == null ? -1 : b[i]);
}

function hasOriginal(item) {
    return item[2] != null;
}

function joinSegments(item) {
    return item ? item.join(',') : '';
}

function sortedIndex(array, value, compare) {
    var mid;
    var low = 0;
    var high = array.length;

    while (low < high) {
        mid = floor((low + high) / 2);

        if (compare(array[mid], value) < 0) {
            low = mid + 1;
        } else {
            high = mid;
        }
    }

    return low;
}
<|endoftext|>


<commit_before>var request		= require( "request" );
var xml2js 		= require( "xml2js" );
var entities 	= new ( require( "html-entities" ).AllHtmlEntities );

var lastPostTime = 0;
var readyToGo = false;
var handledPosts = {};

// Setup our threads table
db.run( "CREATE TABLE IF NOT EXISTS threads ( id INTEGER PRIMARY KEY AUTOINCREMENT, thread TEXT UNIQUE )" );

function IntToBase62( i ) {

	var digits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	var buf = [];

	do {

		var m = i % 62;
		buf.unshift( digits.charAt( m ) );

		i = Math.floor( i / 62 );

	} while ( i > 0 );

	return buf.join( "" );

}

function HandlePosts( posts ) {

	// Handle newer posts first
	for ( var i = posts.length - 1; i >= 0; i-- ) {

		var post = posts[ i ][ "$" ]

		lastPostTime = Math.max( post.date, lastPostTime );

		var html = entities.decode( post.html );

		// Read out some data badly
		var title = html.match(/href='showthread.*?>(.*?)</); title = title && title[1];
		var postID = html.match(/href='showthread\.php(.*?)&p=([0-9]+)/); postID = postID && postID[2];

		if ( !title || !postID || !readyToGo )
			continue;

		if ( handledPosts[ postID ] )
			continue;

		handledPosts[ postID ] = true;

		// We need a new scope for our title and postID values
		( function( title, postID ) {

			db.all( "SELECT thread FROM threads", function( err, rows ) {

				for ( var i = 0; i < rows.length; i++ ) {

					var lowerTitle = title.toLowerCase();

					if ( lowerTitle.indexOf( rows[i].thread.toLowerCase() ) != -1 ) {

						console.log( postID, IntToBase62( postID ) );
						bot.sendMessage( rows[i].thread + " - http://fcpn.ch/" + IntToBase62( postID ) );
						break;

					}

				}

			} );

		} )( title, postID );

	}

	readyToGo = true; // We ignore the first set of posts

}

function OnTicker( error, res, body ) {

	if ( error ) {
		console.trace( error );
		return;
	}

	xml2js.parseString( body, function( err, res ) {

		// Facepunch is dodgy
		if ( err )
			return;

		if ( !res || !res.newposts || !res.newposts.post )
			return;

		HandlePosts( res.newposts.post );

	} );

}

function RequestTicker() {

	var options = {
		qs: {
			aj:			1,
			lasttime:	lastPostTime
		}
	}

	request( "http://facepunch.com/fp_ticker.php", options, OnTicker );

}

setInterval( RequestTicker, 2500 );

// Commands for controlling what threads we want

bot.registerCommand( "fplist", function( name, user, args, argstr, group ) {

	db.all( "SELECT id, thread FROM threads", function( err, rows ) {

		var data = "Listening for:";

		if ( rows.length == 0 )
			data += " nothing.";
		else
			data += "\n";

		for ( var i = 0; i < rows.length; i++ ) {
			data += rows[i].id + " - " + rows[i].thread + "\n";
		}

		bot.sendMessage( data, group );

	} );

} );

bot.registerCommand( "fpadd", function( name, steamID, args, argstr ) {

	db.run( "INSERT INTO threads ( thread ) VALUES( ? )", argstr, function() {
		// We don't want to error if inserting a duplicate (let it fail silently who gives a shit)
	} );

} );

bot.registerCommand( "fpremove", function( name, steamID, args, argstr ) {

	db.run( "DELETE FROM threads WHERE id = ? OR thread = ?", argstr, argstr );

} );
<commit_msg>Change for upcoming ANCHOR LINKS for fcpn.ch<commit_after>var request		= require( "request" );
var xml2js 		= require( "xml2js" );
var entities 	= new ( require( "html-entities" ).AllHtmlEntities );

var lastPostTime = 0;
var readyToGo = false;
var handledPosts = {};

// Setup our threads table
db.run( "CREATE TABLE IF NOT EXISTS threads ( id INTEGER PRIMARY KEY AUTOINCREMENT, thread TEXT UNIQUE )" );

function IntToBase62( i ) {

	var digits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	var buf = [];

	do {

		var m = i % 62;
		buf.unshift( digits.charAt( m ) );

		i = Math.floor( i / 62 );

	} while ( i > 0 );

	return buf.join( "" );

}

function HandlePosts( posts ) {

	// Handle newer posts first
	for ( var i = posts.length - 1; i >= 0; i-- ) {

		var post = posts[ i ][ "$" ]

		lastPostTime = Math.max( post.date, lastPostTime );

		var html = entities.decode( post.html );

		// Read out some data badly
		var title = html.match(/href='showthread.*?>(.*?)</); title = title && title[1];
		var postID = html.match(/href='showthread\.php(.*?)&p=([0-9]+)/); postID = postID && postID[2];

		if ( !title || !postID || !readyToGo )
			continue;

		if ( handledPosts[ postID ] )
			continue;

		handledPosts[ postID ] = true;

		// We need a new scope for our title and postID values
		( function( title, postID ) {

			db.all( "SELECT thread FROM threads", function( err, rows ) {

				for ( var i = 0; i < rows.length; i++ ) {

					var lowerTitle = title.toLowerCase();

					if ( lowerTitle.indexOf( rows[i].thread.toLowerCase() ) != -1 ) {

						console.log( postID, IntToBase62( postID ) );
						bot.sendMessage( rows[i].thread + " - http://fcpn.ch#" + IntToBase62( postID ) );
						break;

					}

				}

			} );

		} )( title, postID );

	}

	readyToGo = true; // We ignore the first set of posts

}

function OnTicker( error, res, body ) {

	if ( error ) {
		console.trace( error );
		return;
	}

	xml2js.parseString( body, function( err, res ) {

		// Facepunch is dodgy
		if ( err )
			return;

		if ( !res || !res.newposts || !res.newposts.post )
			return;

		HandlePosts( res.newposts.post );

	} );

}

function RequestTicker() {

	var options = {
		qs: {
			aj:			1,
			lasttime:	lastPostTime
		}
	}

	request( "http://facepunch.com/fp_ticker.php", options, OnTicker );

}

setInterval( RequestTicker, 2500 );

// Commands for controlling what threads we want

bot.registerCommand( "fplist", function( name, user, args, argstr, group ) {

	db.all( "SELECT id, thread FROM threads", function( err, rows ) {

		var data = "Listening for:";

		if ( rows.length == 0 )
			data += " nothing.";
		else
			data += "\n";

		for ( var i = 0; i < rows.length; i++ ) {
			data += rows[i].id + " - " + rows[i].thread + "\n";
		}

		bot.sendMessage( data, group );

	} );

} );

bot.registerCommand( "fpadd", function( name, steamID, args, argstr ) {

	db.run( "INSERT INTO threads ( thread ) VALUES( ? )", argstr, function() {
		// We don't want to error if inserting a duplicate (let it fail silently who gives a shit)
	} );

} );

bot.registerCommand( "fpremove", function( name, steamID, args, argstr ) {

	db.run( "DELETE FROM threads WHERE id = ? OR thread = ?", argstr, argstr );

} );
<|endoftext|>


<commit_before>import { Schema, arrayOf, normalize } from 'normalizr'
import { camelizeKeys } from 'humps'
import 'isomorphic-fetch'

// Action key that carries API call info interpreted by this Redux middleware.
export const CALL_API = Symbol('Call API')

// A Redux middleware that interprets actions with CALL_API info specified.
// Performs the call and promises when such actions are dispatched.
export default store => next => action => {
  const callAPI = action[CALL_API]
  if (typeof callAPI === 'undefined') {
    return next(action)
  }

  let { endpoint } = callAPI
  const { schema, types } = callAPI

  if (typeof endpoint === 'function') {
    endpoint = endpoint(store.getState())
  }

  if (typeof endpoint !== 'string') {
    throw new Error('Specify a string endpoint URL.')
  }
  if (!schema) {
    throw new Error('Specify one of the exported Schemas.')
  }
  if (!Array.isArray(types) || types.length !== 3) {
    throw new Error('Expected an array of three action types.')
  }
  if (!types.every(type => typeof type === 'string')) {
    throw new Error('Expected action types to be strings.')
  }

  function actionWith(data) {
    const finalAction = Object.assign({}, action, data)
    delete finalAction[CALL_API]
    return finalAction
  }

  const [ requestType, successType, failureType ] = types
  next(actionWith({ type: requestType }))

  return callApi(endpoint, schema).then(
    response => next(actionWith({
      response,
      type: successType
    })),
    error => next(actionWith({
      type: failureType,
      error: error.message || 'Something bad happened'
    }))
  )
}
<commit_msg>middleware updated<commit_after>import { Schema, arrayOf, normalize } from 'normalizr'
import { camelizeKeys } from 'humps'
import 'isomorphic-fetch'
import {BASE_URL} from '../constants/global'

function callApi(endpoint, authenticated) {
  let token = localStorage.getItem('id_token') || null
  let config = {}

  if(authenticated) {
    if(token){
      config = {headers: {Authorization: "JWT " + `${token}`}}
    } else {
      throw "No token saved"
    }
  }

  return fetch(BASE_URL + endpoint, config)
    .then(response => response.text().then(text => ({text, response}))
    ).then(({ text, response }) => {
      if(!response.ok){
	return Promise.rejext(text)
      }
      return JSON.parse(text)
    }).catch(err => console.lot(err))
}

// Action key that carries API call info interpreted by this Redux middleware.
export const CALL_API = Symbol('Call API')

// A Redux middleware that interprets actions with CALL_API info specified.
// Performs the call and promises when such actions are dispatched.
export default store => next => action => {
  const callAPI = action[CALL_API]
  if (typeof callAPI === 'undefined') {
    return next(action)
  }

  let { endpoint, authenticated } = callAPI
  const { schema, types } = callAPI

  if (typeof endpoint === 'function') {
    endpoint = endpoint(store.getState())
  }

  if (typeof endpoint !== 'string') {
    throw new Error('Specify a string endpoint URL.')
  }
  /* if (!schema) {
   *   throw new Error('Specify one of the exported Schemas.')
   * }*/
  if (!Array.isArray(types) || types.length !== 3) {
    throw new Error('Expected an array of three action types.')
  }
  if (!types.every(type => typeof type === 'string')) {
    throw new Error('Expected action types to be strings.')
  }

  function actionWith(data) {
    const finalAction = Object.assign({}, action, data)
    delete finalAction[CALL_API]
    return finalAction
  }

  const [ requestType, successType, failureType ] = types
  next(actionWith({ type: requestType }))

  //return callApi(endpoint, schema).then(
  return callApi(endpoint, authenticated).then(
    response => next(actionWith({
      response,
      type: successType
    })),
    error => next(actionWith({
      type: failureType,
      error: error.message || 'Something bad happened'
    }))
  )
}
<|endoftext|>


<commit_before>'use strict';

const Benchmark = require('benchmark');
const Table = require('cli-table2');
const ora = require('ora');
const React = require('react');

const underscore = require('underscore').memoize;
const lodash = require('lodash').memoize;
const ramda = require('ramda').memoize;
const memoizee = require('memoizee');
const fastMemoize = require('fast-memoize');
const addyOsmani = require('./addy-osmani');
const memoizerific = require('memoizerific');
const lruMemoize = require('lru-memoize').default;
const moize = require('../lib');

const deepEquals = require('lodash').isEqual;

const showResults = (benchmarkResults) => {
  const table = new Table({
    head: ['Name', 'Ops / sec', 'Relative margin of error', 'Sample size']
  });

  benchmarkResults.forEach((result) => {
    const name = result.target.name;
    const opsPerSecond = result.target.hz.toLocaleString('en-US', {
      maximumFractionDigits: 0
    });
    const relativeMarginOferror = `± ${result.target.stats.rme.toFixed(2)}%`;
    const sampleSize = result.target.stats.sample.length;

    table.push([name, opsPerSecond, relativeMarginOferror, sampleSize]);
  });

  console.log(table.toString()); // eslint-disable-line no-console
};

const sortDescResults = (benchmarkResults) => {
  return benchmarkResults.sort((a, b) => {
    return a.target.hz < b.target.hz ? 1 : -1;
  });
};

const spinner = ora('Running benchmark');

let results = [];

const onCycle = (event) => {
  results.push(event);
  ora(event.target.name).succeed();
};

const onComplete = () => {
  spinner.stop();

  const orderedBenchmarkResults = sortDescResults(results);

  showResults(orderedBenchmarkResults);
};

const fibonacci = (number) => {
  return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2);
};

const fibonacciMultiplePrimitive = (number, isComplete) => {
  if (isComplete) {
    return number;
  }

  const firstValue = number - 1;
  const secondValue = number - 2;

  return (
    fibonacciMultiplePrimitive(firstValue, firstValue < 2) + fibonacciMultiplePrimitive(secondValue, secondValue < 2)
  );
};

const fibonacciMultipleObject = (number, check) => {
  if (check.isComplete) {
    return number;
  }

  const firstValue = number - 1;
  const secondValue = number - 2;

  return (
    fibonacciMultipleObject(firstValue, {
      isComplete: firstValue < 2
    }) +
    fibonacciMultipleObject(secondValue, {
      isComplete: secondValue < 2
    })
  );
};

const fibonacciMultipleDeepEqual = ({number}) => {
  return number < 2
    ? number
    : fibonacciMultipleDeepEqual({number: number - 1}) + fibonacciMultipleDeepEqual({number: number - 2});
};

const runSingleParameterSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Single parameter');
  const fibonacciNumber = 35;

  const mUnderscore = underscore(fibonacci);
  const mLodash = lodash(fibonacci);
  const mRamda = ramda(fibonacci);
  const mMemoizee = memoizee(fibonacci);
  const mFastMemoize = fastMemoize(fibonacci);
  const mAddyOsmani = addyOsmani(fibonacci);
  const mMemoizerific = memoizerific(Infinity)(fibonacci);
  const mLruMemoize = lruMemoize(Infinity)(fibonacci);
  const mMoize = moize(fibonacci);
  const mMoizeSerialize = moize.serialize(fibonacci);

  return new Promise((resolve) => {
    fibonacciSuite
      .add('underscore', () => {
        mUnderscore(fibonacciNumber);
      })
      .add('lodash', () => {
        mLodash(fibonacciNumber);
      })
      .add('memoizee', () => {
        mMemoizee(fibonacciNumber);
      })
      .add('ramda', () => {
        mRamda(fibonacciNumber);
      })
      .add('fast-memoize', () => {
        mFastMemoize(fibonacciNumber);
      })
      .add('addy-osmani', () => {
        mAddyOsmani(fibonacciNumber);
      })
      .add('memoizerific', () => {
        mMemoizerific(fibonacciNumber);
      })
      .add('lru-memoize', () => {
        mLruMemoize(fibonacciNumber);
      })
      .add('moize', () => {
        mMoize(fibonacciNumber);
      })
      .add('moize (serialized)', () => {
        mMoizeSerialize(fibonacciNumber);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for functions with a single parameter...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

const runMultiplePrimitiveSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Primitive)');
  const fibonacciNumber = 35;
  const isComplete = false;

  const mMemoizee = memoizee(fibonacciMultiplePrimitive);
  const mFastMemoize = fastMemoize(fibonacciMultiplePrimitive);
  const mAddyOsmani = addyOsmani(fibonacciMultiplePrimitive);
  const mMemoizerific = memoizerific(Infinity)(fibonacciMultiplePrimitive);
  const mLruMemoize = lruMemoize(Infinity)(fibonacciMultiplePrimitive);
  const mMoize = moize(fibonacciMultiplePrimitive);
  const mMoizeSerialize = moize.serialize(fibonacciMultiplePrimitive);

  return new Promise((resolve) => {
    fibonacciSuite
      .add('memoizee', () => {
        mMemoizee(fibonacciNumber, isComplete);
      })
      .add('fast-memoize', () => {
        mFastMemoize(fibonacciNumber, isComplete);
      })
      .add('addy-osmani', () => {
        mAddyOsmani(fibonacciNumber, isComplete);
      })
      .add('memoizerific', () => {
        mMemoizerific(fibonacciNumber, isComplete);
      })
      .add('lru-memoize', () => {
        mLruMemoize(fibonacciNumber, isComplete);
      })
      .add('moize', () => {
        mMoize(fibonacciNumber, isComplete);
      })
      .add('moize (serialized)', () => {
        mMoizeSerialize(fibonacciNumber, isComplete);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for functions with multiple parameters that contain only primitives...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

const runMultipleObjectSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Object)');
  const fibonacciNumber = 35;
  const isComplete = {
    isComplete: false
  };

  const mMemoizee = memoizee(fibonacciMultipleObject);
  const mFastMemoize = fastMemoize(fibonacciMultipleObject);
  const mAddyOsmani = addyOsmani(fibonacciMultipleObject);
  const mMemoizerific = memoizerific(Infinity)(fibonacciMultipleObject);
  const mLruMemoize = lruMemoize(Infinity)(fibonacciMultipleObject);
  const mMoize = moize(fibonacciMultipleObject);
  const mMoizeSerialize = moize.serialize(fibonacciMultipleObject);

  return new Promise((resolve) => {
    fibonacciSuite
      .add('memoizee', () => {
        mMemoizee(fibonacciNumber, isComplete);
      })
      .add('fast-memoize', () => {
        mFastMemoize(fibonacciNumber, isComplete);
      })
      .add('addy-osmani', () => {
        mAddyOsmani(fibonacciNumber, isComplete);
      })
      .add('memoizerific', () => {
        mMemoizerific(fibonacciNumber, isComplete);
      })
      .add('lru-memoize', () => {
        mLruMemoize(fibonacciNumber, isComplete);
      })
      .add('moize', () => {
        mMoize(fibonacciNumber, isComplete);
      })
      .add('moize (serialized)', () => {
        mMoizeSerialize(fibonacciNumber, isComplete);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for functions with multiple parameters that contain objects...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

const runAlternativeOptionsSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Object)');
  const fibonacciNumber = {
    number: 35
  };
  const props = {
    foo: {
      foo: {
        foo: 'foo'
      }
    },
    bar: {
      bar: {
        bar: 'bar'
      }
    }
  };
  const context = {};

  const mMoizeDeep = moize(fibonacciMultipleDeepEqual, {
    equals: deepEquals
  });
  const mMoizeSerialize = moize.serialize(fibonacciMultipleDeepEqual);

  const Foo = (props) => {
    return React.createElement('div', null, JSON.stringify(props));
  };

  const mMoizeReact = moize.react(Foo);
  const mMoizeReactDeep = moize.react(Foo, {
    equals(newKey, existingKey) {
      return deepEquals(newKey[0], existingKey[0]) && deepEquals(newKey[1], existingKey[1]);
    }
  });
  const mMoizeReactOld = moize(Foo, {
    maxArgs: 2,
    serialize: true,
    serializeFunctions: true
  });

  const chooseSpecificArgs = (foo, bar, baz) => {
    return [foo, baz];
  };

  const mMoizeSpecificArgs = moize(chooseSpecificArgs, {
    transformArgs(args) {
      let index = args.length,
          newKey = [];

      while (--index) {
        newKey[index - 1] = args[index];
      }

      return newKey;
    }
  });
  const mMoizeMaxArgs = moize.maxArgs(2)((one, two, three) => {
    return [one, two, three];
  });

  const object1 = {foo: 'bar'};
  const object2 = ['foo'];

  return new Promise((resolve) => {
    fibonacciSuite
      .add('moize deep equals (lodash isEqual)', () => {
        mMoizeDeep(fibonacciNumber);
      })
      .add('moize deep equals (serialized)', () => {
        mMoizeSerialize(fibonacciNumber);
      })
      .add('moize react (v2)', () => {
        mMoizeReactOld(props, context);
      })
      .add('moize react (v3)', () => {
        mMoizeReact(props, context);
      })
      .add('moize react deep equals (lodash isEqual)', () => {
        mMoizeReactDeep(props, context);
      })
      .add('moize (transform args)', () => {
        mMoizeSpecificArgs('foo', object1, object2);
      })
      .add('moize (maximum args)', () => {
        mMoizeMaxArgs('foo', object1, object2);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for alternative cache types...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

runAlternativeOptionsSuite();

// runSingleParameterSuite()
//   .then(runMultiplePrimitiveSuite)
//   .then(runMultipleObjectSuite)
//   .then(runAlternativeOptionsSuite);
<commit_msg>Return benchmark to running everything<commit_after>'use strict';

const Benchmark = require('benchmark');
const Table = require('cli-table2');
const ora = require('ora');
const React = require('react');

const underscore = require('underscore').memoize;
const lodash = require('lodash').memoize;
const ramda = require('ramda').memoize;
const memoizee = require('memoizee');
const fastMemoize = require('fast-memoize');
const addyOsmani = require('./addy-osmani');
const memoizerific = require('memoizerific');
const lruMemoize = require('lru-memoize').default;
const moize = require('../lib');

const deepEquals = require('lodash').isEqual;

const showResults = (benchmarkResults) => {
  const table = new Table({
    head: ['Name', 'Ops / sec', 'Relative margin of error', 'Sample size']
  });

  benchmarkResults.forEach((result) => {
    const name = result.target.name;
    const opsPerSecond = result.target.hz.toLocaleString('en-US', {
      maximumFractionDigits: 0
    });
    const relativeMarginOferror = `± ${result.target.stats.rme.toFixed(2)}%`;
    const sampleSize = result.target.stats.sample.length;

    table.push([name, opsPerSecond, relativeMarginOferror, sampleSize]);
  });

  console.log(table.toString()); // eslint-disable-line no-console
};

const sortDescResults = (benchmarkResults) => {
  return benchmarkResults.sort((a, b) => {
    return a.target.hz < b.target.hz ? 1 : -1;
  });
};

const spinner = ora('Running benchmark');

let results = [];

const onCycle = (event) => {
  results.push(event);
  ora(event.target.name).succeed();
};

const onComplete = () => {
  spinner.stop();

  const orderedBenchmarkResults = sortDescResults(results);

  showResults(orderedBenchmarkResults);
};

const fibonacci = (number) => {
  return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2);
};

const fibonacciMultiplePrimitive = (number, isComplete) => {
  if (isComplete) {
    return number;
  }

  const firstValue = number - 1;
  const secondValue = number - 2;

  return (
    fibonacciMultiplePrimitive(firstValue, firstValue < 2) + fibonacciMultiplePrimitive(secondValue, secondValue < 2)
  );
};

const fibonacciMultipleObject = (number, check) => {
  if (check.isComplete) {
    return number;
  }

  const firstValue = number - 1;
  const secondValue = number - 2;

  return (
    fibonacciMultipleObject(firstValue, {
      isComplete: firstValue < 2
    }) +
    fibonacciMultipleObject(secondValue, {
      isComplete: secondValue < 2
    })
  );
};

const fibonacciMultipleDeepEqual = ({number}) => {
  return number < 2
    ? number
    : fibonacciMultipleDeepEqual({number: number - 1}) + fibonacciMultipleDeepEqual({number: number - 2});
};

const runSingleParameterSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Single parameter');
  const fibonacciNumber = 35;

  const mUnderscore = underscore(fibonacci);
  const mLodash = lodash(fibonacci);
  const mRamda = ramda(fibonacci);
  const mMemoizee = memoizee(fibonacci);
  const mFastMemoize = fastMemoize(fibonacci);
  const mAddyOsmani = addyOsmani(fibonacci);
  const mMemoizerific = memoizerific(Infinity)(fibonacci);
  const mLruMemoize = lruMemoize(Infinity)(fibonacci);
  const mMoize = moize(fibonacci);
  const mMoizeSerialize = moize.serialize(fibonacci);

  return new Promise((resolve) => {
    fibonacciSuite
      .add('underscore', () => {
        mUnderscore(fibonacciNumber);
      })
      .add('lodash', () => {
        mLodash(fibonacciNumber);
      })
      .add('memoizee', () => {
        mMemoizee(fibonacciNumber);
      })
      .add('ramda', () => {
        mRamda(fibonacciNumber);
      })
      .add('fast-memoize', () => {
        mFastMemoize(fibonacciNumber);
      })
      .add('addy-osmani', () => {
        mAddyOsmani(fibonacciNumber);
      })
      .add('memoizerific', () => {
        mMemoizerific(fibonacciNumber);
      })
      .add('lru-memoize', () => {
        mLruMemoize(fibonacciNumber);
      })
      .add('moize', () => {
        mMoize(fibonacciNumber);
      })
      .add('moize (serialized)', () => {
        mMoizeSerialize(fibonacciNumber);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for functions with a single parameter...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

const runMultiplePrimitiveSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Primitive)');
  const fibonacciNumber = 35;
  const isComplete = false;

  const mMemoizee = memoizee(fibonacciMultiplePrimitive);
  const mFastMemoize = fastMemoize(fibonacciMultiplePrimitive);
  const mAddyOsmani = addyOsmani(fibonacciMultiplePrimitive);
  const mMemoizerific = memoizerific(Infinity)(fibonacciMultiplePrimitive);
  const mLruMemoize = lruMemoize(Infinity)(fibonacciMultiplePrimitive);
  const mMoize = moize(fibonacciMultiplePrimitive);
  const mMoizeSerialize = moize.serialize(fibonacciMultiplePrimitive);

  return new Promise((resolve) => {
    fibonacciSuite
      .add('memoizee', () => {
        mMemoizee(fibonacciNumber, isComplete);
      })
      .add('fast-memoize', () => {
        mFastMemoize(fibonacciNumber, isComplete);
      })
      .add('addy-osmani', () => {
        mAddyOsmani(fibonacciNumber, isComplete);
      })
      .add('memoizerific', () => {
        mMemoizerific(fibonacciNumber, isComplete);
      })
      .add('lru-memoize', () => {
        mLruMemoize(fibonacciNumber, isComplete);
      })
      .add('moize', () => {
        mMoize(fibonacciNumber, isComplete);
      })
      .add('moize (serialized)', () => {
        mMoizeSerialize(fibonacciNumber, isComplete);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for functions with multiple parameters that contain only primitives...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

const runMultipleObjectSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Object)');
  const fibonacciNumber = 35;
  const isComplete = {
    isComplete: false
  };

  const mMemoizee = memoizee(fibonacciMultipleObject);
  const mFastMemoize = fastMemoize(fibonacciMultipleObject);
  const mAddyOsmani = addyOsmani(fibonacciMultipleObject);
  const mMemoizerific = memoizerific(Infinity)(fibonacciMultipleObject);
  const mLruMemoize = lruMemoize(Infinity)(fibonacciMultipleObject);
  const mMoize = moize(fibonacciMultipleObject);
  const mMoizeSerialize = moize.serialize(fibonacciMultipleObject);

  return new Promise((resolve) => {
    fibonacciSuite
      .add('memoizee', () => {
        mMemoizee(fibonacciNumber, isComplete);
      })
      .add('fast-memoize', () => {
        mFastMemoize(fibonacciNumber, isComplete);
      })
      .add('addy-osmani', () => {
        mAddyOsmani(fibonacciNumber, isComplete);
      })
      .add('memoizerific', () => {
        mMemoizerific(fibonacciNumber, isComplete);
      })
      .add('lru-memoize', () => {
        mLruMemoize(fibonacciNumber, isComplete);
      })
      .add('moize', () => {
        mMoize(fibonacciNumber, isComplete);
      })
      .add('moize (serialized)', () => {
        mMoizeSerialize(fibonacciNumber, isComplete);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for functions with multiple parameters that contain objects...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

const runAlternativeOptionsSuite = () => {
  const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Object)');
  const fibonacciNumber = {
    number: 35
  };
  const props = {
    foo: {
      foo: {
        foo: 'foo'
      }
    },
    bar: {
      bar: {
        bar: 'bar'
      }
    }
  };
  const context = {};

  const mMoizeDeep = moize(fibonacciMultipleDeepEqual, {
    equals: deepEquals
  });
  const mMoizeSerialize = moize.serialize(fibonacciMultipleDeepEqual);

  const Foo = (props) => {
    return React.createElement('div', null, JSON.stringify(props));
  };

  const mMoizeReact = moize.react(Foo);
  const mMoizeReactDeep = moize.react(Foo, {
    equals(newKey, existingKey) {
      return deepEquals(newKey[0], existingKey[0]) && deepEquals(newKey[1], existingKey[1]);
    }
  });
  const mMoizeReactOld = moize(Foo, {
    maxArgs: 2,
    serialize: true,
    serializeFunctions: true
  });

  const chooseSpecificArgs = (foo, bar, baz) => {
    return [foo, baz];
  };

  const mMoizeSpecificArgs = moize(chooseSpecificArgs, {
    transformArgs(args) {
      let index = args.length,
          newKey = [];

      while (--index) {
        newKey[index - 1] = args[index];
      }

      return newKey;
    }
  });
  const mMoizeMaxArgs = moize.maxArgs(2)((one, two, three) => {
    return [one, two, three];
  });

  const object1 = {foo: 'bar'};
  const object2 = ['foo'];

  return new Promise((resolve) => {
    fibonacciSuite
      .add('moize deep equals (lodash isEqual)', () => {
        mMoizeDeep(fibonacciNumber);
      })
      .add('moize deep equals (serialized)', () => {
        mMoizeSerialize(fibonacciNumber);
      })
      .add('moize react (v2)', () => {
        mMoizeReactOld(props, context);
      })
      .add('moize react (v3)', () => {
        mMoizeReact(props, context);
      })
      .add('moize react deep equals (lodash isEqual)', () => {
        mMoizeReactDeep(props, context);
      })
      .add('moize (transform args)', () => {
        mMoizeSpecificArgs('foo', object1, object2);
      })
      .add('moize (maximum args)', () => {
        mMoizeMaxArgs('foo', object1, object2);
      })
      .on('start', () => {
        console.log(''); // eslint-disable-line no-console
        console.log('Starting cycles for alternative cache types...'); // eslint-disable-line no-console

        results = [];

        spinner.start();
      })
      .on('cycle', onCycle)
      .on('complete', () => {
        onComplete();
        resolve();
      })
      .run({
        async: true
      });
  });
};

// runAlternativeOptionsSuite();

runSingleParameterSuite()
  .then(runMultiplePrimitiveSuite)
  .then(runMultipleObjectSuite)
  .then(runAlternativeOptionsSuite);
<|endoftext|>


<commit_before>var fs = require('fs');
var ursa = require('ursa');
var dgram = require('dgram');
var tls = require('tls');
var net = require('net');
var mongoose = require('mongoose');

var message_parser = function(){

	/************************************************************************/
	//Webサーバーから送られてきた子機への命令
	var command = {};
	var receive_command = function(str){
		var obj = JSON.parse(str);
		command.type = obj["type"];
		command.functionName = obj["value"]["functionName"];
		command.id = obj["value"]["id"];
		command.args = JSON.stringify(obj["value"]["args"]);
	};

	var send_command = function(str){
		receive_command(str);
		var obj = {
			"id" : command.id,
			"message" : {
				"type" : command.type,
				"value" : {
					"functionName" : command.functionName,
					"args" : JSON.parse(command.args)
				}
			}
		};
		console.log("[RECIVE COMMAND] id: " + command.id + " functionName: " + command.functionName);
		var str = JSON.stringify(obj);
		return str;
	};
	/************************************************************************/
	//子機から送られてきたWebサーバーへの機能の通知
	var functions = {};
	var receive_functions = function(str){
		var obj = JSON.parse(str);
		functions.name = obj["name"];
		functions.id = obj["id"];
		functions.dest = obj["dest"];
		functions.type = obj["type"];
		functions.value = [];
		for(var i=0;i<obj["value"].length;i++){
			functions.value[i] = JSON.stringify(obj["value"][i]);
		}
	};

	var send_functions = function(str){
		receive_functions(str);
		var obj = {
			"type" : "notice",
			"value" : {
				"id" : functions.id,
				"name" : functions.name,
				"functions" : []
			}
		};
		for(var i=0;i<functions.value.length;i++){
			obj["value"]["functions"][i] = JSON.parse(functions.value[i]);
		}
		console.log("[SEND FUNCTION] id: " + functions.id + " name: " + functions.name);
		var str = JSON.stringify(obj);
		return str;
	};
	/************************************************************************/
	//子機から命令の結果
	var result = {};
	var receive_result = function(str){
		var obj = JSON.parse(str);
		result.name = obj["name"];
		result.id = obj["id"];
		result.dest = obj["dest"];
		result.type = obj["type"];
		result.value = obj["value"];

	};

	var send_result = function(str){
		receive_result(str);
		if(result.value.result == undefined || result.value.result == null){
			result.value.result = {};
		}
		var obj = {
			"type" : "result",
			"value" : {
				"id" : result.id,
				"functionName" : result.value.functionName,
				"hasError": result.value.hasError,
				"result" : result.value.result
			}
		};
		console.log("[FUNCTION RESULT] id: " + result.id + " name: " + result.value.functionName);
		var str = JSON.stringify(obj);
		return str;
	};
	/*******************************************************************************/
	//サーバー&クライアント
	var th = this;

	var check_message = function(str){
		var obj = JSON.parse(str);
		switch(obj["type"]){
			//子機から機能の通知
			case 'function':
				var send_json = send_functions(str);
				webclient_sender.write(send_json);
				break;
			//子機からの命令結果
			case 'result':
			case 'message':
				var send_json = send_result(str);
				webclient_sender.write(send_json);
			    break;
			//Webサーバーからの命令
			case 'call':
				var send_json = send_command(str);
				children_sender.write(send_json);
				break;
		}
	};

	var message_server = new net.Server(function(socket){
		socket.on('data',function(str){
			check_message(str);
		});
	});

	var webclient_server_port = 10000;
	var webclient_sender = new net.Socket();
	webclient_sender.on('error',function(error){});
	webclient_sender.on('close',function(){
		webclient_sender.setTimeout(500,function(){
			webclient_sender.connect(webclient_server_port);
		});
	});

	var children_server_port = 20000;
	var children_sender = new net.Socket();
	children_sender.on('error',function(error){});
	children_sender.on('close',function(error){
		children_sender.setTimeout(500,function(){
			children_sender.connect(children_server_port);
		});
	});

	this.start = function(){
		message_server.listen(12000);
		webclient_sender.connect(this.webclient_server_port);
		children_sender.connect(this.children_server_port);
	};
}

module.exports = message_parser;
<commit_msg>Add Children name value for result object<commit_after>var fs = require('fs');
var ursa = require('ursa');
var dgram = require('dgram');
var tls = require('tls');
var net = require('net');
var mongoose = require('mongoose');

var message_parser = function(){

	/************************************************************************/
	//Webサーバーから送られてきた子機への命令
	var command = {};
	var receive_command = function(str){
		var obj = JSON.parse(str);
		command.type = obj["type"];
		command.functionName = obj["value"]["functionName"];
		command.id = obj["value"]["id"];
		command.args = JSON.stringify(obj["value"]["args"]);
	};

	var send_command = function(str){
		receive_command(str);
		var obj = {
			"id" : command.id,
			"message" : {
				"type" : command.type,
				"value" : {
					"functionName" : command.functionName,
					"args" : JSON.parse(command.args)
				}
			}
		};
		console.log("[RECIVE COMMAND] id: " + command.id + " functionName: " + command.functionName);
		var str = JSON.stringify(obj);
		return str;
	};
	/************************************************************************/
	//子機から送られてきたWebサーバーへの機能の通知
	var functions = {};
	var receive_functions = function(str){
		var obj = JSON.parse(str);
		functions.name = obj["name"];
		functions.id = obj["id"];
		functions.dest = obj["dest"];
		functions.type = obj["type"];
		functions.value = [];
		for(var i=0;i<obj["value"].length;i++){
			functions.value[i] = JSON.stringify(obj["value"][i]);
		}
	};

	var send_functions = function(str){
		receive_functions(str);
		var obj = {
			"type" : "notice",
			"value" : {
				"id" : functions.id,
				"name" : functions.name,
				"functions" : []
			}
		};
		for(var i=0;i<functions.value.length;i++){
			obj["value"]["functions"][i] = JSON.parse(functions.value[i]);
		}
		console.log("[SEND FUNCTION] id: " + functions.id + " name: " + functions.name);
		var str = JSON.stringify(obj);
		return str;
	};
	/************************************************************************/
	//子機から命令の結果
	var result = {};
	var receive_result = function(str){
		var obj = JSON.parse(str);
		result.name = obj["name"];
		result.id = obj["id"];
		result.dest = obj["dest"];
		result.type = obj["type"];
		result.value = obj["value"];

	};

	var send_result = function(str){
		receive_result(str);
		if(result.value.result == undefined || result.value.result == null){
			result.value.result = {};
		}
		var obj = {
			"type" : "result",
			"value" : {
				"id" : result.id,
				"name" : result.name,
				"functionName" : result.value.functionName,
				"hasError": result.value.hasError,
				"result" : result.value.result
			}
		};
		console.log("[FUNCTION RESULT] id: " + result.id + " name: " + result.value.functionName);
		var str = JSON.stringify(obj);
		return str;
	};
	/*******************************************************************************/
	//サーバー&クライアント
	var th = this;

	var check_message = function(str){
		var obj = JSON.parse(str);
		switch(obj["type"]){
			//子機から機能の通知
			case 'function':
				var send_json = send_functions(str);
				webclient_sender.write(send_json);
				break;
			//子機からの命令結果
			case 'result':
			case 'message':
				var send_json = send_result(str);
				webclient_sender.write(send_json);
			    break;
			//Webサーバーからの命令
			case 'call':
				var send_json = send_command(str);
				children_sender.write(send_json);
				break;
		}
	};

	var message_server = new net.Server(function(socket){
		socket.on('data',function(str){
			check_message(str);
		});
	});

	var webclient_server_port = 10000;
	var webclient_sender = new net.Socket();
	webclient_sender.on('error',function(error){});
	webclient_sender.on('close',function(){
		webclient_sender.setTimeout(500,function(){
			webclient_sender.connect(webclient_server_port);
		});
	});

	var children_server_port = 20000;
	var children_sender = new net.Socket();
	children_sender.on('error',function(error){});
	children_sender.on('close',function(error){
		children_sender.setTimeout(500,function(){
			children_sender.connect(children_server_port);
		});
	});

	this.start = function(){
		message_server.listen(12000);
		webclient_sender.connect(this.webclient_server_port);
		children_sender.connect(this.children_server_port);
	};
}

module.exports = message_parser;
<|endoftext|>