<commit_before>import Baobab from 'baobab';
import isEqual from 'lodash.isequal';

export default class State {
    constructor(initialState = {}, options) {
        this._tree = new Baobab(initialState, options);
        this._watchingQueue = [];
        this._watchingPaths = [];
        this._fetchers = [];
        this._senders = [];

        this._onPathGet = this._onPathGet.bind(this);
        this._onPathWrite = this._onPathWrite.bind(this);
    }

    /**
     * Fetchers API
     */
    _onPathGet(e) {
        const path = e.data.path;

        this._fetchers.forEach(this._processFetcherWithPath.bind(this, path));
    }

    _isPathMatchedBy(cursorPath, matchersFactories) {
        return matchersFactories.some(matchersFactory => {
            return matchersFactory(cursorPath).some(matcher => {
                return matcher.path.every((chunk, i) => {
                    return isEqual(chunk, cursorPath[i]);
                });
            });
        });
    }

    _isPathMatched(cursorPath) {
        return this._fetchers.some(this._isPathMatchedBy.bind(this, cursorPath));
    }

    _processFetcherWithPath(cursorPath, matchersFactories) {
        const isDataExists = this.exists(cursorPath);

        matchersFactories.forEach(matchersFactory => {
            matchersFactory(cursorPath).forEach(matcher => {
                const matched = matcher.path.every((chunk, i) => {
                    return chunk === cursorPath[i];
                });

                // if cursor path is matched by some fetcher and
                // such data is not exists yet
                if (matched && !isDataExists) {
                    matcher.callback();
                }
            });
        });
    }

    _registerFetcher(fetcher) {
        // add `get`-listener if there is no fetchers yet
        if (this._fetchers.length === 0) {
            this._tree.on('get', this._onPathGet);
        }

        // move matched by new fetcher paths from queue
        this._watchingQueue = this._watchingQueue.filter(queuePath => {
            if (this._isPathMatchedBy(queuePath, fetcher)) {
                this._watchingPaths.push(queuePath);

                return false;
            }

            return true;
        });

        // process all watching paths with the new fetcher
        this._watchingPaths.forEach(watchingPath => {
            this._processFetcherWithPath(watchingPath, fetcher);
        });

        // store fetcher
        this._fetchers.push(fetcher);
    }

    _unregisterFetcher(fetcher) {
        // remove fetcher from registered fetchers
        this._fetchers = this._fetchers.filter(registeredFetcher => {
            return registeredFetcher !== fetcher;
        });

        // move watched by nobody paths to queue
        this._watchingPaths = this._watchingPaths.filter(watchingPath => {
            if (!this._isPathMatched(watchingPath)) {
                this._watchingQueue.push(watchingPath);

                return false;
            }

            return true;
        });

        // remove `get`-listener if there is no fetchers anymore
        if (this._fetchers.length === 0) {
            this._tree.off('get', this._onPathGet);
        }
    }

    _addToWatchingQueue(cursorPath) {
        // only if path is not matched by registered fetchers
        if (!this._isPathMatched(cursorPath)) {
            this._watchingQueue.push(cursorPath);
        }
    }

    _removeFromWatchingQueue(cursorPath) {
        // only if path is not matched by registered fetchers
        if (!this._isPathMatched(cursorPath)) {
            // remove it from watching
            this._watchingPaths = this._watchingPaths.filter(watchingPath => {
                return !isEqual(cursorPath, watchingPath);
            });

            // and from queue
            this._watchingQueue = this._watchingQueue.filter(watchingPath => {
                return !isEqual(cursorPath, watchingPath);
            });
        }
    }

    /**
     * Senders API
     */
    _onPathWrite(e) {
        const path = e.data.path;

        this._senders.forEach(this._processSenderWithPath.bind(this, path));
    }

    _processSenderWithPath(cursorPath, matchersFactories) {
        const isDataExists = this.exists(cursorPath);

        // it's intentionally undefined by default
        // because Baobab returns undefined if there is no value in path
        let value;

        // we retrieve value only if it is exists
        // so DataFetcher wouldn't trigger when we don't want to
        if (isDataExists) {
            value = this.getIn(cursorPath);
        }

        matchersFactories.forEach(matchersFactory => {
            matchersFactory(cursorPath).forEach(matcher => {
                const matched = matcher.path.every((chunk, i) => {
                    return chunk === cursorPath[i];
                });

                // trigger callback if path is matched
                if (matched) {
                    matcher.callback(value);
                }
            });
        });
    }

    _registerSender(sender) {
        // add `write`-listener if there is no senders yet
        if (this._senders.length === 0) {
            this._tree.on('write', this._onPathWrite);
        }

        // store sender
        this._senders.push(sender);
    }

    _unregisterSender(sender) {
        // remove sender from registered senders
        this._senders = this._senders.filter(registeredSender => {
            return registeredSender !== sender;
        });

        // remove `write`-listener if there is no senders anymore
        if (this._senders.length === 0) {
            this._tree.off('write', this._onPathWrite);
        }
    }

    /**
     * Public API
     */
    getTree() {
        return this._tree;
    }

    get() {
        return this._tree.get();
    }

    getIn(cursorPath) {
        return this._tree.get(cursorPath);
    }

    set(data) {
        this._tree.set(data);
    }

    setIn(cursorPath, data) {
        this._tree.set(cursorPath, data);
    }

    unset() {
        this._tree.unset();
    }

    unsetIn(cursorPath) {
        this._tree.unset(cursorPath);
    }

    exists(path) {
        return this._tree.select(path).exists();
    }
}
<commit_msg>:heavy_check_mark: fix incorrect watchingQueue/watchingPath handling<commit_after>import Baobab from 'baobab';
import isEqual from 'lodash.isequal';

export default class State {
    constructor(initialState = {}, options) {
        this._tree = new Baobab(initialState, options);
        this._watchingQueue = [];
        this._watchingPaths = [];
        this._fetchers = [];
        this._senders = [];

        this._onPathGet = this._onPathGet.bind(this);
        this._onPathWrite = this._onPathWrite.bind(this);
    }

    /**
     * Fetchers API
     */
    _onPathGet(e) {
        const path = e.data.path;

        this._fetchers.forEach(this._processFetcherWithPath.bind(this, path));
    }

    _isPathMatchedBy(cursorPath, matchersFactories) {
        return matchersFactories.some(matchersFactory => {
            return matchersFactory(cursorPath).some(matcher => {
                return matcher.path.every((chunk, i) => {
                    return isEqual(chunk, cursorPath[i]);
                });
            });
        });
    }

    _isPathMatched(cursorPath) {
        return this._fetchers.some(this._isPathMatchedBy.bind(this, cursorPath));
    }

    _processFetcherWithPath(cursorPath, matchersFactories) {
        const isDataExists = this.exists(cursorPath);

        matchersFactories.forEach(matchersFactory => {
            matchersFactory(cursorPath).forEach(matcher => {
                const matched = matcher.path.every((chunk, i) => {
                    return chunk === cursorPath[i];
                });

                // if cursor path is matched by some fetcher and
                // such data is not exists yet
                if (matched && !isDataExists) {
                    matcher.callback();
                }
            });
        });
    }

    _registerFetcher(fetcher) {
        // add `get`-listener if there is no fetchers yet
        if (this._fetchers.length === 0) {
            this._tree.on('get', this._onPathGet);
        }

        // move matched by new fetcher paths from queue
        this._watchingQueue = this._watchingQueue.filter(queuePath => {
            if (this._isPathMatchedBy(queuePath, fetcher)) {
                this._watchingPaths.push(queuePath);

                return false;
            }

            return true;
        });

        // process all watching paths with the new fetcher
        this._watchingPaths.forEach(watchingPath => {
            this._processFetcherWithPath(watchingPath, fetcher);
        });

        // store fetcher
        this._fetchers.push(fetcher);
    }

    _unregisterFetcher(fetcher) {
        // remove fetcher from registered fetchers
        this._fetchers = this._fetchers.filter(registeredFetcher => {
            return registeredFetcher !== fetcher;
        });

        // move watched by nobody paths to queue
        this._watchingPaths = this._watchingPaths.filter(watchingPath => {
            if (!this._isPathMatched(watchingPath)) {
                this._watchingQueue.push(watchingPath);

                return false;
            }

            return true;
        });

        // remove `get`-listener if there is no fetchers anymore
        if (this._fetchers.length === 0) {
            this._tree.off('get', this._onPathGet);
        }
    }

    _addToWatchingQueue(cursorPath) {
        // only if path is not matched by registered fetchers
        if (!this._isPathMatched(cursorPath)) {
            this._watchingQueue.push(cursorPath);
        } else {
            this._watchingPaths.push(cursorPath);
        }
    }

    _removeFromWatchingQueue(cursorPath) {
        // remove from queue if path is not matched by registered fetchers
        if (!this._isPathMatched(cursorPath)) {
            this._watchingQueue.splice(this._watchingQueue.indexOf(cursorPath), 1);

        // otherwise remove from watching paths
        } else {
            this._watchingPaths.splice(this._watchingPaths.indexOf(cursorPath), 1);
        }
    }

    /**
     * Senders API
     */
    _onPathWrite(e) {
        const path = e.data.path;

        this._senders.forEach(this._processSenderWithPath.bind(this, path));
    }

    _processSenderWithPath(cursorPath, matchersFactories) {
        const isDataExists = this.exists(cursorPath);

        // it's intentionally undefined by default
        // because Baobab returns undefined if there is no value in path
        let value;

        // we retrieve value only if it is exists
        // so DataFetcher wouldn't trigger when we don't want to
        if (isDataExists) {
            value = this.getIn(cursorPath);
        }

        matchersFactories.forEach(matchersFactory => {
            matchersFactory(cursorPath).forEach(matcher => {
                const matched = matcher.path.every((chunk, i) => {
                    return chunk === cursorPath[i];
                });

                // trigger callback if path is matched
                if (matched) {
                    matcher.callback(value);
                }
            });
        });
    }

    _registerSender(sender) {
        // add `write`-listener if there is no senders yet
        if (this._senders.length === 0) {
            this._tree.on('write', this._onPathWrite);
        }

        // store sender
        this._senders.push(sender);
    }

    _unregisterSender(sender) {
        // remove sender from registered senders
        this._senders = this._senders.filter(registeredSender => {
            return registeredSender !== sender;
        });

        // remove `write`-listener if there is no senders anymore
        if (this._senders.length === 0) {
            this._tree.off('write', this._onPathWrite);
        }
    }

    /**
     * Public API
     */
    getTree() {
        return this._tree;
    }

    get() {
        return this._tree.get();
    }

    getIn(cursorPath) {
        return this._tree.get(cursorPath);
    }

    set(data) {
        this._tree.set(data);
    }

    setIn(cursorPath, data) {
        this._tree.set(cursorPath, data);
    }

    unset() {
        this._tree.unset();
    }

    unsetIn(cursorPath) {
        this._tree.unset(cursorPath);
    }

    exists(path) {
        return this._tree.select(path).exists();
    }
}
<|endoftext|>


<commit_before>var gzip = require('zlib').gzip;
var url = require('url');
var fingerprint = require('./fingerprint');
var testSuffix = /\.[a-zA-Z0-9]+$/;

// the index client
var client;

// index document http headers
var resHeaders = {
    'Cache-Control': 'public, max-age=31536000',
    'Vary': 'Accept-Encoding',
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Encoding': 'gzip',
    'Server': 'JCES'
};

// create file caches
var publicCache = engine.cache.file('public', {wd: engine.repo + 'public/'});
var markupCache = engine.cache.file('markup', {wd: engine.repo + 'markup/', noCompression: true});
var fileClient = engine.cache.file('client', {wd: engine.root + 'lib/client/'});
var fileModule = engine.cache.file('module', {wd: engine.paths.app_modules});

// create a composition cache
var moduleInstanceCache = engine.cache.comp('moduleInstances');

// listen to the script core event
engine.on('script', getScript);

// load html snippets
engine.on('html', getHtml);

/**
 * Send the index document or the requeted public file.
 *
 * @public
 * @param {array} The url paht splitted into an array.
 * @param {object} The http request object.
 * @param {object} The http respone object.
 */
module.exports = function (pathname, req, res) {
    var self = this;

    // send client
    if (pathname === '/') {
        return sendClient(res, req._sidInvalid);
    }

    publicCache.set(pathname, function (err, file) {

        if (err) {

            // return not found if path has a suffix
            if (testSuffix.test(pathname)) {

                res.writeHead(404, {'Content-Type': 'text/plain; charset=utf-8'});
                res.end('File "' + pathname + '" not found.');

                return;
            }

            // send client
            return sendClient(res, req._sidInvalid);
        }

        res.writeHead(200, file.http);
        res.end(file.data);
    });
};

/**
 * Send the index document to the client.
 *
 * @private
 * @param {object} The http response object.
 * @param {boolean} Remove session if true.
 */
function sendClient (res, removeSid) {

    // remove sid
    if (removeSid) {
        resHeaders['Set-Cookie'] = 'sid=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT';
    } else if (resHeaders['Set-Cookie']) {
        delete resHeaders['Set-Cookie'];
    }

    if (client) {
        res.writeHead(200, resHeaders);
        return res.end(client);
    }

    // get the engine client and core modules paths with fingerprints
    // TODO combine the client files in production mode
    fingerprint.addToFiles(null, ['engine.js', 'flow.js', 'utils.js',  'link.js'], function (err, files) {

        // send the error in the browser
        if (err) {
            res.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end(err.toString());
            return;
        }

        // load engine client in a browser
        var script = '';

        // load the core engine modules
        script += '';

        // zip and send index document
        gzip(
            '<!DOCTYPE html>' +
                '<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">' +
                '<link rel="icon" href="/favicon.ico"/>' +
                script +
            '',
            function (err, data) {

                client = data;
                resHeaders['Content-Length'] = client.length;

                resHeaders['Last-Modified'] = new Date().toString();

                res.writeHead(200, resHeaders);
                res.end(client);
            }
        );
    });
}

/**
 * Get the client side module and engine scripts.
 *
 * @public
 * @param {object} The link object.
 */
function getScript (link) {
    var self = this;

    // the module name must be almost alphanumeric
    if (link.pathname.replace(/[^a-z0-9\/\.\-_@\|]|\.\.\//gi, '') !== link.pathname) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(400, 'Incorrect data in module request URL');
    }

    // get client engine files
    if (link.path[0] === 'E') {

        // save compressed/compiled script in cache and send it to the client
        fileClient.set(
            link.path[1],
            function (err, script) {

                if (err) {
                    link.headers = {'content-type': 'text/plain'};
                    link.end(404, err.toString());
                    return;
                }

                link.headers = script.http;
                link.end(200, script.data);
            },
            null,
            'E/'
        );

        return;
    }

    // get module composition
    var module = moduleInstanceCache._modules[link.path[0]];

    // check if module exists
    if (!module || !module.clientDependencies) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(404, 'File not found.');
    }

    // save compressed/compiled script in cache and send it to the client
    fileModule.set(

        // create absolute file path
        link.path[1],

        // callback
        function (err, script) {

            if (err) {
                link.headers = {'content-type': 'text/plain'};
                return link.end(404, err.toString());
            }

            link.headers = script.http;
            link.end(200, script.data);
        },

        // pass an array of allowed files
        module.clientDependencies,

        // prepend module path to the requested file
        link.path[0] + '/'
    );
}

/**
 * Get a HTML snippet.
 *
 * @public
 * @param {object} The link object.
 */
function getHtml (link) {
    var self = this;

    link.data(function (err, path) {

        // ignore errors
        if (err) {
            return;
        }

        // send html snippet to client
        markupCache.set(path, function (err, snippet) {

            if (err || !snippet && !snippet.data) {
                err = err|| 'HTML snippet not found.';
                console.error('lib/static.js#229', err.toString());
                return link.send(err);
            }

            link.send(err, [path, snippet.data.toString('utf8')]);
        });
    });
}
<commit_msg>"clientDependencies" is now "clientLoad"<commit_after>var gzip = require('zlib').gzip;
var url = require('url');
var fingerprint = require('./fingerprint');
var testSuffix = /\.[a-zA-Z0-9]+$/;

// the index client
var client;

// index document http headers
var resHeaders = {
    'Cache-Control': 'public, max-age=31536000',
    'Vary': 'Accept-Encoding',
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Encoding': 'gzip',
    'Server': 'JCES'
};

// create file caches
var publicCache = engine.cache.file('public', {wd: engine.repo + 'public/'});
var markupCache = engine.cache.file('markup', {wd: engine.repo + 'markup/', noCompression: true});
var fileClient = engine.cache.file('client', {wd: engine.root + 'lib/client/'});
var fileModule = engine.cache.file('module', {wd: engine.paths.app_modules});

// create a composition cache
var moduleInstanceCache = engine.cache.comp('moduleInstances');

// listen to the script core event
engine.on('script', getScript);

// load html snippets
engine.on('html', getHtml);

/**
 * Send the index document or the requeted public file.
 *
 * @public
 * @param {array} The url paht splitted into an array.
 * @param {object} The http request object.
 * @param {object} The http respone object.
 */
module.exports = function (pathname, req, res) {
    var self = this;

    // send client
    if (pathname === '/') {
        return sendClient(res, req._sidInvalid);
    }

    publicCache.set(pathname, function (err, file) {

        if (err) {

            // return not found if path has a suffix
            if (testSuffix.test(pathname)) {

                res.writeHead(404, {'Content-Type': 'text/plain; charset=utf-8'});
                res.end('File "' + pathname + '" not found.');

                return;
            }

            // send client
            return sendClient(res, req._sidInvalid);
        }

        res.writeHead(200, file.http);
        res.end(file.data);
    });
};

/**
 * Send the index document to the client.
 *
 * @private
 * @param {object} The http response object.
 * @param {boolean} Remove session if true.
 */
function sendClient (res, removeSid) {

    // remove sid
    if (removeSid) {
        resHeaders['Set-Cookie'] = 'sid=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT';
    } else if (resHeaders['Set-Cookie']) {
        delete resHeaders['Set-Cookie'];
    }

    if (client) {
        res.writeHead(200, resHeaders);
        return res.end(client);
    }

    // get the engine client and core modules paths with fingerprints
    // TODO combine the client files in production mode
    fingerprint.addToFiles(null, ['engine.js', 'flow.js', 'utils.js',  'link.js'], function (err, files) {

        // send the error in the browser
        if (err) {
            res.writeHead(500, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end(err.toString());
            return;
        }

        // load engine client in a browser
        var script = '';

        // load the core engine modules
        script += '';

        // zip and send index document
        gzip(
            '<!DOCTYPE html>' +
                '<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">' +
                '<link rel="icon" href="/favicon.ico"/>' +
                script +
            '',
            function (err, data) {

                client = data;
                resHeaders['Content-Length'] = client.length;

                resHeaders['Last-Modified'] = new Date().toString();

                res.writeHead(200, resHeaders);
                res.end(client);
            }
        );
    });
}

/**
 * Get the client side module and engine scripts.
 *
 * @public
 * @param {object} The link object.
 */
function getScript (link) {
    var self = this;

    // the module name must be almost alphanumeric
    if (link.pathname.replace(/[^a-z0-9\/\.\-_@\|]|\.\.\//gi, '') !== link.pathname) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(400, 'Incorrect data in module request URL');
    }

    // get client engine files
    if (link.path[0] === 'E') {

        // save compressed/compiled script in cache and send it to the client
        fileClient.set(
            link.path[1],
            function (err, script) {

                if (err) {
                    link.headers = {'content-type': 'text/plain'};
                    link.end(404, err.toString());
                    return;
                }

                link.headers = script.http;
                link.end(200, script.data);
            },
            null,
            'E/'
        );

        return;
    }

    // get module composition
    var module = moduleInstanceCache._modules[link.path[0]];

    // check if module exists
    if (!module || !module.clientLoad) {
        link.headers = {'content-type': 'text/plain'};
        return link.end(404, 'File not found.');
    }

    // save compressed/compiled script in cache and send it to the client
    fileModule.set(

        // create absolute file path
        link.path[1],

        // callback
        function (err, script) {

            if (err) {
                link.headers = {'content-type': 'text/plain'};
                return link.end(404, err.toString());
            }

            link.headers = script.http;
            link.end(200, script.data);
        },

        // pass an array of allowed files
        module.clientLoad,

        // prepend module path to the requested file
        link.path[0] + '/'
    );
}

/**
 * Get a HTML snippet.
 *
 * @public
 * @param {object} The link object.
 */
function getHtml (link) {
    var self = this;

    link.data(function (err, path) {

        // ignore errors
        if (err) {
            return;
        }

        // send html snippet to client
        markupCache.set(path, function (err, snippet) {

            if (err || !snippet && !snippet.data) {
                err = err|| 'HTML snippet not found.';
                console.error('lib/static.js#229', err.toString());
                return link.send(err);
            }

            link.send(err, [path, snippet.data.toString('utf8')]);
        });
    });
}
<|endoftext|>


<commit_before>"use strict";

var dgram = require("dgram");
var mersenne = require("./mersenne");
var mt = new mersenne.MersenneTwister19937();

var EPHEMERAL_LIFETIME_MS = 1000;

var Client = function(host, port, socket) {
    this.host = host || "localhost";
    this.port = port || 8125;

    // optional shared socket
    this.socket = socket;

    // when a *shared* socked isn't provided, an ephemeral
    // socket is demand allocated.  This ephemeral socket is closed
    // after being idle for EPHEMERAL_LIFETIME_MS.
    this.ephemeral_socket = this.last_used_timer = null;
};

Client.prototype.timing = function(stat, time, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = time + "|ms";
    self.send(stats, sample_rate, tags);
};

Client.prototype.increment = function(stats, sample_rate, tags) {
    var self = this;
    self.update_stats(stats, 1, sample_rate, tags);
};

Client.prototype.decrement = function(stats, sample_rate, tags) {
    var self = this;
    self.update_stats(stats, - 1, sample_rate, tags);
};

Client.prototype.gauge = function(stat, value, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = value + "|g";
    self.send(stats, sample_rate, tags);
};

Client.prototype.histogram = function(stat, value, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = value + "|h";
    self.send(stats, sample_rate, tags);
};

Client.prototype.update_stats = function(stats, delta, sampleRate, tags) {
    var self = this;
    if (typeof(stats) === "string")
        stats = [stats];
    if (!delta)
        delta = 1;

    var data = {};
    for (var i = 0; i < stats.length; i++)
        data[stats[i]] = delta + "|c";
    self.send(data, sampleRate, tags);
};

// An internal function update the last time the socket was
// used.  This function is called when the socket is used
// and causes demand allocated ephemeral sockets to be closed
// after a period of inactivity.
Client.prototype._update_last_used = function () {
    if (!this.ephemeral_socket)
        return;

    if (this.last_used_timer)
        clearTimeout(this.last_used_timer);

    var self = this;
    this.last_used_timer = setTimeout(function() {
        if (self.ephemeral_socket)
            self.ephemeral_socket.close();
        delete self.ephemeral_socket;
    }, EPHEMERAL_LIFETIME_MS);
};

Client.prototype.send_data = function (buf) {
    var socket;

    if (!this.socket) {
        if (!this.ephemeral_socket) {
            this.ephemeral_socket = dgram.createSocket("udp4");
            this.ephemeral_socket.on("error", function() {});
        }
        socket = this.ephemeral_socket;
    }
    else {
        socket = this.socket;
    }

    this._update_last_used();

    socket.send(buf, 0, buf.length, this.port, this.host);
};

Client.prototype.send = function(data, sample_rate, tags) {
    if (!sample_rate)
        sample_rate = 1;

    var value;
    var sampled_data = {};
    if (sample_rate < 1) {
        if (mt.genrand_real2(0, 1) <= sample_rate) {
            for (stat in data) {
                value = data[stat];
                sampled_data[stat] = value + "|@" + sample_rate;
            }
        }
    }
    else
        sampled_data = data;

    if (tags && Array.isArray(tags)) {
        var tagStr = tags.join(",");

        for (stat in sampled_data)
            sampled_data[stat] = sampled_data[stat] + "|#" + tagStr;
    }

    for (var stat in sampled_data) {
        var send_data = stat + ":" + sampled_data[stat];
        this.send_data(new Buffer(send_data));
    }
};

Client.prototype.close = function() {
    if (this.socket)
        this.socket.close();
    if (this.ephemeral_socket)
        this.ephemeral_socket.close();
    if (this.last_used_timer)
        clearTimeout(this.last_used_timer);

    this.ephemeral_socket =
    this.last_used_timer =
    this.socket = null;
};

exports.StatsD = Client;
<commit_msg>Adding set support.<commit_after>"use strict";

var dgram = require("dgram");
var mersenne = require("./mersenne");
var mt = new mersenne.MersenneTwister19937();

var EPHEMERAL_LIFETIME_MS = 1000;

var Client = function(host, port, socket) {
    this.host = host || "localhost";
    this.port = port || 8125;

    // optional shared socket
    this.socket = socket;

    // when a *shared* socked isn't provided, an ephemeral
    // socket is demand allocated.  This ephemeral socket is closed
    // after being idle for EPHEMERAL_LIFETIME_MS.
    this.ephemeral_socket = this.last_used_timer = null;
};

Client.prototype.timing = function(stat, time, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = time + "|ms";
    self.send(stats, sample_rate, tags);
};

Client.prototype.increment = function(stats, sample_rate, tags) {
    var self = this;
    self.update_stats(stats, 1, sample_rate, tags);
};

Client.prototype.decrement = function(stats, sample_rate, tags) {
    var self = this;
    self.update_stats(stats, - 1, sample_rate, tags);
};

Client.prototype.gauge = function(stat, value, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = value + "|g";
    self.send(stats, sample_rate, tags);
};

Client.prototype.histogram = function(stat, value, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = value + "|h";
    self.send(stats, sample_rate, tags);
};

Client.prototype.set = function(stat, value, sample_rate, tags) {
    var self = this;
    var stats = {};
    stats[stat] = value + "|s";
    self.send(stats, sample_rate, tags);
};

Client.prototype.update_stats = function(stats, delta, sampleRate, tags) {
    var self = this;
    if (typeof(stats) === "string")
        stats = [stats];
    if (!delta)
        delta = 1;

    var data = {};
    for (var i = 0; i < stats.length; i++)
        data[stats[i]] = delta + "|c";
    self.send(data, sampleRate, tags);
};

// An internal function update the last time the socket was
// used.  This function is called when the socket is used
// and causes demand allocated ephemeral sockets to be closed
// after a period of inactivity.
Client.prototype._update_last_used = function () {
    if (!this.ephemeral_socket)
        return;

    if (this.last_used_timer)
        clearTimeout(this.last_used_timer);

    var self = this;
    this.last_used_timer = setTimeout(function() {
        if (self.ephemeral_socket)
            self.ephemeral_socket.close();
        delete self.ephemeral_socket;
    }, EPHEMERAL_LIFETIME_MS);
};

Client.prototype.send_data = function (buf) {
    var socket;

    if (!this.socket) {
        if (!this.ephemeral_socket) {
            this.ephemeral_socket = dgram.createSocket("udp4");
            this.ephemeral_socket.on("error", function() {});
        }
        socket = this.ephemeral_socket;
    }
    else {
        socket = this.socket;
    }

    this._update_last_used();

    socket.send(buf, 0, buf.length, this.port, this.host);
};

Client.prototype.send = function(data, sample_rate, tags) {
    if (!sample_rate)
        sample_rate = 1;

    var value;
    var sampled_data = {};
    if (sample_rate < 1) {
        if (mt.genrand_real2(0, 1) <= sample_rate) {
            for (stat in data) {
                value = data[stat];
                sampled_data[stat] = value + "|@" + sample_rate;
            }
        }
    }
    else
        sampled_data = data;

    if (tags && Array.isArray(tags)) {
        var tagStr = tags.join(",");

        for (stat in sampled_data)
            sampled_data[stat] = sampled_data[stat] + "|#" + tagStr;
    }

    for (var stat in sampled_data) {
        var send_data = stat + ":" + sampled_data[stat];
        this.send_data(new Buffer(send_data));
    }
};

Client.prototype.close = function() {
    if (this.socket)
        this.socket.close();
    if (this.ephemeral_socket)
        this.ephemeral_socket.close();
    if (this.last_used_timer)
        clearTimeout(this.last_used_timer);

    this.ephemeral_socket =
    this.last_used_timer =
    this.socket = null;
};

exports.StatsD = Client;
<|endoftext|>


<commit_before>// Mistigri /// Σ:{
// Mustache-inspired JavaScript template engine

/*
Sample use
----------
var template = "{{&type}} template; {{#person}}\n{{name default ='Madam'}} loves Σ:{ " +
    '{{love yes="much" no="not"}}{{/person}}';
var model = {
    type: "Test",
    love: false,
    person: [{name: "jido", love: true}, {name: "Mrs Nock", love: false}, {}]};
alert(mistigri.prrcess(template, model));
*/

var mistigri = (function(){

var DEFAULT_OPEN_BRACE = "{{";
var DEFAULT_CLOSE_BRACE = "}}";
var DEFAULT_PLACEHOLDER_TEXT = "N/A";
var DEFAULT_METHOD_CALL = false;
var DEFAULT_ESCAPE_FUNCTION = function escapeHtml(html) {
    var text = document.createTextNode(html);
    var div = document.createElement('div');
    div.appendChild(text);
    return div.innerHTML;
}

var main = function prrcess(template, model, config) {
    open_brace = (config && 'openBrace' in config) ? config.openBrace : DEFAULT_OPEN_BRACE;
    return render(template.split(open_brace), model, config);
}

var render = function render(parts, model, config) {
    var open_brace_len = (config && 'openBrace' in config) ? config.openBrace.length : DEFAULT_OPEN_BRACE.length;
    var close_brace = (config && 'closeBrace' in config) ? config.closeBrace : DEFAULT_CLOSE_BRACE;
    var default_text = (config && 'placeholderText' in config) ? config.placeholderText : DEFAULT_PLACEHOLDER_TEXT;
    var default_escape = (config && 'escapeFunction' in config) ? config.escapeFunction : DEFAULT_ESCAPE_FUNCTION;
    var bind = (config && 'methodCall' in config) ? config.methodCall : DEFAULT_METHOD_CALL;
    var text = parts[0];
    var rendered = text;
    var position = rendered.length;
    var in_block = 0;
    var start;
    var start_text;
    var action;
    var args;
    for (var partnum = 1; partnum < parts.length; ++partnum)
    {
        var part = parts[partnum];
        var mitext = splitAt(close_brace, part);
        var mistigri = mitext[0];
        if (!in_block)
        {
            args = {$position: position, $template: parts, $model: model, $placeholderText: default_text};
        }
        switch (part.substr(0, 1))
        {
            case "#":
            case "^":
                if (in_block > 0)
                {
                    ++in_block;
                    break;      // break out of switch case
                }
                in_block = 1;
                action = parseAction(mistigri.substr(1), args, bind);
                args.$prelude = text;
                start = partnum;
                start_text = mitext[1];
                break;
            case "/":
                --in_block;
                if (in_block > 0) break;
                if (in_block < 0 || mistigri.replace(/^\/\s*/, "") !== action)
                {
                    rendered += mistigri;       // invalid close tag
                    break;
                }
                args.$ending = mitext[1];
                rendered += handleBlock(action, args, start_text, parts.slice(start, partnum), config);
                break;
            case ">":
                if (in_block > 0) break;
                console.log("Include: " + mistigri);
                break;
            case "!":
                break;  // just a comment
            case "&":
                if (in_block > 0) break;
                action = parseAction(mistigri.substr(1), args, bind);
                rendered += handleValue(action, args, bind);
                break;
            default:
                if (in_block > 0) break;
                action = parseAction(mistigri, args, bind);
                rendered += default_escape(handleValue(action, args, bind));
        }
        if (in_block <= 0)
        {
            text = mitext[1];
            rendered += text;
        }
        position += part.length + open_brace_len;
    }
    return rendered;
}

var splitAt = function splitAt(pattern, text) {
    var found = text.indexOf(pattern);
    if (found === -1)
    {
        return [text];
    }
    else
    {
        return [text.substr(0, found), text.substr(found + pattern.length)];
    }
}

var parseAction = function parseAction(tag, args, bind) {
    var parts = /^\s*(\S+)\s*([^]*)/.exec(tag);
    if (parts === null) return "";
    var action = parts[1];
    if (parts[2].length > 0)
    {
        getArgs(parts[2], args, bind);
    }
    return action;
}

var unbackslash = function(_, char) {
    if (char === "n")
    {
        return "\n";
    }
    else if (char === "t")
    {
        return "\t";
    }
    else
    {
        return char;
    }
}

var getArgs = function getArgs(text, args, bind) {
    var cleaned = text.replace(/\\[^]/, "\\1");
    var name = "";
    var seen_sign = false;
    var single_quoted = false;
    var double_quoted = false;
    var end = -1;
    var find_space = /\s+/g;
    var space;
    for (var start = 0; space !== null; start = find_space.lastIndex)
    {
        space = find_space.exec(cleaned);
        end = (space === null) ? cleaned.length : space.index;
        var value = cleaned.substring(start, end);
        if (single_quoted || double_quoted)
        {
            single_quoted = (single_quoted && value.indexOf("'", -1) === -1);
            double_quoted = (double_quoted && value.indexOf('"', -1) === -1);
            var shift = (single_quoted || double_quoted) ? 0 : 1; // closing quote
            args[name] += " " + text.substring(start, end - shift).replace(/\\([^])/, unbackslash);
        }
        else
        {
            if (!seen_sign)
            {
                var argval = splitAt("=", value);
                if (argval[0].length > 0)
                {
                    name = argval[0];
                    start += name.length;
                }
                value = argval[1];
                seen_sign = (argval.length === 2);
                start += seen_sign ? 1 : 0;
            }
            if (seen_sign && name.length === 0)
            {
                break;    // What, no arg name?
            }
            if (seen_sign && value.length > 0)
            {
                var arg;
                if (/^[-+0-9]/.test(value))
                {
                    arg = parseFloat(value);
                }
                else if (/^'[^]*'$/.test(value) || /^"[^]*"$/.test(value))
                {
                    arg = text.substring(start + 1, end - 1).replace(/\\([^])/, unbackslash);
                }
                else if (value.lastIndexOf("'", 0) === 0)
                {
                    arg = text.substring(start + 1, end).replace(/\\([^])/, unbackslash);
                    single_quoted = true;
                }
                else if (value.lastIndexOf('"', 0) === 0)
                {
                    arg = text.substring(start + 1, end).replace(/\\([^])/, unbackslash);
                    double_quoted = true;
                }
                else
                {
                    arg = valueFor(value, args.$model, bind);
                }
                args[name] = arg;
            }
        }
        if (seen_sign && value.length > 0 && !single_quoted && !double_quoted)
        {
            name = "";
            seen_sign = false;
        }
    }
    return !seen_sign;
}

var handleValue = function handleValue(action, args, bind) {
    var value = valueFor(action, args.$model, bind);
    var result;
    switch (typeof value)
    {
        case 'number':
            return value;
        case 'string':
            return value;
        case 'undefined':
        case 'object':
            result = value;
            break;
        case 'boolean':
            result = value ? args.yes : args.no;
            break;
        case 'function':
            result = value(args);
            while (typeof result === 'function')
            {
                result = result(args);
            }
            break;
        default:
            // Not sure what to do
            return value;
    }
    if (result === undefined || result === null)
    {
        return ('default' in args) ? args.default : args.$placeholderText;
    }
    return result;
}

var handleBlock = function handleBlock(action, args, content, parts, config) {
    var result = "";
    var invert = (parts[0].lastIndexOf("^", 0) === 0);
    parts[0] = content;
    args.$invertBlock = invert;
    args.$template = parts;
    var bind = (config && 'methodCall' in config) ? config.methodCall : DEFAULT_METHOD_CALL;
    var value = valueFor(action, args.$model, bind);
    while (typeof value === 'function')
    {
        value = value(args);
        if (typeof value === 'string')
        {
            return value; // add to output
        }
    }
    var is_empty = !value;
    if ((is_empty && invert) || (!is_empty && !invert))
    {
        var suffix = ('suffix' in args) ? args.suffix : "";
        var middle = "";
        if ('tag' in args)
        {
            var left = args.$prelude.toLowerCase().lastIndexOf("<" + args.tag.toLowerCase());
            var right = args.$ending.toLowerCase().indexOf("</" + args.tag.toLowerCase());
            if (left !== -1 && right !== -1)
            {
                right = args.$ending.indexOf(">", right) + 1;
                middle = args.$ending.substr(0, right) + args.$prelude.substr(left);
            }
        }
        var list = value;
        if (!Array.isArray(value))
        {
            list = [value];
        }
        var count = 0;
        for (var index in list)
        {
            count += 1;
            var item = list[index];
            var submodel = args.$model;
            submodel.$item = item;
            submodel["$item" + suffix] = item;
            submodel.$count = count;
            submodel["$count" + suffix] = count;
            if (typeof item === 'object' && item !== null)
            {
                submodel = Object.create(submodel);
                for (var key in item)
                {
                    submodel[key + suffix] = item[key];
                }
            }
            result += (count > 1 ? middle : "") + render(parts, submodel, config);
        }
    }
    return result;
}

var valueFor = function valueFor(name, model, bind) {
    var path = name.split(".");
    if (path[0].length === 0)
    {
        return (path.length === 2 && path[1].length === 0) ? model.$item : null;
    }
    var value = model;
    for (var child in path)
    {
        var next = value[path[child]];
        if (next === undefined || next === null)
        {
            return null;
        }
        if (bind && child > 0 && typeof next === 'function')
        {
            var saved = value;
            next = next.bind(saved);
        }
        value = next;
    }
    return value;
}

return {prrcess: main, process:main}; // note: prrcess gives cooler results, I swear.
})();
<commit_msg>Fix regular expression replaces<commit_after>// Mistigri /// Σ:{
// Mustache-inspired JavaScript template engine

/*
Sample use
----------
var template = "{{&type}} template; {{#person}}\n{{name default ='Madam'}} loves Σ:{ " +
    '{{love yes="much" no="not"}}{{/person}}';
var model = {
    type: "Test",
    love: false,
    person: [{name: "jido", love: true}, {name: "Mrs Nock", love: false}, {}]};
alert(mistigri.prrcess(template, model));
*/

var mistigri = (function(){

var DEFAULT_OPEN_BRACE = "{{";
var DEFAULT_CLOSE_BRACE = "}}";
var DEFAULT_PLACEHOLDER_TEXT = "N/A";
var DEFAULT_METHOD_CALL = false;
var DEFAULT_ESCAPE_FUNCTION = function escapeHtml(html) {
    var text = document.createTextNode(html);
    var div = document.createElement('div');
    div.appendChild(text);
    return div.innerHTML;
}

var main = function prrcess(template, model, config) {
    open_brace = (config && 'openBrace' in config) ? config.openBrace : DEFAULT_OPEN_BRACE;
    return render(template.split(open_brace), model, config);
}

var render = function render(parts, model, config) {
    var open_brace_len = (config && 'openBrace' in config) ? config.openBrace.length : DEFAULT_OPEN_BRACE.length;
    var close_brace = (config && 'closeBrace' in config) ? config.closeBrace : DEFAULT_CLOSE_BRACE;
    var default_text = (config && 'placeholderText' in config) ? config.placeholderText : DEFAULT_PLACEHOLDER_TEXT;
    var default_escape = (config && 'escapeFunction' in config) ? config.escapeFunction : DEFAULT_ESCAPE_FUNCTION;
    var bind = (config && 'methodCall' in config) ? config.methodCall : DEFAULT_METHOD_CALL;
    var text = parts[0];
    var rendered = text;
    var position = rendered.length;
    var in_block = 0;
    var start;
    var start_text;
    var action;
    var args;
    for (var partnum = 1; partnum < parts.length; ++partnum)
    {
        var part = parts[partnum];
        var mitext = splitAt(close_brace, part);
        var mistigri = mitext[0];
        if (!in_block)
        {
            args = {$position: position, $template: parts, $model: model, $placeholderText: default_text};
        }
        switch (part.substr(0, 1))
        {
            case "#":
            case "^":
                if (in_block > 0)
                {
                    ++in_block;
                    break;      // break out of switch case
                }
                in_block = 1;
                action = parseAction(mistigri.substr(1), args, bind);
                args.$prelude = text;
                start = partnum;
                start_text = mitext[1];
                break;
            case "/":
                --in_block;
                if (in_block > 0) break;
                if (in_block < 0 || mistigri.replace(/^\/\s*/, "") !== action)
                {
                    rendered += mistigri;       // invalid close tag
                    break;
                }
                args.$ending = mitext[1];
                rendered += handleBlock(action, args, start_text, parts.slice(start, partnum), config);
                break;
            case ">":
                if (in_block > 0) break;
                console.log("Include: " + mistigri);
                break;
            case "!":
                break;  // just a comment
            case "&":
                if (in_block > 0) break;
                action = parseAction(mistigri.substr(1), args, bind);
                rendered += handleValue(action, args, bind);
                break;
            default:
                if (in_block > 0) break;
                action = parseAction(mistigri, args, bind);
                rendered += default_escape(handleValue(action, args, bind));
        }
        if (in_block <= 0)
        {
            text = mitext[1];
            rendered += text;
        }
        position += part.length + open_brace_len;
    }
    return rendered;
}

var splitAt = function splitAt(pattern, text) {
    var found = text.indexOf(pattern);
    if (found === -1)
    {
        return [text];
    }
    else
    {
        return [text.substr(0, found), text.substr(found + pattern.length)];
    }
}

var parseAction = function parseAction(tag, args, bind) {
    var parts = /^\s*(\S+)\s*([^]*)/.exec(tag);
    if (parts === null) return "";
    var action = parts[1];
    if (parts[2].length > 0)
    {
        getArgs(parts[2], args, bind);
    }
    return action;
}

var unbackslash = function(_, char) {
    if (char === "n")
    {
        return "\n";
    }
    else if (char === "t")
    {
        return "\t";
    }
    else
    {
        return char;
    }
}

var getArgs = function getArgs(text, args, bind) {
    var backslashes = /\\([^])/g;
    var cleaned = text.replace(backslashes, "\\1");
    var name = "";
    var seen_sign = false;
    var single_quoted = false;
    var double_quoted = false;
    var end = -1;
    var find_space = /\s+/g;
    var space;
    for (var start = 0; space !== null; start = find_space.lastIndex)
    {
        space = find_space.exec(cleaned);
        end = (space === null) ? cleaned.length : space.index;
        var value = cleaned.substring(start, end);
        if (single_quoted || double_quoted)
        {
            single_quoted = (single_quoted && value.indexOf("'", -1) === -1);
            double_quoted = (double_quoted && value.indexOf('"', -1) === -1);
            var shift = (single_quoted || double_quoted) ? 0 : 1; // closing quote
            args[name] += " " + text.substring(start, end - shift).replace(backslashes, unbackslash);
        }
        else
        {
            if (!seen_sign)
            {
                var argval = splitAt("=", value);
                if (argval[0].length > 0)
                {
                    name = argval[0];
                    start += name.length;
                }
                value = argval[1];
                seen_sign = (argval.length === 2);
                start += seen_sign ? 1 : 0;
            }
            if (seen_sign && name.length === 0)
            {
                break;    // What, no arg name?
            }
            if (seen_sign && value.length > 0)
            {
                var arg;
                if (/^[-+0-9]/.test(value))
                {
                    arg = parseFloat(value);
                }
                else if (/^'[^]*'$/.test(value) || /^"[^]*"$/.test(value))
                {
                    arg = text.substring(start + 1, end - 1).replace(backslashes, unbackslash);
                }
                else if (value.lastIndexOf("'", 0) === 0)
                {
                    arg = text.substring(start + 1, end).replace(backslashes, unbackslash);
                    single_quoted = true;
                }
                else if (value.lastIndexOf('"', 0) === 0)
                {
                    arg = text.substring(start + 1, end).replace(backslashes, unbackslash);
                    double_quoted = true;
                }
                else
                {
                    arg = valueFor(value, args.$model, bind);
                }
                args[name] = arg;
            }
        }
        if (seen_sign && value.length > 0 && !single_quoted && !double_quoted)
        {
            name = "";
            seen_sign = false;
        }
    }
    return !seen_sign;
}

var handleValue = function handleValue(action, args, bind) {
    var value = valueFor(action, args.$model, bind);
    var result;
    switch (typeof value)
    {
        case 'number':
            return value;
        case 'string':
            return value;
        case 'undefined':
        case 'object':
            result = value;
            break;
        case 'boolean':
            result = value ? args.yes : args.no;
            break;
        case 'function':
            result = value(args);
            while (typeof result === 'function')
            {
                result = result(args);
            }
            break;
        default:
            // Not sure what to do
            return value;
    }
    if (result === undefined || result === null)
    {
        return ('default' in args) ? args.default : args.$placeholderText;
    }
    return result;
}

var handleBlock = function handleBlock(action, args, content, parts, config) {
    var result = "";
    var invert = (parts[0].lastIndexOf("^", 0) === 0);
    parts[0] = content;
    args.$invertBlock = invert;
    args.$template = parts;
    var bind = (config && 'methodCall' in config) ? config.methodCall : DEFAULT_METHOD_CALL;
    var value = valueFor(action, args.$model, bind);
    while (typeof value === 'function')
    {
        value = value(args);
        if (typeof value === 'string')
        {
            return value; // add to output
        }
    }
    var is_empty = !value;
    if ((is_empty && invert) || (!is_empty && !invert))
    {
        var suffix = ('suffix' in args) ? args.suffix : "";
        var middle = "";
        if ('tag' in args)
        {
            var left = args.$prelude.toLowerCase().lastIndexOf("<" + args.tag.toLowerCase());
            var right = args.$ending.toLowerCase().indexOf("</" + args.tag.toLowerCase());
            if (left !== -1 && right !== -1)
            {
                right = args.$ending.indexOf(">", right) + 1;
                middle = args.$ending.substr(0, right) + args.$prelude.substr(left);
            }
        }
        var list = value;
        if (!Array.isArray(value))
        {
            list = [value];
        }
        var count = 0;
        for (var index in list)
        {
            count += 1;
            var item = list[index];
            var submodel = args.$model;
            submodel.$item = item;
            submodel["$item" + suffix] = item;
            submodel.$count = count;
            submodel["$count" + suffix] = count;
            if (typeof item === 'object' && item !== null)
            {
                submodel = Object.create(submodel);
                for (var key in item)
                {
                    submodel[key + suffix] = item[key];
                }
            }
            result += (count > 1 ? middle : "") + render(parts, submodel, config);
        }
    }
    return result;
}

var valueFor = function valueFor(name, model, bind) {
    var path = name.split(".");
    if (path[0].length === 0)
    {
        return (path.length === 2 && path[1].length === 0) ? model.$item : null;
    }
    var value = model;
    for (var child in path)
    {
        var next = value[path[child]];
        if (next === undefined || next === null)
        {
            return null;
        }
        if (bind && child > 0 && typeof next === 'function')
        {
            var saved = value;
            next = next.bind(saved);
        }
        value = next;
    }
    return value;
}

return {prrcess: main, process:main}; // note: prrcess gives cooler results, I swear.
})();
<|endoftext|>


<commit_before>#import "contrib/tuneup_js/tuneup.js";
#import "buildArtifacts/environment.js"
// these are here so that everything just imports from Common.js
#import "Config.js";
#import "AppMap.js";
#import "Automator.js";
#import "Bridge.js";
#import "contrib/mechanic.js";

var target = UIATarget.localTarget();
var mainWindow = target.frontMostApp().mainWindow();

function delay(seconds) {
    target.delay(seconds);
}

function getTime() {
    var output = target.host().performTaskWithPathArgumentsTimeout("/bin/date", ["+%s"], 5);
    return parseInt(output.stdout);
}

function isMatchingVersion(input, prefix, major, minor, rev) {
    var findStr = prefix + major;

    if (undefined !== minor) {
        findStr += "." + minor;
        if (undefined !== rev) {
            findStr += "." + rev;
        }
    }

    return input.indexOf(findStr) > -1;
}

function isSimVersion(major, minor, rev) {
    return isMatchingVersion(target.systemVersion(), "", major, minor, rev);
}

function assertDesiredSimVersion() {
    var ver = target.systemVersion();
    if (("iOS " + ver).indexOf(config.automatorDesiredSimVersion) == -1) {
        throw "Simulator version " + ver + " is running, but generated-config.js " +
            "specifies " + config.automatorDesiredSimVersion;
    }
}

// for compatibility with iOS 6 that doesn't name cells, buttons, etc
function getNamedItemFromContainer(itemContainerView,
                                   itemArrayAccessor,
                                   itemMatchPredicate,
                                   itemSelector,
                                   name) {

    var items = itemArrayAccessor(itemContainerView);
    for (var i = 0; i < items.length; ++i) {
        var item = items[i];
        if (itemMatchPredicate(item)) return itemSelector(item);
    }

    return null;
}

function getNamedCellFromContainer(cellContainerView, name) {
    var ret = getNamedItemFromContainer(cellContainerView,
                                        function(c) { return c.cells(); },
                                        function(i) {
                                            var ii = i.elements()[0];
                                            return ii.isNotNil() && ii.name() == name;
                                        },
                                        function(i) { return i.elements()[0]; },
                                        name);
    return ret !== null ? ret : cellContainerView.cells().firstWithName(name).elements().firstWithName(name);
}

function dumpElements(elements) {
    for (var i in elements) {
        UIALogger.logDebug(elements[i].toString());
    }
}

// given a UI element accessor function, throw an error if the element's existence
//   doesn't match what the expectedVisibility was.  "name" is for logging purposes
function verifyUIElementVisibility(name, targetAccessorFn, expectedVisibility, timeout) {
    timeout = timeout === undefined ? 1 : timeout;
    var visibility;
    try {
        var theItem = target.waitUntilAccessorSuccess(targetAccessorFn, timeout, name);
        theItem.waitUntilVisible(timeout);
        visibility = true;
    } catch (e) {
        visibility = false;
    }

    if (expectedVisibility != visibility) {
        throw ("''" + name + "'' has visiblity '" + visibility +
               "' but expected '" + expectedVisibility + "'");
    }
}

// given a UI element accessor function, throw an error if the element's editabilty
//   doesn't match what the editabilty (keyboard use) was.  "name" is for logging purposes
function verifyUIElementEditability(name, targetAccessorFn, expectedEditability) {

    var editability;
    try {
        var theItem = target.waitUntilAccessorSuccess(targetAccessorFn, 1, name);
        theItem.tap();

        target.frontMostApp().keyboard().waitUntilVisible(2);
        editability = true;
    } catch (e) {
        editability = false;
    }

    if (expectedEditability != editability) {
        throw ("''" + name + "'' has edit-ability '" + editability +
               "' but expected '" + expectedEditability + "'");
    }

}

function mkActionForItemVisibility(name, targetAccessorFn) {
    return function(parm) {
        return verifyUIElementVisibility(name, targetAccessorFn, parm.expected);
    };
}

function mkActionForItemEditability(name, targetAccessorFn) {
    return function(parm) {
        return verifyUIElementEditability(name, targetAccessorFn, parm.expected);
    };
}

function mkActionForNavbarButton(navbar_name, button_name) {
    return function () {
        var button;
        var triesLeft = 2;
        while (triesLeft > 0) {
            triesLeft--;
            button = target.waitUntilAccessorSuccess(function(targ) {
                    if (navbar_name) {
                        return targ.frontMostApp().mainWindow().navigationBars()[navbar_name].buttons()[button_name];
                    } else {
                        return targ.frontMostApp().mainWindow().navigationBar().buttons()[button_name];
                    }
                }, 10, "'" + button_name + "' button in navbar of '" + navbar_name);
            button.waitUntilVisible(5);
            try {
                button.tap();
                return;
            } catch (e) {
                if (triesLeft > 0) {
                    UIALogger.logDebug("Error tapping (will retry) " + button_name + " in " + navbar_name + ": " + e);
                } else {
                    throw e;
                }
            }
        }
    }
}

function makeActionForVisibilityWithSelector(description, selector) {
    return function (param) {
        if (typeof(selector) === "function") {
            selector = selector(param);
        }
        verifyUIElementVisibility(description, function (targ) {
            var elts = $(selector);
            if (elts && elts.length) { return elts[0]; }
            return null;
        }, param.expected);
    };
}

function makeActionForPredicateOnElement(description, selector, predicate) {
    return function (param) {
        if (typeof(selector) === "function") {
            selector = selector(param);
        }
        verifyUIElementVisibility(description, function (targ) {
            var elts = $(selector);
            if (elts && elts.length) { return elts[0]; }
            return null;
        }, true);
        if (!predicate($(selector)[0], param)) {
            throw new Error("Predicate " + description + " did not match " + selector);
        }
    };
}

function makeActionForTapWithSelector(description, selector) {
    return function () {
        verifyUIElementVisibility(description, function (targ) {
            var elts = $(selector);
            if (elts && elts.length) { return elts[0]; }
            return null;
        }, true);
        $(selector).tap();
    }
}

function makeActionToEnterTextWithSelector(description, elementKey) {
    return function (param) {
        $(elementKey).input(param.text);
    }
}

function getPlistData(path) {

    var jsonOutput;
    var scriptPath = automatorRoot + "/scripts/plist_to_json.sh";
    UIALogger.logDebug("Running " + scriptPath + " '" + path + "'");

    var output = target.host().performTaskWithPathArgumentsTimeout(scriptPath, [path], 30);
    try {
        jsonOutput = JSON.parse(output.stdout);
    } catch(e) {
        throw ("plist_to_json.sh gave bad JSON: ```" + output.stdout + "```");
    }

    return jsonOutput;
}

function actionCompareScreenshotToTemplate(parm) {
    var templatePath = parm.templatePath;
    var captureTitle = parm.captureTitle;

    delay(0.4); // wait for any animations to settle

    var diff_pngPath = automatorRoot + "/scripts/diff_png.rb";
    UIATarget.localTarget().captureScreenWithName(captureTitle);

    screenshotDir  = automatorRoot + "/buildArtifacts/UIAutomationReport/Run 1"; // it's always Run 1
    screenshotFile = captureTitle + ".png";
    screenshotPath = screenshotDir + "/" + screenshotFile;
    comparePngPath = screenshotDir + "/compared_" + screenshotFile;
    compareGifPath = screenshotDir + "/compared_" + captureTitle + ".gif";

    UIALogger.logDebug("Diffing images, this may take tens of seconds: " + templatePath + " :: " + screenshotPath);
    var output = target.host().performTaskWithPathArgumentsTimeout("/usr/bin/ruby",
                                                                   [diff_pngPath, templatePath, screenshotPath, comparePngPath],
                                                                   120);

    // turn the output into key/value pairs separated by ":"
    var outputArr = output.stdout.split("\n");
    var outputObj = {};
    for (var i = 0; i < outputArr.length; ++i) {
        var sp = outputArr[i].split(": ", 2)
        if (sp.length == 2) {
            outputObj[sp[0]] = sp[1];
        }
    }

    // sanity check
    if (!outputObj["pixels changed"]) throw "actionCompareScreenshotToTemplate: diff_png.rb failed to produce 'pixels changed' output";

    // if output has no differences, return
    var wrongPixels = parseInt(outputObj["pixels changed"]);
    if (0 == wrongPixels) return;

    // generate an animated gif of the changes
    var output = target.host().performTaskWithPathArgumentsTimeout("/usr/local/bin/convert",
                                                                   ["-delay", "50",
                                                                    templatePath, screenshotPath,
                                                                    "-loop", "0",
                                                                    compareGifPath],
                                                                   120);

    throw ["Screenshot differed from", templatePath,
           "by", wrongPixels, "pixels. ",
           "Comparison image saved to:", comparePngPath,
           " and comparison animation saved to:", compareGifPath].join(" ");
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Appmap additions - common capabilities
////////////////////////////////////////////////////////////////////////////////////////////////////
appmap.createOrAugmentApp("ios-automator").withScreen("do")
    .onDevice("iPhone", function() { return true; })
    .onDevice("iPad", function() { return true; })

    .withAction("delay", "Delay a given amount of time")
    .withParam("seconds", "Number of seconds to delay", true, true)
    .withImplementation(function(parm) {delay(parm.seconds);})

    .withAction("debug", "Print the results of a debug function")
    .withParam("debug_fn", "Function returning a string", true)
    .withImplementation(function(parm) { UIALogger.logMessage(parm.debug_fn()); })

    .withAction("logTree", "Log the UI element tree")
    .withImplementation(function() { UIATarget.localTarget().logElementTree(); })

    .withAction("fail", "Unconditionally fail the current test for debugging purposes")
    .withImplementation(function() { throw "purposely-thrown exception to halt the test scenario"; })

    .withAction("verifyScreenshot", "Validate a screenshot against a png template of the expected view")
    .withParam("templatePath", "The path to the file that is considered the 'expected' view", true, true)
    .withParam("captureTitle", "The title of the screenshot to capture", true, true)
    .withImplementation(actionCompareScreenshotToTemplate);
<commit_msg>allow variable delay before screenshot capture<commit_after>#import "contrib/tuneup_js/tuneup.js";
#import "buildArtifacts/environment.js"
// these are here so that everything just imports from Common.js
#import "Config.js";
#import "AppMap.js";
#import "Automator.js";
#import "Bridge.js";
#import "contrib/mechanic.js";

var target = UIATarget.localTarget();
var mainWindow = target.frontMostApp().mainWindow();

function delay(seconds) {
    target.delay(seconds);
}

function getTime() {
    var output = target.host().performTaskWithPathArgumentsTimeout("/bin/date", ["+%s"], 5);
    return parseInt(output.stdout);
}

function isMatchingVersion(input, prefix, major, minor, rev) {
    var findStr = prefix + major;

    if (undefined !== minor) {
        findStr += "." + minor;
        if (undefined !== rev) {
            findStr += "." + rev;
        }
    }

    return input.indexOf(findStr) > -1;
}

function isSimVersion(major, minor, rev) {
    return isMatchingVersion(target.systemVersion(), "", major, minor, rev);
}

function assertDesiredSimVersion() {
    var ver = target.systemVersion();
    if (("iOS " + ver).indexOf(config.automatorDesiredSimVersion) == -1) {
        throw "Simulator version " + ver + " is running, but generated-config.js " +
            "specifies " + config.automatorDesiredSimVersion;
    }
}

// for compatibility with iOS 6 that doesn't name cells, buttons, etc
function getNamedItemFromContainer(itemContainerView,
                                   itemArrayAccessor,
                                   itemMatchPredicate,
                                   itemSelector,
                                   name) {

    var items = itemArrayAccessor(itemContainerView);
    for (var i = 0; i < items.length; ++i) {
        var item = items[i];
        if (itemMatchPredicate(item)) return itemSelector(item);
    }

    return null;
}

function getNamedCellFromContainer(cellContainerView, name) {
    var ret = getNamedItemFromContainer(cellContainerView,
                                        function(c) { return c.cells(); },
                                        function(i) {
                                            var ii = i.elements()[0];
                                            return ii.isNotNil() && ii.name() == name;
                                        },
                                        function(i) { return i.elements()[0]; },
                                        name);
    return ret !== null ? ret : cellContainerView.cells().firstWithName(name).elements().firstWithName(name);
}

function dumpElements(elements) {
    for (var i in elements) {
        UIALogger.logDebug(elements[i].toString());
    }
}

// given a UI element accessor function, throw an error if the element's existence
//   doesn't match what the expectedVisibility was.  "name" is for logging purposes
function verifyUIElementVisibility(name, targetAccessorFn, expectedVisibility, timeout) {
    timeout = timeout === undefined ? 1 : timeout;
    var visibility;
    try {
        var theItem = target.waitUntilAccessorSuccess(targetAccessorFn, timeout, name);
        theItem.waitUntilVisible(timeout);
        visibility = true;
    } catch (e) {
        visibility = false;
    }

    if (expectedVisibility != visibility) {
        throw ("''" + name + "'' has visiblity '" + visibility +
               "' but expected '" + expectedVisibility + "'");
    }
}

// given a UI element accessor function, throw an error if the element's editabilty
//   doesn't match what the editabilty (keyboard use) was.  "name" is for logging purposes
function verifyUIElementEditability(name, targetAccessorFn, expectedEditability) {

    var editability;
    try {
        var theItem = target.waitUntilAccessorSuccess(targetAccessorFn, 1, name);
        theItem.tap();

        target.frontMostApp().keyboard().waitUntilVisible(2);
        editability = true;
    } catch (e) {
        editability = false;
    }

    if (expectedEditability != editability) {
        throw ("''" + name + "'' has edit-ability '" + editability +
               "' but expected '" + expectedEditability + "'");
    }

}

function mkActionForItemVisibility(name, targetAccessorFn) {
    return function(parm) {
        return verifyUIElementVisibility(name, targetAccessorFn, parm.expected);
    };
}

function mkActionForItemEditability(name, targetAccessorFn) {
    return function(parm) {
        return verifyUIElementEditability(name, targetAccessorFn, parm.expected);
    };
}

function mkActionForNavbarButton(navbar_name, button_name) {
    return function () {
        var button;
        var triesLeft = 2;
        while (triesLeft > 0) {
            triesLeft--;
            button = target.waitUntilAccessorSuccess(function(targ) {
                    if (navbar_name) {
                        return targ.frontMostApp().mainWindow().navigationBars()[navbar_name].buttons()[button_name];
                    } else {
                        return targ.frontMostApp().mainWindow().navigationBar().buttons()[button_name];
                    }
                }, 10, "'" + button_name + "' button in navbar of '" + navbar_name);
            button.waitUntilVisible(5);
            try {
                button.tap();
                return;
            } catch (e) {
                if (triesLeft > 0) {
                    UIALogger.logDebug("Error tapping (will retry) " + button_name + " in " + navbar_name + ": " + e);
                } else {
                    throw e;
                }
            }
        }
    }
}

function makeActionForVisibilityWithSelector(description, selector) {
    return function (param) {
        if (typeof(selector) === "function") {
            selector = selector(param);
        }
        verifyUIElementVisibility(description, function (targ) {
            var elts = $(selector);
            if (elts && elts.length) { return elts[0]; }
            return null;
        }, param.expected);
    };
}

function makeActionForPredicateOnElement(description, selector, predicate) {
    return function (param) {
        if (typeof(selector) === "function") {
            selector = selector(param);
        }
        verifyUIElementVisibility(description, function (targ) {
            var elts = $(selector);
            if (elts && elts.length) { return elts[0]; }
            return null;
        }, true);
        if (!predicate($(selector)[0], param)) {
            throw new Error("Predicate " + description + " did not match " + selector);
        }
    };
}

function makeActionForTapWithSelector(description, selector) {
    return function () {
        verifyUIElementVisibility(description, function (targ) {
            var elts = $(selector);
            if (elts && elts.length) { return elts[0]; }
            return null;
        }, true);
        $(selector).tap();
    }
}

function makeActionToEnterTextWithSelector(description, elementKey) {
    return function (param) {
        $(elementKey).input(param.text);
    }
}

function getPlistData(path) {

    var jsonOutput;
    var scriptPath = automatorRoot + "/scripts/plist_to_json.sh";
    UIALogger.logDebug("Running " + scriptPath + " '" + path + "'");

    var output = target.host().performTaskWithPathArgumentsTimeout(scriptPath, [path], 30);
    try {
        jsonOutput = JSON.parse(output.stdout);
    } catch(e) {
        throw ("plist_to_json.sh gave bad JSON: ```" + output.stdout + "```");
    }

    return jsonOutput;
}

function actionCompareScreenshotToTemplate(parm) {
    var templatePath = parm.templatePath;
    var captureTitle = parm.captureTitle;
    var delayCapture = parm.delay === undefined ? 0.4 : parm.delay;

    delay(delayCapture); // wait for any animations to settle

    var diff_pngPath = automatorRoot + "/scripts/diff_png.rb";
    UIATarget.localTarget().captureScreenWithName(captureTitle);

    screenshotDir  = automatorRoot + "/buildArtifacts/UIAutomationReport/Run 1"; // it's always Run 1
    screenshotFile = captureTitle + ".png";
    screenshotPath = screenshotDir + "/" + screenshotFile;
    comparePngPath = screenshotDir + "/compared_" + screenshotFile;
    compareGifPath = screenshotDir + "/compared_" + captureTitle + ".gif";

    UIALogger.logDebug("Diffing images, this may take tens of seconds: " + templatePath + " :: " + screenshotPath);
    var output = target.host().performTaskWithPathArgumentsTimeout("/usr/bin/ruby",
                                                                   [diff_pngPath, templatePath, screenshotPath, comparePngPath],
                                                                   120);

    // turn the output into key/value pairs separated by ":"
    var outputArr = output.stdout.split("\n");
    var outputObj = {};
    for (var i = 0; i < outputArr.length; ++i) {
        var sp = outputArr[i].split(": ", 2)
        if (sp.length == 2) {
            outputObj[sp[0]] = sp[1];
        }
    }

    // sanity check
    if (!outputObj["pixels changed"]) throw "actionCompareScreenshotToTemplate: diff_png.rb failed to produce 'pixels changed' output";

    // if output has no differences, return
    var wrongPixels = parseInt(outputObj["pixels changed"]);
    if (0 == wrongPixels) return;

    // generate an animated gif of the changes
    var output = target.host().performTaskWithPathArgumentsTimeout("/usr/local/bin/convert",
                                                                   ["-delay", "50",
                                                                    templatePath, screenshotPath,
                                                                    "-loop", "0",
                                                                    compareGifPath],
                                                                   120);

    throw ["Screenshot differed from", templatePath,
           "by", wrongPixels, "pixels. ",
           "Comparison image saved to:", comparePngPath,
           " and comparison animation saved to:", compareGifPath].join(" ");
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Appmap additions - common capabilities
////////////////////////////////////////////////////////////////////////////////////////////////////
appmap.createOrAugmentApp("ios-automator").withScreen("do")
    .onDevice("iPhone", function() { return true; })
    .onDevice("iPad", function() { return true; })

    .withAction("delay", "Delay a given amount of time")
    .withParam("seconds", "Number of seconds to delay", true, true)
    .withImplementation(function(parm) {delay(parm.seconds);})

    .withAction("debug", "Print the results of a debug function")
    .withParam("debug_fn", "Function returning a string", true)
    .withImplementation(function(parm) { UIALogger.logMessage(parm.debug_fn()); })

    .withAction("logTree", "Log the UI element tree")
    .withImplementation(function() { UIATarget.localTarget().logElementTree(); })

    .withAction("fail", "Unconditionally fail the current test for debugging purposes")
    .withImplementation(function() { throw "purposely-thrown exception to halt the test scenario"; })

    .withAction("verifyScreenshot", "Validate a screenshot against a png template of the expected view")
    .withParam("templatePath", "The path to the file that is considered the 'expected' view", true, true)
    .withParam("captureTitle", "The title of the screenshot to capture", true, true)
    .withParam("delay", "The amount of time to delay before taking the screenshot", false, true)
    .withImplementation(actionCompareScreenshotToTemplate);
<|endoftext|>


<commit_before>'use strict';

const https = require('https');

function status() {
  return new Promise(function(resolve, reject) {
    https.request({
      method: 'GET',
      hostname: 'status.mojang.com',
      path: '/check',
    }, function(resp) {
      let data = [], result = [], fix = {};

      resp.on('data', function(chunk) {
        data.push(chunk);
      });

      resp.on('end', function() {
        result = JSON.parse(Buffer.concat(data).toString());
        result.forEach(x => {
          for (let name in x) {
            fix[name] = x[name];
          }
        });
        resolve(fix);
      });

    }).end();
  });
}

module.exports = status;
<commit_msg>Update mojang.status<commit_after>'use strict';

const request = require('./_request');

function status() {
  return request({
    hostname: 'status.mojang.com',
    path: '/check',
  }).then(function(sites) {
    let fix = {};
    sites.forEach(function(site) {
      for (let name in site) {
        fix[name] = site[name];
      }
    });
    return fix;
  });
}

module.exports = status;
<|endoftext|>