<commit_before>
    var reader = require('./serializers').reader;
    var writer = require('./serializers').writer;
    var cmd = require('./commands');
    var net = require('net');
    var sys = require('sys');
    var queue = require('./containers').queue;

    function packetLength(data)
    {
        var len = data.charCodeAt(0);
        len += (data.charCodeAt(1) << 8);
        len += (data.charCodeAt(2) << 16);
        return len;
    }

    exports.createTCPClient = function(host, port)
    {
        var host = host ? host : "locahost";
        var port = port ? port : 3306;
        var connection = net.createConnection(3306, "localhost");
        connection.pscache = {};
        connection.setEncoding("binary");
        connection.setTimeout(0);

        return new socketClient(connection);
    }

function dump(d)
{
   return;
   for (var i=0; i < d.length; ++i)
   {
       sys.puts(i.toString() + " " + d.charAt(i) + " " + d.charCodeAt(i).toString());
   }
}

    socketClient = function(connection)
    {
        var client = this;
        this.commands = new queue();
        this.connection = connection;
        connection.buffer = "";

        // TODO: fold following code into somthing without copy-paste
        this.close = function()
        {
            return this.add(new cmd.close());
        }

        this.debug = function( text )
        {
            return this.add(new cmd.debug(text));
        }

        this.auth = function(dbname, user, password)
        {
            return this.add(new cmd.auth(dbname, user, password));
        }

        this.query = function(q)
        {
            return this.add(new cmd.query(q));
        }

        this.prepare = function(q)
        {
            return this.add(new cmd.prepare(q));
        }

        // TODO: too many copy-paste, cleanup
        this.execute = function(q, parameters)
        {
            if (!this.pscache)
                this.pscache = {};
            if (this.auto_prepare == true)
            {
                var cached = this.connection.pscache[q];
                if (!cached) {
                    var prepare_cmd = this.add(new cmd.prepare(q));
                    var execute_cmd = this.add(new cmd.execute(q, parameters));
                    prepare_cmd.addListener('prepared', function(ps) { execute_cmd.ps = ps; });
                    prepare_cmd.addListener('error', function(err)
                    {
                        execute_cmd.emit('error', err);
                        execute_cmd.prepare_failed = true;
                    });
                    return execute_cmd;
                } else {
                    var execute_cmd = this.add(new cmd.execute(q, parameters));
                    execute_cmd.ps = cached;
                    return execute_cmd;
                }
            }
            return this.add(new cmd.execute(q, parameters));
        }

        this.terminate = function()
        {
            this.connection.end();
        }

        this.write_packet = function(packet, pnum)
        {
            packet.addHeader(pnum);
            this.connection.write(packet.data, 'binary');
        }

        this.dispatch_packet = function(packet)
        {
            if (this.commands.empty())
                return;
            if (this.commands.top().process_packet(packet))
            {
                this.commands.shift();
                this.connection.emit('queue', this.commands.length);
                this.dispatch_packet();
            }
        }

        // proxy request to socket eventemitter
        this.addListener = function()
        {
            this.connection.addListener.apply(this.connection, arguments);
        }

        this.add = function(c)
        {
            c.connection = this;
            var need_start_queue = this.connection.connected && this.commands.empty();
            this.commands.push(c);
            this.connection.emit('queue', this.commands.length);
            if (need_start_queue)
                this.dispatch_packet();

            var connection = this.connection;
            //c.addListener('end', function(cmd) { connection.emit('command_end', c); });
            //c.addListener('error', function(e) { sys.puts(e.message); });  // TODO: throw exception
            return c;
        }

        this.connection.addListener("data",
            function(data)
            {
                // TODO: move to 'onconnect' event
                // replace connected with 'first packet' or 'ready state' or smth similar
                if (!this.connected)
                {
                    this.connected = true;
                    client.dispatch_packet();
                }

                this.buffer += data;
                var len = packetLength(this.buffer);

                while (this.buffer.length >= len + 4)
                {
                    var packet = this.buffer.substr(4,len);
                    client.dispatch_packet(new reader(packet) );
                    this.buffer = this.buffer.substr(len+4, this.buffer.length-len-4);
                    len = packetLength(this.buffer);
                }
            }
        );

        return client;
    }

<commit_msg>'on' alias for addListener<commit_after>
    var reader = require('./serializers').reader;
    var writer = require('./serializers').writer;
    var cmd = require('./commands');
    var net = require('net');
    var sys = require('sys');
    var queue = require('./containers').queue;

    function packetLength(data)
    {
        var len = data.charCodeAt(0);
        len += (data.charCodeAt(1) << 8);
        len += (data.charCodeAt(2) << 16);
        return len;
    }

    exports.createTCPClient = function(host, port)
    {
        var host = host ? host : "locahost";
        var port = port ? port : 3306;
        var connection = net.createConnection(3306, "localhost");
        connection.pscache = {};
        connection.setEncoding("binary");
        connection.setTimeout(0);

        return new socketClient(connection);
    }

function dump(d)
{
   return;
   for (var i=0; i < d.length; ++i)
   {
       sys.puts(i.toString() + " " + d.charAt(i) + " " + d.charCodeAt(i).toString());
   }
}

    socketClient = function(connection)
    {
        var client = this;
        this.commands = new queue();
        this.connection = connection;
        connection.buffer = "";

        // TODO: fold following code into somthing without copy-paste
        this.close = function()
        {
            return this.add(new cmd.close());
        }

        this.debug = function( text )
        {
            return this.add(new cmd.debug(text));
        }

        this.auth = function(dbname, user, password)
        {
            return this.add(new cmd.auth(dbname, user, password));
        }

        this.query = function(q)
        {
            return this.add(new cmd.query(q));
        }

        this.prepare = function(q)
        {
            return this.add(new cmd.prepare(q));
        }

        // TODO: too many copy-paste, cleanup
        this.execute = function(q, parameters)
        {
            if (!this.pscache)
                this.pscache = {};
            if (this.auto_prepare == true)
            {
                var cached = this.connection.pscache[q];
                if (!cached) {
                    var prepare_cmd = this.add(new cmd.prepare(q));
                    var execute_cmd = this.add(new cmd.execute(q, parameters));
                    prepare_cmd.addListener('prepared', function(ps) { execute_cmd.ps = ps; });
                    prepare_cmd.addListener('error', function(err)
                    {
                        execute_cmd.emit('error', err);
                        execute_cmd.prepare_failed = true;
                    });
                    return execute_cmd;
                } else {
                    var execute_cmd = this.add(new cmd.execute(q, parameters));
                    execute_cmd.ps = cached;
                    return execute_cmd;
                }
            }
            return this.add(new cmd.execute(q, parameters));
        }

        this.terminate = function()
        {
            this.connection.end();
        }

        this.write_packet = function(packet, pnum)
        {
            packet.addHeader(pnum);
            this.connection.write(packet.data, 'binary');
        }

        this.dispatch_packet = function(packet)
        {
            if (this.commands.empty())
                return;
            if (this.commands.top().process_packet(packet))
            {
                this.commands.shift();
                this.connection.emit('queue', this.commands.length);
                this.dispatch_packet();
            }
        }

        // proxy request to socket eventemitter
        this.on = this.addListener = function()
        {
            this.connection.addListener.apply(this.connection, arguments);
        }

        this.add = function(c)
        {
            c.connection = this;
            var need_start_queue = this.connection.connected && this.commands.empty();
            this.commands.push(c);
            this.connection.emit('queue', this.commands.length);
            if (need_start_queue)
                this.dispatch_packet();

            var connection = this.connection;
            //c.addListener('end', function(cmd) { connection.emit('command_end', c); });
            //c.addListener('error', function(e) { sys.puts(e.message); });  // TODO: throw exception
            return c;
        }

        this.connection.addListener("data",
            function(data)
            {
                // TODO: move to 'onconnect' event
                // replace connected with 'first packet' or 'ready state' or smth similar
                if (!this.connected)
                {
                    this.connected = true;
                    client.dispatch_packet();
                }

                this.buffer += data;
                var len = packetLength(this.buffer);

                while (this.buffer.length >= len + 4)
                {
                    var packet = this.buffer.substr(4,len);
                    client.dispatch_packet(new reader(packet) );
                    this.buffer = this.buffer.substr(len+4, this.buffer.length-len-4);
                    len = packetLength(this.buffer);
                }
            }
        );

        return client;
    }

<|endoftext|>


<commit_before>'use strict';
var d3 = require('d3');
var inherits = require('inherits');
var utils = require('lightning-client-utils');

var margin = {
    top: 20,
    right: 20,
    bottom: 20,
    left: 45
};

var Force = function(selector, data, images, opts) {

    if(!opts) {
        opts = {};
    }

    this.opts = opts
    this.width = (opts.width || $(selector).width()) - margin.left - margin.right;
    this.height = (opts.height || (this.width * 0.6)) - margin.top - margin.bottom;

    this.data = this._formatData(data)
    this.selector = selector;
    this.defaultSize = 8
    this.defaultFill = '#68a1e5'
    this._init();

};

inherits(Force, require('events').EventEmitter);

module.exports = Force;

Force.prototype._init = function() {

    var opts = this.opts
    var height = this.height
    var width = this.width
    var selector = this.selector
    var self = this
    var links = this.data.links
    var nodes = this.data.nodes

    // if points are colored use gray, otherwise use our default
    var linkStrokeColor = nodes[0].c ? '#999' : '#A38EF3';

    // set opacity inversely proportional to number of links
    var linkStrokeOpacity = Math.max(1 - 0.0005 * links.length, 0.15)

    var zoom = d3.behavior.zoom()
        .scaleExtent([0.1, 20])
        .on("zoom", zoomed)

    var svg = d3.select(selector)
        .append('svg:svg')
        .attr('class', 'line-plot')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .attr('pointer-events', 'all')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
        .call(zoom)
        .on('dblclick.zoom', null)
        .append('svg:g')

    function zoomed() {
        svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    // highlight based on links
    // borrowed from: http://www.coppelia.io/2014/07/an-a-to-z-of-extra-features-for-the-d3-force-layout/

    // toggle for highlighting
    var toggleOpacity = 0;

    // array indicating links
    var linkedByIndex = {};
    var i
    for (i = 0; i < nodes.length; i++) {
        console.log(i)
        linkedByIndex[i + ',' + i] = 1;
    };
    links.forEach(function (d) {
        linkedByIndex[d.source + "," + d.target] = 1;
    });

    // look up neighbor pairs
    function neighboring(a, b) {
        return linkedByIndex[a.index + "," + b.index];
    }

    function connectedNodesOpacity() {
        console.log(toggleOpacity)
        if (toggleOpacity == 0) {
            // change opacity of all but the neighbouring nodes
            var d = d3.select(this).node().__data__;
            node.style("stroke", function (o) {
                return d.index == o.index ? "rgb(30,30,30)" : "white";
            });
            node.style("opacity", function (o) {
                return neighboring(d, o) | neighboring(o, d) ? 1 : 0.2;
            });
            link.style("opacity", function (o) {
                return d.index==o.source.index | d.index==o.target.index ? 1 : linkStrokeOpacity / 10;
            });
            toggleOpacity = 1;
        } else {
            // restore properties
            node.style("stroke", "white")
            node.style("opacity", 1)
            link.style("opacity", linkStrokeOpacity);
            toggleOpacity = 0;
        }
    }

    var force = d3.layout.force()
        .size([width, height])
        .charge(-120)
        .linkDistance(30)
        .nodes(nodes)
        .links(links)
        .start();

    var drag = force.drag()
      .on("dragstart", function(d) {
        d3.event.sourceEvent.stopPropagation();
      });

    var link = svg.selectAll(".link")
        .data(links)
    .enter().append("line")
        .attr("class", "link")
        .style("stroke-width", function(d) { return 1 * Math.sqrt(d.value); })
        .style("stroke", linkStrokeColor)
        .style("stroke-opacity", linkStrokeOpacity);

    var node = svg.selectAll(".node")
        .data(nodes)
    .enter().append("circle")
        .attr("class", "node")
        .attr("r", function(d) { return (d.s ? d.s : self.defaultSize); })
        .style("fill", function(d) { return (d.c ? d.c : self.defaultFill); })
        .style("fill-opacity", 0.9)
        .style("stroke", "white")
        .style("stroke-width", 1)
        .on('dblclick', connectedNodesOpacity)
        .call(drag);

    force.on("tick", function() {
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; });
    });

};

Force.prototype._formatData = function(data) {

    var retColor = utils.getColorFromData(data)
    var retSize = data.size || []
    var retName = data.name || []

    data.nodes = data.nodes.map(function (d,i) {
        d = []
        d.i = i
        d.n = retName[i]
        d.c = retColor.length > 1 ? retColor[i] : retColor[0]
        d.s = retSize.length > 1 ? retSize[i] : retSize[0]
        return d
    });

    data.links = data.links.map(function (d,i) {
        d.source = d[0]
        d.target = d[1]
        d.value = d[2]
        return d
    })

    return data

}<commit_msg>Add point highlighting<commit_after>'use strict';
var d3 = require('d3');
var inherits = require('inherits');
var utils = require('lightning-client-utils');

var margin = {
    top: 20,
    right: 20,
    bottom: 20,
    left: 45
};

var Force = function(selector, data, images, opts) {

    if(!opts) {
        opts = {};
    }

    this.opts = opts
    this.width = (opts.width || $(selector).width()) - margin.left - margin.right;
    this.height = (opts.height || (this.width * 0.6)) - margin.top - margin.bottom;

    this.data = this._formatData(data)
    this.selector = selector;
    this.defaultSize = 8
    this.defaultFill = '#68a1e5'
    this._init();

};

inherits(Force, require('events').EventEmitter);

module.exports = Force;

Force.prototype._init = function() {

    var opts = this.opts
    var height = this.height
    var width = this.width
    var selector = this.selector
    var self = this
    var links = this.data.links
    var nodes = this.data.nodes

    // if points are colored use gray, otherwise use our default
    var linkStrokeColor = nodes[0].c ? '#999' : '#A38EF3';

    // set opacity inversely proportional to number of links
    var linkStrokeOpacity = Math.max(1 - 0.0005 * links.length, 0.15)

    var zoom = d3.behavior.zoom()
        .scaleExtent([0.1, 20])
        .on("zoom", zoomed)

    var svg = d3.select(selector)
        .append('svg:svg')
        .attr('class', 'line-plot')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .attr('pointer-events', 'all')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
        .call(zoom)
        .on('dblclick.zoom', null)
        .append('svg:g')

    function zoomed() {
        svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
    }

    // highlight based on links
    // borrowed from: http://www.coppelia.io/2014/07/an-a-to-z-of-extra-features-for-the-d3-force-layout/

    // toggle for highlighting
    var toggleOpacity = 0;

    // array indicating links
    var linkedByIndex = {};
    var i
    for (i = 0; i < nodes.length; i++) {
        console.log(i)
        linkedByIndex[i + ',' + i] = 1;
    };
    links.forEach(function (d) {
        linkedByIndex[d.source + "," + d.target] = 1;
    });

    function selectedNodeOpacityIn() {
        d3.select(this).transition().duration(100).style("stroke", "rgb(30,30,30)")
    }

    function selectedNodeOpacityOut() {
        d3.select(this).transition().duration(50).style("stroke", "white")
    }

    // look up neighbor pairs
    function neighboring(a, b) {
        return linkedByIndex[a.index + "," + b.index];
    }

    function connectedNodesOpacity() {
        console.log(toggleOpacity)
        if (toggleOpacity == 0) {
            // change opacity of all but the neighbouring nodes
            var d = d3.select(this).node().__data__;
            node.style("opacity", function (o) {
                return neighboring(d, o) | neighboring(o, d) ? 1 : 0.2;
            });
            link.style("opacity", function (o) {
                return d.index==o.source.index | d.index==o.target.index ? 1 : linkStrokeOpacity / 10;
            });
            toggleOpacity = 1;
        } else {
            // restore properties
            node.style("opacity", 1)
            link.style("opacity", linkStrokeOpacity);
            toggleOpacity = 0;
        }
    }

    var force = d3.layout.force()
        .size([width, height])
        .charge(-120)
        .linkDistance(30)
        .nodes(nodes)
        .links(links)
        .start();

    var drag = force.drag()
      .on("dragstart", function(d) {
        d3.event.sourceEvent.stopPropagation();
      });

    var link = svg.selectAll(".link")
        .data(links)
    .enter().append("line")
        .attr("class", "link")
        .style("stroke-width", function(d) { return 1 * Math.sqrt(d.value); })
        .style("stroke", linkStrokeColor)
        .style("stroke-opacity", linkStrokeOpacity);

    var node = svg.selectAll(".node")
        .data(nodes)
    .enter().append("circle")
        .attr("class", "node")
        .attr("r", function(d) { return (d.s ? d.s : self.defaultSize); })
        .style("fill", function(d) { return (d.c ? d.c : self.defaultFill); })
        .style("fill-opacity", 0.9)
        .style("stroke", "white")
        .style("stroke-width", 1)
        .on('dblclick', connectedNodesOpacity)
        .on('mouseenter', selectedNodeOpacityIn)
        .on('mouseleave', selectedNodeOpacityOut)
        .call(drag);

    force.on("tick", function() {
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; });
    });

};

Force.prototype._formatData = function(data) {

    var retColor = utils.getColorFromData(data)
    var retSize = data.size || []
    var retName = data.name || []

    data.nodes = data.nodes.map(function (d,i) {
        d = []
        d.i = i
        d.n = retName[i]
        d.c = retColor.length > 1 ? retColor[i] : retColor[0]
        d.s = retSize.length > 1 ? retSize[i] : retSize[0]
        return d
    });

    data.links = data.links.map(function (d,i) {
        d.source = d[0]
        d.target = d[1]
        d.value = d[2]
        return d
    })

    return data

}<|endoftext|>


<commit_before>/*
 * viperfish
 * https://github.com/mattstyles/viperfish
 *
 * Copyright (c) 2013 Matt Styles
 * Licensed under the MIT license.
 */

/**
 * Viperfish config file
 */
var vpf = require( './viperfish' );

/**
 * Config object
 * --
 * @type {Object}
 */
vpf.config = module.exports = {

    // Module Options
    moduleOpts: {
        // Nav options
        nav     : [],
        port    : 3001
    },

    // Template Options
    tmplOpts: {

        path    : './defaults/'

    },

    // GithubApi Config Options
    githubapi : {

        apiOpts : {
            version     : '3.0.0',
            timeout     : 5000
        },

        contentOpts : {
            user        : 'mattstyles',
            repo        : 'vfj-content',
            path        : null
        }
    },

    // Marked Config Options
    markedOpts : {

        gfm         : true,
        tables      : true,
        breaks      : false,
        pedantic    : false,
        sanitize    : false,
        smartLists  : true,
        langPrefix  : 'language-',
        highlight   : function ( code, lang ) {
            // Javascript highlighting
            if ( lang === 'js' ) {
                return highlighter.javascript(code);
            }
            return code;
        }
    }

};<commit_msg>Pointed to dummy content repo as a default action for viperfish<commit_after>/*
 * viperfish
 * https://github.com/mattstyles/viperfish
 *
 * Copyright (c) 2013 Matt Styles
 * Licensed under the MIT license.
 */

/**
 * Viperfish config file
 */
var vpf = require( './viperfish' );

/**
 * Config object
 * --
 * @type {Object}
 */
vpf.config = module.exports = {

    // Module Options
    moduleOpts: {
        // Nav options
        nav     : [],
        port    : 3001
    },

    // Template Options
    tmplOpts: {

        path    : './default/'

    },

    // GithubApi Config Options
    githubapi : {

        apiOpts : {
            version     : '3.0.0',
            timeout     : 5000
        },

        contentOpts : {
            user        : 'mattstyles',
            repo        : 'vpf-def',
            path        : null
        }
    },

    // Marked Config Options
    markedOpts : {

        gfm         : true,
        tables      : true,
        breaks      : false,
        pedantic    : false,
        sanitize    : false,
        smartLists  : true,
        langPrefix  : 'language-',
        highlight   : function ( code, lang ) {
            // Javascript highlighting
            if ( lang === 'js' ) {
                return highlighter.javascript(code);
            }
            return code;
        }
    }

};<|endoftext|>


<commit_before>/**
 * Countable is a script to allow for live paragraph-, word- and character-
 * counting on an HTML element.
 *
 * @author   Sacha Schmid (<https://github.com/RadLikeWhoa>)
 * @version  2.1.1
 * @license  MIT
 * @see      <http://radlikewhoa.github.io/Countable/>
 */

/**
 * Note: For the purpose of this internal documentation, arguments of the type
 * {Nodes} are to be interpreted as either {NodeList} or {Element}.
 */

;(function (global) {
  'use strict'

  /**
   * @private
   *
   * `_liveElements` holds all elements that have the live-counting
   * functionality bound to them.
   */

  var _liveElements = []

  /**
   * `ucs2decode` function from the punycode.js library.
   *
   * Creates an array containing the decimal code points of each Unicode
   * character in the string. While JavaScript uses UCS-2 internally, this
   * function will convert a pair of surrogate halves (each of which UCS-2
   * exposes as separate characters) into a single code point, matching
   * UTF-16.
   *
   * @see     <http://goo.gl/8M09r>
   * @see     <http://goo.gl/u4UUC>
   *
   * @param   {String}  string   The Unicode input string (UCS-2).
   *
   * @return  {Array}   The new array of code points.
   */

  function _decode (string) {
    var output = [],
        counter = 0,
        length = string.length,
        value, extra

    while (counter < length) {
      value = string.charCodeAt(counter++)

      if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
        // High surrogate, and there is a next character.
        extra = string.charCodeAt(counter++)

        if ((extra & 0xFC00) == 0xDC00) {
          // Low surrogate.
          output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000)
        } else {
          // unmatched surrogate; only append this code unit, in case the next
          // code unit is the high surrogate of a surrogate pair
          output.push(value, extra)
          counter--
        }
      } else {
        output.push(value)
      }
    }

    return output
  }

  /**
   * `_validateArguments` validates the arguments given to each function call.
   * Errors are logged to the console as warnings, but Countable fails
   * silently.
   *
   * @private
   *
   * @param   {Nodes}     elements  The (collection of) element(s) to
   *                                validate.
   *
   * @param   {Function}  callback  The callback function to validate.
   *
   * @return  {Boolean}   Returns whether all arguments are vaild.
   */

  function _validateArguments (elements, callback) {
    var nodes = Object.prototype.toString.call(elements),
        elementsValid = elements && (((nodes === '[object NodeList]' || nodes === '[object HTMLCollection]') && elements.length) || (elements.nodeType === 1)),
        callbackValid = callback && typeof callback === 'function'

    if ('console' in window && 'warn' in console) {
      if (!elementsValid) console.warn('Countable: No valid elements were found')
      if (!callbackValid) console.warn('Countable: "' + callback + '" is not a valid callback function')
    }

    return elementsValid && callbackValid
  }

  /**
   * `_extendDefaults` is a function to extend a set of default options with
   * the ones given in the function call. Available options are described
   * below.
   *
   * {Boolean}  hardReturns      Use two returns to seperate a paragraph
   *                             instead of one.
   * {Boolean}  stripTags        Strip HTML tags before counting the values.
   * {Boolean}  ignoreReturns    Ignore returns when calculating the `all`
   *                             property.
   * {Boolean}  ignoreZeroWidth  Ignore zero-width space characters.
   *
   * @private
   *
   * @param   {Object}  options  Countable allows the options described above.
   *                             They can be used in a function call to
   *                             override the default behaviour.
   *
   * @return  {Object}  The new options object.
   */

  function _extendDefaults (options) {
    var defaults = {
      hardReturns: false,
      stripTags: false,
      ignoreReturns: false,
      ignoreZeroWidth: true
    }

    for (var prop in options) {
      if (defaults.hasOwnProperty(prop)) defaults[prop] = options[prop]
    }

    return defaults
  }

  /**
   * `_count` trims an element's value, optionally strips HTML tags and counts
   * paragraphs, sentences, words, characters and characters plus spaces.
   *
   * @private
   *
   * @param   {Element}  element  The element whose value is to be counted.
   *
   * @param   {Object}   options  The options to use for the counting.
   *
   * @return  {Object}   The object containing the number of paragraphs,
   *                     sentences, words, characters and characters plus
   *                     spaces.
   */

  function _count (element, options) {
    var original = '' + ('value' in element ? element.value : element.innerText || element.textContent),
        trimmed

    /**
     * The initial implementation to allow for HTML tags stripping was created
     * @craniumslows while the current one was created by @Rob--W.
     *
     * @see <http://goo.gl/Exmlr>
     * @see <http://goo.gl/gFQQh>
     */

    if (options.stripTags) original = original.replace(/<\/?[a-z][^>]*>/gi, '')
    if (options.ignoreZeroWidth) original = original.replace(/[\u200B]+/, '')

    trimmed = original.trim()

    /**
     * Most of the performance improvements are based on the works of @epmatsw.
     *
     * @see <http://goo.gl/SWOLB>
     */

    return {
      paragraphs: trimmed ? (trimmed.match(options.hardReturns ? /\n{2,}/g : /\n+/g) || []).length + 1 : 0,
      sentences: trimmed ? (trimmed.match(/[.?!…]+./g) || []).length + 1 : 0,
      words: trimmed ? (trimmed.replace(/['";:,.?¿\-!¡]+/g, '').match(/\S+/g) || []).length : 0,
      characters: trimmed ? _decode(trimmed.replace(/\s/g, '')).length : 0,
      all: _decode(options.ignoreReturns ? original.replace(/[\n\r]/g, '') : original).length
    }
  }

  /**
   * `_loop` is a helper function to iterate over a collection, e.g. a NodeList
   * or an Array. The callback receives the current element as the single
   * parameter.
   *
   * @private
   *
   * @param  {Array}     which     The collection to iterate over.
   *
   * @param  {Function}  callback  The callback function to call on each
   *                               iteration.
   */

  function _loop (which, callback) {
    var len = which.length

    if (typeof len !== 'undefined') {
      while (len--) {
        callback(which[len])
      }
    } else {
      callback(which)
    }
  }

  /**
   * This is the main object that will later be exposed to other scripts. It
   * holds all the public methods that can be used to enable the Countable
   * functionality.
   */

  var Countable = {

    /**
     * The `live` method binds the counting handler to all given elements. The
     * event is either `oninput` or `onkeydown`, based on the capabilities of
     * the browser.
     *
     * @param   {Nodes}     elements   All elements that should receive the
     *                                 Countable functionality.
     *
     * @param   {Function}  callback   The callback to fire whenever the
     *                                 element's value changes. The callback is
     *                                 called with the relevant element bound
     *                                 to `this` and the counted values as the
     *                                 single parameter.
     *
     * @param   {Object}    [options]  An object to modify Countable's
     *                                 behaviour. Refer to `_extendDefaults`
     *                                 for a list of available options.
     *
     * @return  {Object}    Returns the Countable object to allow for chaining.
     */

    live: function (elements, callback, options) {
      var ops = _extendDefaults(options),
          bind = function (element) {
            var handler = function () {
                  callback.call(element, _count(element, ops))
                }

            _liveElements.push({ element: element, handler: handler })

            handler()

            element.addEventListener('input', handler, false)
          }

      if (!_validateArguments(elements, callback)) return

      if (elements.length) {
        _loop(elements, bind)
      } else {
        bind(elements)
      }

      return this
    },

    /**
     * The `die` method removes the Countable functionality from all given
     * elements.
     *
     * @param   {Nodes}  elements  All elements whose Countable functionality
     *                             should be unbound.
     *
     * @return  {Object}  Returns the Countable object to allow for chaining.
     */

    die: function (elements) {
      if (!_validateArguments(elements, function () {})) return

      _loop(elements, function (element) {
        var liveElement

        _loop(_liveElements, function (live) {
          if (live.element === element) liveElement = live
        })

        if (!liveElement) return

        element.removeEventListener('input', liveElement.handler, false)

        _liveElements.splice(_liveElements.indexOf(liveElement), 1)
      })

      return this
    },

    /**
     * The `once` method works mostly like the `live` method, but no events are
     * bound, the functionality is only executed once.
     *
     * @alias   Countable.count
     *
     * @param   {Nodes}     elements   All elements that should receive the
     *                                 Countable functionality.
     *
     * @param   {Function}  callback   The callback to fire whenever the
     *                                 element's value changes. The callback is
     *                                 called with the relevant element bound
     *                                 to `this` and the counted values as the
     *                                 single parameter.
     *
     * @param   {Object}    [options]  An object to modify Countable's
     *                                 behaviour. Refer to `_extendDefaults`
     *                                 for a list of available options.
     *
     * @return  {Object}    Returns the Countable object to allow for chaining.
     */

    once: function (elements, callback, options) {
      if (!_validateArguments(elements, callback)) return

      _loop(elements, function (element) {
        callback.call(element, _count(element, _extendDefaults(options)))
      })

      return this
    },

    count: function (elements, callback, options) {
      return this.once(elements, callback, options)
    },

    /**
     * The `enabled` method checks if the live-counting functionality is bound
     * to an element.
     *
     * @param   {Element}  element  A single Element.
     *
     * @return  {Boolean}  A boolean value representing whether Countable
     *                     functionality is bound to the given element.
     */

    enabled: function (element) {
      var isEnabled = false

      if (element && element.nodeType === 1) {
        _loop(_liveElements, function (live) {
          if (live.element === element) isEnabled = true
        })
      }

      return isEnabled
    }

  }

  /**
   * Expose Countable depending on the module system used across the
   * application. (Node / CommonJS, AMD, global)
   */

  if (typeof exports === 'object') {
    module.exports = Countable
  } else if (typeof define === 'function' && define.amd) {
    define(function () { return Countable })
  } else {
    global.Countable = Countable
  }
}(this));
<commit_msg>Refactor live, enabled<commit_after>/**
 * Countable is a script to allow for live paragraph-, word- and character-
 * counting on an HTML element.
 *
 * @author   Sacha Schmid (<https://github.com/RadLikeWhoa>)
 * @version  2.1.1
 * @license  MIT
 * @see      <http://radlikewhoa.github.io/Countable/>
 */

/**
 * Note: For the purpose of this internal documentation, arguments of the type
 * {Nodes} are to be interpreted as either {NodeList} or {Element}.
 */

;(function (global) {
  'use strict'

  /**
   * @private
   *
   * `_liveElements` holds all elements that have the live-counting
   * functionality bound to them.
   */

  var _liveElements = []

  /**
   * `ucs2decode` function from the punycode.js library.
   *
   * Creates an array containing the decimal code points of each Unicode
   * character in the string. While JavaScript uses UCS-2 internally, this
   * function will convert a pair of surrogate halves (each of which UCS-2
   * exposes as separate characters) into a single code point, matching
   * UTF-16.
   *
   * @see     <http://goo.gl/8M09r>
   * @see     <http://goo.gl/u4UUC>
   *
   * @param   {String}  string   The Unicode input string (UCS-2).
   *
   * @return  {Array}   The new array of code points.
   */

  function _decode (string) {
    var output = [],
        counter = 0,
        length = string.length,
        value, extra

    while (counter < length) {
      value = string.charCodeAt(counter++)

      if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
        // High surrogate, and there is a next character.
        extra = string.charCodeAt(counter++)

        if ((extra & 0xFC00) == 0xDC00) {
          // Low surrogate.
          output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000)
        } else {
          // unmatched surrogate; only append this code unit, in case the next
          // code unit is the high surrogate of a surrogate pair
          output.push(value, extra)
          counter--
        }
      } else {
        output.push(value)
      }
    }

    return output
  }

  /**
   * `_validateArguments` validates the arguments given to each function call.
   * Errors are logged to the console as warnings, but Countable fails
   * silently.
   *
   * @private
   *
   * @param   {Nodes}     elements  The (collection of) element(s) to
   *                                validate.
   *
   * @param   {Function}  callback  The callback function to validate.
   *
   * @return  {Boolean}   Returns whether all arguments are vaild.
   */

  function _validateArguments (elements, callback) {
    var nodes = Object.prototype.toString.call(elements),
        elementsValid = elements && (((nodes === '[object NodeList]' || nodes === '[object HTMLCollection]') && elements.length) || (elements.nodeType === 1)),
        callbackValid = callback && typeof callback === 'function'

    if ('console' in window && 'warn' in console) {
      if (!elementsValid) console.warn('Countable: No valid elements were found')
      if (!callbackValid) console.warn('Countable: "' + callback + '" is not a valid callback function')
    }

    return elementsValid && callbackValid
  }

  /**
   * `_extendDefaults` is a function to extend a set of default options with
   * the ones given in the function call. Available options are described
   * below.
   *
   * {Boolean}  hardReturns      Use two returns to seperate a paragraph
   *                             instead of one.
   * {Boolean}  stripTags        Strip HTML tags before counting the values.
   * {Boolean}  ignoreReturns    Ignore returns when calculating the `all`
   *                             property.
   * {Boolean}  ignoreZeroWidth  Ignore zero-width space characters.
   *
   * @private
   *
   * @param   {Object}  options  Countable allows the options described above.
   *                             They can be used in a function call to
   *                             override the default behaviour.
   *
   * @return  {Object}  The new options object.
   */

  function _extendDefaults (options) {
    var defaults = {
      hardReturns: false,
      stripTags: false,
      ignoreReturns: false,
      ignoreZeroWidth: true
    }

    for (var prop in options) {
      if (defaults.hasOwnProperty(prop)) defaults[prop] = options[prop]
    }

    return defaults
  }

  /**
   * `_count` trims an element's value, optionally strips HTML tags and counts
   * paragraphs, sentences, words, characters and characters plus spaces.
   *
   * @private
   *
   * @param   {Element}  element  The element whose value is to be counted.
   *
   * @param   {Object}   options  The options to use for the counting.
   *
   * @return  {Object}   The object containing the number of paragraphs,
   *                     sentences, words, characters and characters plus
   *                     spaces.
   */

  function _count (element, options) {
    var original = '' + ('value' in element ? element.value : element.innerText || element.textContent),
        trimmed

    /**
     * The initial implementation to allow for HTML tags stripping was created
     * @craniumslows while the current one was created by @Rob--W.
     *
     * @see <http://goo.gl/Exmlr>
     * @see <http://goo.gl/gFQQh>
     */

    if (options.stripTags) original = original.replace(/<\/?[a-z][^>]*>/gi, '')
    if (options.ignoreZeroWidth) original = original.replace(/[\u200B]+/, '')

    trimmed = original.trim()

    /**
     * Most of the performance improvements are based on the works of @epmatsw.
     *
     * @see <http://goo.gl/SWOLB>
     */

    return {
      paragraphs: trimmed ? (trimmed.match(options.hardReturns ? /\n{2,}/g : /\n+/g) || []).length + 1 : 0,
      sentences: trimmed ? (trimmed.match(/[.?!…]+./g) || []).length + 1 : 0,
      words: trimmed ? (trimmed.replace(/['";:,.?¿\-!¡]+/g, '').match(/\S+/g) || []).length : 0,
      characters: trimmed ? _decode(trimmed.replace(/\s/g, '')).length : 0,
      all: _decode(options.ignoreReturns ? original.replace(/[\n\r]/g, '') : original).length
    }
  }

  /**
   * `_loop` is a helper function to iterate over a collection, e.g. a NodeList
   * or an Array. The callback receives the current element as the single
   * parameter.
   *
   * @private
   *
   * @param  {Array}     which     The collection to iterate over.
   *
   * @param  {Function}  callback  The callback function to call on each
   *                               iteration.
   */

  function _loop (which, callback) {
    var len = which.length

    if (typeof len !== 'undefined') {
      while (len--) {
        callback(which[len])
      }
    } else {
      callback(which)
    }
  }

  /**
   * This is the main object that will later be exposed to other scripts. It
   * holds all the public methods that can be used to enable the Countable
   * functionality.
   */

  var Countable = {

    /**
     * The `live` method binds the counting handler to all given elements. The
     * event is either `oninput` or `onkeydown`, based on the capabilities of
     * the browser.
     *
     * @param   {Nodes}     elements   All elements that should receive the
     *                                 Countable functionality.
     *
     * @param   {Function}  callback   The callback to fire whenever the
     *                                 element's value changes. The callback is
     *                                 called with the relevant element bound
     *                                 to `this` and the counted values as the
     *                                 single parameter.
     *
     * @param   {Object}    [options]  An object to modify Countable's
     *                                 behaviour. Refer to `_extendDefaults`
     *                                 for a list of available options.
     *
     * @return  {Object}    Returns the Countable object to allow for chaining.
     */

    live: function (elements, callback, options) {
      if (!_validateArguments(elements, callback)) return

      var ops = _extendDefaults(options),
          bind = function (element) {
            var handler = function () {
                  callback.call(element, _count(element, ops))
                }

            _liveElements.push({ element: element, handler: handler })

            handler()

            element.addEventListener('input', handler, false)
          }

      _loop(elements, bind)

      return this
    },

    /**
     * The `die` method removes the Countable functionality from all given
     * elements.
     *
     * @param   {Nodes}  elements  All elements whose Countable functionality
     *                             should be unbound.
     *
     * @return  {Object}  Returns the Countable object to allow for chaining.
     */

    die: function (elements) {
      if (!_validateArguments(elements, function () {})) return

      _loop(elements, function (element) {
        var liveElement

        _loop(_liveElements, function (live) {
          if (live.element === element) liveElement = live
        })

        if (!liveElement) return

        element.removeEventListener('input', liveElement.handler, false)

        _liveElements.splice(_liveElements.indexOf(liveElement), 1)
      })

      return this
    },

    /**
     * The `once` method works mostly like the `live` method, but no events are
     * bound, the functionality is only executed once.
     *
     * @alias   Countable.count
     *
     * @param   {Nodes}     elements   All elements that should receive the
     *                                 Countable functionality.
     *
     * @param   {Function}  callback   The callback to fire whenever the
     *                                 element's value changes. The callback is
     *                                 called with the relevant element bound
     *                                 to `this` and the counted values as the
     *                                 single parameter.
     *
     * @param   {Object}    [options]  An object to modify Countable's
     *                                 behaviour. Refer to `_extendDefaults`
     *                                 for a list of available options.
     *
     * @return  {Object}    Returns the Countable object to allow for chaining.
     */

    once: function (elements, callback, options) {
      if (!_validateArguments(elements, callback)) return

      _loop(elements, function (element) {
        callback.call(element, _count(element, _extendDefaults(options)))
      })

      return this
    },

    count: function (elements, callback, options) {
      return this.once(elements, callback, options)
    },

    /**
     * The `enabled` method checks if the live-counting functionality is bound
     * to an element.
     *
     * @param   {Element}  element  A single Element.
     *
     * @return  {Boolean}  A boolean value representing whether Countable
     *                     functionality is bound to the given element.
     */

    enabled: function (element) {
      return _liveElements.filter(function (el) { return el.element === element }).length > 0
    }

  }

  /**
   * Expose Countable depending on the module system used across the
   * application. (Node / CommonJS, AMD, global)
   */

  if (typeof exports === 'object') {
    module.exports = Countable
  } else if (typeof define === 'function' && define.amd) {
    define(function () { return Countable })
  } else {
    global.Countable = Countable
  }
}(this));
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Netsend.
 *
 * This file is part of PersDB.
 *
 * PersDB is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Affero General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * PersDB is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with PersDB. If not, see <https://www.gnu.org/licenses/>.
 */

/* jshint -W116 */

'use strict';

var programName = require('path').basename(__filename, '.js');

if (process.getuid() !== 0) {
  console.error('%s: execute as root', programName);
  process.exit(1);
}

var spawn = require('child_process').spawn;

var chroot = require('chroot');
var posix = require('posix');

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

/**
 * Spawn a WebSocket server child and handle connections.
 *
 * Full FSM: init --> listen
 *
 * The first message emitted is "init" which signals that this process is ready to
 * receive the configuration data. File desciptors for the log config should be
 * sent in subsequent messages.
 *
 * {
 *   log:            {Object}      // log configuration
 *   [maxconn]:      {Number}      // maximum number of connections, default 100
 *   [chroot]:       {String}      // defaults to /var/empty
 *   [user]:         {String}      // defaults to "nobody"
 *   [group]:        {String}      // defaults to "nobody"
 *
 *   all wss_server options:
 *
 *   cert:           {Object}      // X.509 certificate for the TLS server
 *   key:            {Object}      // RSA private key for the certificate
 *   dhparam:        {Object}      // file that contains a Diffie-Hellman group
 *   [host]:         {String}      // websocket host, defaults to 127.0.0.1
 *   [port]:         {Number}      // websocket port, defaults to 3344
 * }
 *
 * No other messages should be sent to this process.
 *
 * As soon as the server is listening, a "listen" message is emitted. After this
 * only new incoming auth requests are sent from this process to it's parent.
 */

var log; // used after receiving the log configuration

/**
 * Manage open file descriptors used for IPC.
 *
 * @param {Array} stdio  array of stream objects
 * @param {Number} [offset]  optional offset for stdio
 * @return { open(), close(i) } an open and close function
 */
function fdsHandler(stdio, offset) {
  if (!Array.isArray(stdio)) { throw new TypeError('stdio must be an array'); }
  if (offset != null && typeof offset !== 'number') { throw new TypeError('offset must be a number'); }
  offset = offset || 0;
  if (offset >= stdio.length) { throw new Error('offset >= stdio'); }

  // init fds
  var fds = {};
  for (var i = offset; i < stdio.length; i++) {
    fds[i] = null;
  }

  var openFds = 0;

  /**
   * @return {Boolean|Number} open fd, or false if all are in use
   */
  function open() {
    if (openFds === stdio.length) {
      log.notice('all fds are in use %d', openFds);
      return false;
    }
    var j = offset;
    while (j < stdio.length && fds[j])
      j++;
    if (j < stdio.length) {
      fds[j] = true;
      openFds++;
      return j;
    }

    log.err('no free fd found %d', j);
    throw new Error('no free fd found');
  }

  // close fd if open
  function close(j) {
    if (fds[j]) {
      fds[j] = false;
      openFds--;
    }
  }

  return {
    open: open,
    close: close
  };
}

if (typeof process.send !== 'function') {
  throw new Error('this module should be invoked via child_process.fork');
}

process.send('init');

/**
 * Expect one init request (request that contains listen config)
 * {
 *   log:            {Object}      // log configuration
 *   [maxconn]:      {Number}      // maximum number of connections, default 100
 *   [chroot]:       {String}      // defaults to /var/empty
 *   [user]:         {String}      // defaults to "nobody"
 *   [group]:        {String}      // defaults to "nobody"
 *
 *   all wss_server options:
 *
 *   cert:           {Object}      // X.509 certificate for the TLS server
 *   key:            {Object}      // RSA private key for the certificate
 *   dhparam:        {Object}      // file that contains a Diffie-Hellman group
 *   [host]:         {String}      // websocket host, defaults to 127.0.0.1
 *   [port]:         {Number}      // websocket port, defaults to 3344
 * }
 */
process.once('message', function(msg) {
  if (typeof msg !== 'object') { throw new TypeError('msg must be an object'); }

  if (typeof msg.log !== 'object') { throw new TypeError('msg.log must be an object'); }

  if (msg.maxconn != null && typeof msg.maxconn !== 'number') { throw new TypeError('msg.maxconn must be a number'); }
  if (msg.chroot != null && typeof msg.chroot !== 'string') { throw new TypeError('msg.chroot must be a string'); }
  if (msg.user != null && typeof msg.user !== 'string') { throw new TypeError('msg.user must be a string'); }
  if (msg.group != null && typeof msg.group !== 'string') { throw new TypeError('msg.group must be a string'); }

  var maxconn = msg.maxconn || 100;

  programName = 'wss master';

  process.title = 'pdb/' + programName;

  var user = msg.user || 'nobody';
  var group = msg.group || 'nobody';

  var newRoot = msg.chroot || '/var/empty';

  msg.log.ident = programName;

  // open log
  logger(msg.log, function(err, l) {
    if (err) { l.err(err); throw err; }

    log = l; // use this logger in the mt's as well

    var uid, gid;
    try {
      uid = posix.getpwnam(user).uid;
      gid = posix.getgrnam(group).gid;
    } catch(err) {
      log.err('%s %s:%s', err, user, group);
      process.exit(3);
    }

    // spawn child and chroot or exit
    function openServerAndProceed() {
      // create file descriptors for ipc
      var offset = 32;

      // ignore stdin and fd 4-offset, pipe fd offset-maxconn
      var stdio = ['ignore', 'pipe', 'pipe', 'ipc'];
      var i = stdio.length;
      for (; i < offset; i++) {
        stdio.push('ignore');
      }
      for (; i < maxconn; i++) {
        stdio.push('pipe');
      }

      var fds = fdsHandler(stdio, offset);

      var opts = {
        cwd: __dirname,
        env: {},
        stdio: stdio
      };

      // spawn child that runs the WebSocket server
      var child = spawn(process.execPath, [__dirname + '/wss_server.js'], opts);

      log.info('child spawned');

      // chroot or exit
      try {
        chroot(newRoot, user, group);
        log.info('changed root to %s and user:group to %s:%s', newRoot, user, group);
      } catch(err) {
        log.err('changing root or user failed %j %s', msg, err);
        process.exit(8);
      }

      child.on('exit', function(code, signal) {
        log.debug('child exit', code, signal);
      });
      child.on('close', function(code, signal) {
        log.debug('child close', code, signal);
      });
      child.on('error', function(err) {
        log.err('child error', err);
      });

      child.stdout.setEncoding('utf8');
      child.stderr.setEncoding('utf8');

      child.stdout.on('data', function(data) {
        log.debug('child stdout:', data);
      });
      child.stderr.on('data', function(data) {
        log.err('child stderr:', data);
      });

      function handleData(d) {
        log.info('data', d);
      }

      function mainChildHandler(msg, cfd) {
        switch (msg) {
        case 'open':
          if (cfd != null) {
            log.notice('child sent fd with open message');
          }

          var fd = fds.open();
          if (fd) {
            log.info('open fd %d', fd);
            child.send('open', child.stdio[fd]);
            fd.on('data', handleData);
          } else {
            log.debug('could not open fd');
            child.send('close');
          }
          break;
        case 'close':
          if (typeof cfd === 'number') {
            if (cfd < offset || cfd >= stdio.length) {
              log.err('child sent illegal fd: %d, offset: %d, fds: %d', cfd, offset, stdio.length);
            } else {
              cfd.removeListener('data', handleData);
              fds.close(cfd);
            }
          } else {
            log.notice('child sent close without numeric fd: %s', typeof cfd);
          }
          break;
        default:
          log.notice('child sent unknown message "%s"', msg);
        }
      }

      // expect "init" -> "listen" -> "open" || "close" messages
      child.once('message', function(msg) {
        if (msg !== 'init') {
          log.err('expected init message from child: %s', msg);
          process.exit(9);
        }
        child.once('message', function(msg) {
          if (msg !== 'listen') {
            log.err('expected listen message from child: %s', msg);
            process.exit(10);
          }
          child.on('message', mainChildHandler);
          process.send('listen');
        });
      });

      // listen to kill signals
      //process.once('SIGINT', shutdown);
      //process.once('SIGTERM', shutdown);
    }

    openServerAndProceed();
  });
});
<commit_msg>deprecate limited value of wss_master<commit_after><|endoftext|>


<commit_before>var events         = require('events')
  , expat          = require('node-expat')
  , FiniteAutomata = require('./finite-automata')
  ;

// Tests if object is empty (has no own properties).
function isEmpty(obj) {
  for (var key in obj) if (obj.hasOwnProperty(key)) {
    return false;
  }
  return true;
}

// XML entities.
var entities = {
  '"': '"',
  '&': '&',
  '\'': ''',
  '<': '<',
  '>': '>'
};

// Escapes text for XML.
function escape(value) {
  return value.replace(/"|&|'|<|>/g, function(entity) {
    return entities[entity];
  });
}

// Parser events to finite automata events mapping.
var faModes = {
  'startElement': 'enter',
  'endElement': 'leave',
  'text': 'state'
};

// I accidentally the whole class.
module.exports = XmlStream;

// **XmlStream** is an XML stream filter based on Expat.
// It traverses a given stream and emits events for predefined selectors.
// Event listeners receive selected elements, context, and trace from root.
function XmlStream(stream) {
  events.EventEmitter.call(this);
  this._stream = stream;
  this._fa = new FiniteAutomata();
  this._lastState = 0;
  this._startState = {};
  this._finalStates = {};
  this._emitData = false;
  this._bufferLevel = 0;
  this._collect = false;
  parse.call(this);
}

// Inherit events.EventEmitter.
XmlStream.super_ = events.EventEmitter;
XmlStream.prototype = Object.create(events.EventEmitter.prototype, {
  constructor: {
    value: XmlStream,
    enumerable: false
  }
});

// Adds a listener for the specified event.
//
// Supported events:
//
// * `data` on outgoing data chunk,
// * `end` when parsing has ended,
// * `startElement[: selector]` on opening tag for selector match,
// * `updateElement[: selector]` on finished node for selector match
//   with its contents buffered,
// * `endElement[: selector]` on closing tag for selector match,
// * `text[: selector]` on tag text for selector match.
//
// When adding listeners for `startElement`, `updateElement`, and `text` the
// callback can modify the provided node, before it is sent to the consumer.
//
// Selector syntax is CSS-like and currently supports:
//
// * `ancestor descendant`
// * `parent > child`
XmlStream.prototype.on = function(eventName, listener) {
  XmlStream.super_.prototype.on.call(this, eventName, listener);
  if (eventName === 'data') {
    this._emitData = true;
  }
  var event = parseEvent(eventName);
  if (event !== null) {
    // If we're dealing with a selector event,
    // continue with selector-specific processing logic.
    var finalState = getFinalState.call(this, event.selector);
    var self = this;
    if (event.type === 'updateElement') {
      this._fa.on('enter', finalState, function() {
        self._bufferLevel++;
      });
      this._fa.on('leave', finalState, function(element, context, trace) {
        self.emit(event.name, element, context, trace);
        if (!--self._bufferLevel && self._emitData) {
           emitElement.call(self, element, self._name, true);
        }
      });
    } else {
      var fn = function(element, context, trace) {
        self.emit(event.name, element, context, trace);
      };
      this._fa.on(faModes[event.type], finalState, fn);
    }
  }
};

//
XmlStream.prototype.collect = function(selector) {
  selector = normalizeSelector(selector);
  var finalState = getFinalState.call(this, selector);
  var self = this;
  this._fa.on('flag', finalState, function() {
    self._collect = true;
  });
};

// Normalizes the selector and returns the new version and its parts.
function normalizeSelector(selector) {
  var parts = selector.match(/[^\s>]+|>/ig);
  selector = parts.join(' ');
  return {
    normalized: parts.join(' '),
    parts: parts
  };
}

// Parses the selector event string and returns event information.
function parseEvent(event) {
  var eventParts = event.match(/^((?:start|end|update)Element|text):?(.*)/);
  if (eventParts === null) {
    return null;
  }
  var eventType = eventParts[1];
  var selector = normalizeSelector(eventParts[2]);
  return {
    selector: selector,
    type: eventType,
    name: eventType + ': ' + selector.normalized
  };
}

// Compiles a given selector object to a finite automata
// and returns its last state.
function getFinalState(selector) {
  if (this._finalStates.hasOwnProperty(selector.normalized)) {
    finalState = this._finalStates[selector.normalized];
  } else {
    var n = selector.parts.length;
    var immediate = false;
    this._startState[this._lastState] = true;
    for (var i = 0; i < n; i++) {
      var part = selector.parts[i];
      if (part === '>') {
        immediate = true;
      } else {
        if (!immediate) {
          this._fa.transition(this._lastState, '', this._lastState);
        }
        this._fa.transition(this._lastState, part, ++this._lastState);
        immediate = false;
      }
    }
    finalState = this._lastState++;
    this._finalStates[selector.normalized] = finalState;
  }
  return finalState;
}

// Emits XML for element opening tag.
function emitStart(name, attrs) {
  this.emit('data', '<' + name);
  for (var attr in attrs) if (attrs.hasOwnProperty(attr)) {
    this.emit('data', ' ' + attr + '="' + escape(attrs[attr]) + '"');
  }
  this.emit('data', '>');
}

// Emits XML for element closing tag.
function emitEnd(name) {
  this.emit('data', '</' + name + '>');
}

// Emits XML for element text.
function emitText(text) {
  this.emit('data', escape(text).trim());
}

// Emits a single element and its descendants, or an array of elements.
function emitElement(element, name, onLeave) {
  if (Array.isArray(element)) {
    var i;
    for (i = 0; i < element.length - 1; i++) {
      emitOneElement.call(this, element[i], name);
    }
    emitOneElement.call(this, element[i], name, onLeave);
  } else {
    emitOneElement.call(this, element, name, onLeave);
  }
}

// Recursively emits a given element and its descendants.
function emitOneElement(element, name, onLeave) {
  if (typeof element === 'object') {
    emitStart.call(this, name, element.$);
    var hasText = false;
    for (var child in element) {
      if (element.hasOwnProperty(child) && child !== '$' && child != '$name') {
        if (child === '$text') {
          hasText = true;
        } else {
          emitElement.call(this, element[child], child);
        }
      }
    }
    if (hasText) {
      emitText.call(this, element.$text);
    }
  } else {
    emitStart.call(this, name, element.$);
    emitText.call(this, element);
  }
  if(!onLeave) {
    emitEnd.call(this, name);
  }
}

// Starts parsing the source stream and emitting various events.
// The Expat parser is assigned several listeners for this purpose.
function parse(stream) {
  var self = this;
  var xml = new expat.Parser();
  var stack = [];
  var trace = {};
  var curr = {
    element: {},
    collect: this._collect,
    fullText: '',
    space: 0,
    path: '',
    context: {}
  };
  var fa = this._fa;
  fa.setState(this._startState);

  // A listener is assigned on opening tag encounter.
  // Here we traverse the configured finite automata use the stack
  // to form the context and trace for selector event emission.
  xml.on('startElement', function(name, attr) {
    stack.push(curr);
    trace[curr.path] = curr.element;
    var context = Object.create(curr.context);
    var element = {
      $: attr,
      $name: name,
      $text: ''
    };
    var parent = curr.element;
    curr = {
      element: element,
      collect: false,
      fullText: '',
      space: 0,
      path: curr.path + '/' + name,
      context: context
    };
    self._collect = false;
    fa.enter(name, [element, context, trace]);
    name = element.$name;
    curr.collect = self._collect;
    if (curr.collect) {
      var container;
      if (Object.hasOwnProperty.call(parent, name)) {
        container = parent[name];
        container.push(element);
      } else {
        container = [element];
        parent[name] = container;
      }
    } else {
      parent[name] = element;
      context[name] = element;
    }
    if (self._bufferLevel === 0 && self._emitData) {
      emitStart.call(self, name, element.$);
    }
  });

  // A listener is assigned on closing tag encounter.
  // Current node structure object is finalized. A selector listener is
  // invoked with current node, context, and trace; these arguments are
  // removed from the stack afterwards.
  xml.on('endElement', function(name) {
    var prev = stack.pop();
    var element = curr.element;
    var text = curr.fullText;
    var attr = element.$;
    if (typeof attr !== 'object') {
      attr = {};
    }
    var name = element.$name;
    self._name = name;
    delete element.$;
    delete element.$text;
    delete element.$name;
    var val = element;
    if (isEmpty(element) && isEmpty(attr)) {
      val = text;
    } else if (!isEmpty(attr)) {
      element.$ = attr;
    }
    if (text !== '') {
      element.$text = text;
    }
    curr.context[name] = val;
    if (curr.collect) {
      var container = prev.element[name];
      container[container.length - 1] = val;
    } else {
      prev.element[name] = val;
    }
    fa.leave([element, curr.context, trace]);
    if (self._bufferLevel === 0 && self._emitData) {
      emitEnd.call(self, name);
    }
    curr = prev;
    this._collect = curr.collect;
  });

  // Collect node text part by part
  // (and trim leading and trailing whitespace).
  xml.on('text', function(text) {
    curr.element.$text = text;
    fa.run('state', [curr.element, curr.context, trace]);
    if (self._bufferLevel === 0 && self._emitData) {
      emitText.call(self, text);
    }
    var trimmed = curr.element.$text.trim();
    var spaced = curr.element.$text.substr(0, 1);
    spaced = (spaced !== '') && (spaced.trim() === '');
    var after = curr.element.$text.substr(-1, 1);
    after = (after !== '') && (after.trim() === '');
    switch (curr.space) {
      // No words yet (pass through spaces).
      case 0:
        if (trimmed !== '') {
          curr.space = after ? 2 : 1;
        }
        break;

      // Immediately after text or entity.
      case 1:
        if (trimmed === '') {
          curr.space = 2;
        } else {
          if (spaced) {
            curr.fullText += ' ';
          }
          if (after) {
            curr.space = 2;
          }
        }
        break;

      // Some words were emitted, pass through spaces again.
      // Emit spaces only when a word is encountered afterwards.
      case 2:
        if (trimmed !== '') {
          curr.fullText += ' ';
          curr.space = 1;
        }
        break;
    }
    curr.fullText += trimmed;
  });

  // Pass data from stream to parser.
  this._stream.on('data', function(data) {
    xml.parse(data, false);
  });

  // End parsing on stream EOF and emit an *end* event ourselves.
  this._stream.on('end', function() {
    xml.parse('', true);
    self.emit('end');
  });
}
<commit_msg>Added a quick description to collect<commit_after>var events         = require('events')
  , expat          = require('node-expat')
  , FiniteAutomata = require('./finite-automata')
  ;

// Tests if object is empty (has no own properties).
function isEmpty(obj) {
  for (var key in obj) if (obj.hasOwnProperty(key)) {
    return false;
  }
  return true;
}

// XML entities.
var entities = {
  '"': '"',
  '&': '&',
  '\'': ''',
  '<': '<',
  '>': '>'
};

// Escapes text for XML.
function escape(value) {
  return value.replace(/"|&|'|<|>/g, function(entity) {
    return entities[entity];
  });
}

// Parser events to finite automata events mapping.
var faModes = {
  'startElement': 'enter',
  'endElement': 'leave',
  'text': 'state'
};

// I accidentally the whole class.
module.exports = XmlStream;

// **XmlStream** is an XML stream filter based on Expat.
// It traverses a given stream and emits events for predefined selectors.
// Event listeners receive selected elements, context, and trace from root.
function XmlStream(stream) {
  events.EventEmitter.call(this);
  this._stream = stream;
  this._fa = new FiniteAutomata();
  this._lastState = 0;
  this._startState = {};
  this._finalStates = {};
  this._emitData = false;
  this._bufferLevel = 0;
  this._collect = false;
  parse.call(this);
}

// Inherit events.EventEmitter.
XmlStream.super_ = events.EventEmitter;
XmlStream.prototype = Object.create(events.EventEmitter.prototype, {
  constructor: {
    value: XmlStream,
    enumerable: false
  }
});

// Adds a listener for the specified event.
//
// Supported events:
//
// * `data` on outgoing data chunk,
// * `end` when parsing has ended,
// * `startElement[: selector]` on opening tag for selector match,
// * `updateElement[: selector]` on finished node for selector match
//   with its contents buffered,
// * `endElement[: selector]` on closing tag for selector match,
// * `text[: selector]` on tag text for selector match.
//
// When adding listeners for `startElement`, `updateElement`, and `text` the
// callback can modify the provided node, before it is sent to the consumer.
//
// Selector syntax is CSS-like and currently supports:
//
// * `ancestor descendant`
// * `parent > child`
XmlStream.prototype.on = function(eventName, listener) {
  XmlStream.super_.prototype.on.call(this, eventName, listener);
  if (eventName === 'data') {
    this._emitData = true;
  }
  var event = parseEvent(eventName);
  if (event !== null) {
    // If we're dealing with a selector event,
    // continue with selector-specific processing logic.
    var finalState = getFinalState.call(this, event.selector);
    var self = this;
    if (event.type === 'updateElement') {
      this._fa.on('enter', finalState, function() {
        self._bufferLevel++;
      });
      this._fa.on('leave', finalState, function(element, context, trace) {
        self.emit(event.name, element, context, trace);
        if (!--self._bufferLevel && self._emitData) {
           emitElement.call(self, element, self._name, true);
        }
      });
    } else {
      var fn = function(element, context, trace) {
        self.emit(event.name, element, context, trace);
      };
      this._fa.on(faModes[event.type], finalState, fn);
    }
  }
};

// Collects elements with identical names, specified by a selector.
// They will reside in the parent element as an array.
XmlStream.prototype.collect = function(selector) {
  selector = normalizeSelector(selector);
  var finalState = getFinalState.call(this, selector);
  var self = this;
  this._fa.on('flag', finalState, function() {
    self._collect = true;
  });
};

// Normalizes the selector and returns the new version and its parts.
function normalizeSelector(selector) {
  var parts = selector.match(/[^\s>]+|>/ig);
  selector = parts.join(' ');
  return {
    normalized: parts.join(' '),
    parts: parts
  };
}

// Parses the selector event string and returns event information.
function parseEvent(event) {
  var eventParts = event.match(/^((?:start|end|update)Element|text):?(.*)/);
  if (eventParts === null) {
    return null;
  }
  var eventType = eventParts[1];
  var selector = normalizeSelector(eventParts[2]);
  return {
    selector: selector,
    type: eventType,
    name: eventType + ': ' + selector.normalized
  };
}

// Compiles a given selector object to a finite automata
// and returns its last state.
function getFinalState(selector) {
  if (this._finalStates.hasOwnProperty(selector.normalized)) {
    finalState = this._finalStates[selector.normalized];
  } else {
    var n = selector.parts.length;
    var immediate = false;
    this._startState[this._lastState] = true;
    for (var i = 0; i < n; i++) {
      var part = selector.parts[i];
      if (part === '>') {
        immediate = true;
      } else {
        if (!immediate) {
          this._fa.transition(this._lastState, '', this._lastState);
        }
        this._fa.transition(this._lastState, part, ++this._lastState);
        immediate = false;
      }
    }
    finalState = this._lastState++;
    this._finalStates[selector.normalized] = finalState;
  }
  return finalState;
}

// Emits XML for element opening tag.
function emitStart(name, attrs) {
  this.emit('data', '<' + name);
  for (var attr in attrs) if (attrs.hasOwnProperty(attr)) {
    this.emit('data', ' ' + attr + '="' + escape(attrs[attr]) + '"');
  }
  this.emit('data', '>');
}

// Emits XML for element closing tag.
function emitEnd(name) {
  this.emit('data', '</' + name + '>');
}

// Emits XML for element text.
function emitText(text) {
  this.emit('data', escape(text).trim());
}

// Emits a single element and its descendants, or an array of elements.
function emitElement(element, name, onLeave) {
  if (Array.isArray(element)) {
    var i;
    for (i = 0; i < element.length - 1; i++) {
      emitOneElement.call(this, element[i], name);
    }
    emitOneElement.call(this, element[i], name, onLeave);
  } else {
    emitOneElement.call(this, element, name, onLeave);
  }
}

// Recursively emits a given element and its descendants.
function emitOneElement(element, name, onLeave) {
  if (typeof element === 'object') {
    emitStart.call(this, name, element.$);
    var hasText = false;
    for (var child in element) {
      if (element.hasOwnProperty(child) && child !== '$' && child != '$name') {
        if (child === '$text') {
          hasText = true;
        } else {
          emitElement.call(this, element[child], child);
        }
      }
    }
    if (hasText) {
      emitText.call(this, element.$text);
    }
  } else {
    emitStart.call(this, name, element.$);
    emitText.call(this, element);
  }
  if(!onLeave) {
    emitEnd.call(this, name);
  }
}

// Starts parsing the source stream and emitting various events.
// The Expat parser is assigned several listeners for this purpose.
function parse(stream) {
  var self = this;
  var xml = new expat.Parser();
  var stack = [];
  var trace = {};
  var curr = {
    element: {},
    collect: this._collect,
    fullText: '',
    space: 0,
    path: '',
    context: {}
  };
  var fa = this._fa;
  fa.setState(this._startState);

  // A listener is assigned on opening tag encounter.
  // Here we traverse the configured finite automata use the stack
  // to form the context and trace for selector event emission.
  xml.on('startElement', function(name, attr) {
    stack.push(curr);
    trace[curr.path] = curr.element;
    var context = Object.create(curr.context);
    var element = {
      $: attr,
      $name: name,
      $text: ''
    };
    var parent = curr.element;
    curr = {
      element: element,
      collect: false,
      fullText: '',
      space: 0,
      path: curr.path + '/' + name,
      context: context
    };
    self._collect = false;
    fa.enter(name, [element, context, trace]);
    name = element.$name;
    curr.collect = self._collect;
    if (curr.collect) {
      var container;
      if (Object.hasOwnProperty.call(parent, name)) {
        container = parent[name];
        container.push(element);
      } else {
        container = [element];
        parent[name] = container;
      }
    } else {
      parent[name] = element;
      context[name] = element;
    }
    if (self._bufferLevel === 0 && self._emitData) {
      emitStart.call(self, name, element.$);
    }
  });

  // A listener is assigned on closing tag encounter.
  // Current node structure object is finalized. A selector listener is
  // invoked with current node, context, and trace; these arguments are
  // removed from the stack afterwards.
  xml.on('endElement', function(name) {
    var prev = stack.pop();
    var element = curr.element;
    var text = curr.fullText;
    var attr = element.$;
    if (typeof attr !== 'object') {
      attr = {};
    }
    var name = element.$name;
    self._name = name;
    delete element.$;
    delete element.$text;
    delete element.$name;
    var val = element;
    if (isEmpty(element) && isEmpty(attr)) {
      val = text;
    } else if (!isEmpty(attr)) {
      element.$ = attr;
    }
    if (text !== '') {
      element.$text = text;
    }
    curr.context[name] = val;
    if (curr.collect) {
      var container = prev.element[name];
      container[container.length - 1] = val;
    } else {
      prev.element[name] = val;
    }
    fa.leave([element, curr.context, trace]);
    if (self._bufferLevel === 0 && self._emitData) {
      emitEnd.call(self, name);
    }
    curr = prev;
    this._collect = curr.collect;
  });

  // Collect node text part by part
  // (and trim leading and trailing whitespace).
  xml.on('text', function(text) {
    curr.element.$text = text;
    fa.run('state', [curr.element, curr.context, trace]);
    if (self._bufferLevel === 0 && self._emitData) {
      emitText.call(self, text);
    }
    var trimmed = curr.element.$text.trim();
    var spaced = curr.element.$text.substr(0, 1);
    spaced = (spaced !== '') && (spaced.trim() === '');
    var after = curr.element.$text.substr(-1, 1);
    after = (after !== '') && (after.trim() === '');
    switch (curr.space) {
      // No words yet (pass through spaces).
      case 0:
        if (trimmed !== '') {
          curr.space = after ? 2 : 1;
        }
        break;

      // Immediately after text or entity.
      case 1:
        if (trimmed === '') {
          curr.space = 2;
        } else {
          if (spaced) {
            curr.fullText += ' ';
          }
          if (after) {
            curr.space = 2;
          }
        }
        break;

      // Some words were emitted, pass through spaces again.
      // Emit spaces only when a word is encountered afterwards.
      case 2:
        if (trimmed !== '') {
          curr.fullText += ' ';
          curr.space = 1;
        }
        break;
    }
    curr.fullText += trimmed;
  });

  // Pass data from stream to parser.
  this._stream.on('data', function(data) {
    xml.parse(data, false);
  });

  // End parsing on stream EOF and emit an *end* event ourselves.
  this._stream.on('end', function() {
    xml.parse('', true);
    self.emit('end');
  });
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var mocha = require("mocha")
  , Base = mocha.reporters.Base
  , utils = mocha.utils
  , escape = utils.escape
  , config = require("../config.json")
  , fs = require("fs")
  , path = require("path")
  , mkdirp = require("mkdirp")
  , dateFormat = require('dateformat')
  , filePathParser = require('./file-path-parser')(process, global.Date, dateFormat)
  , filePath = filePathParser(process.env.XUNIT_FILE || config.file || "${cwd}/xunit.xml")
  , consoleOutput = process.env.XUNIT_SILENT ? {} : config.consoleOutput || {};

/**
 * Save timer references to avoid Sinon interfering (see GH-237).
 */

var Date = global.Date
  , setTimeout = global.setTimeout
  , setInterval = global.setInterval
  , clearTimeout = global.clearTimeout
  , clearInterval = global.clearInterval;

/**
 * Expose `XUnitFile`.
 */

exports = module.exports = XUnitFile;

/**
 * Initialize a new `XUnitFile` reporter.
 *
 * @param {Runner} runner
 * @api public
 */

function XUnitFile(runner) {
  Base.call(this, runner);
  var stats = this.stats
    , tests = []
    , fd;

  mkdirp.sync(path.dirname(filePath));
  fd = fs.openSync(filePath, 'w', 0755);

  runner.on('suite', function(suite){
    if(consoleOutput.suite){
      console.log('  ' + suite.title);
    }
  });

  runner.on('test', function(test){
    if(consoleOutput.test){
      console.log('  ◦ ' + test.title);
    }
  });

  runner.on('pass', function(test){
    tests.push(test);
  });

  runner.on('fail', function(test){
    if(consoleOutput.fail){
      console.log('  - ' + test.title);
    }
    tests.push(test);
  });

  runner.on('pending', function(test) {
      tests.push(test);
  });

  runner.on('end', function(){
    var timestampStr = (new Date).toISOString().split('.', 1)[0];
    appendLine(fd, tag('testsuite', {
        name: process.env.SUITE_NAME || 'Mocha Tests'
      , tests: stats.tests
      , failures: stats.failures
      , errors: stats.failures
      , skipped: stats.tests - stats.failures - stats.passes
      , timestamp: timestampStr
      , time: stats.duration / 1000
    }, false));

    if( process.env.XUNIT_LOG_ENV) {
      logProperties(fd);
    }

    tests.forEach(function(test){
      writeTest(fd, test);
    });

    appendLine(fd, '</testsuite>');
    fs.closeSync(fd);
  });
}

/**
 * Inherit from `Base.prototype`.
 */

XUnitFile.prototype.__proto__ = Base.prototype;

/**
 * Writes a list of process and environment variables to the <properties> section in the XML.
 */
function logProperties(fd) {
  var attrs = new Object();
  var properties = "\n";

  properties += logProperty('process.arch', process.arch);
  properties += logProperty('process.platform', process.platform);
  properties += logProperty('process.memoryUsage', objectToString(process.memoryUsage()));
  properties += logProperty('process.cwd', process.cwd());
  properties += logProperty('process.execPath', process.execPath)
  properties += logProperty('process.execArgv', process.execArgv.join( ' '));
  properties += logProperty('process.argv', process.argv.join( ' '));
  properties += logProperty('process.version', process.version.replace('"',''));
  properties += logProperty('process.versions', objectToString(process.versions));
  properties += logProperty('process.env.PATH', process.env.PATH);
  properties += logProperty('process.env.NODE_PATH', process.env.NODE_PATH);
  properties += logProperty('process.env.SUITE_NAME', process.env.SUITE_NAME);
  properties += logProperty('process.env.XUNIT_FILE', process.env.XUNIT_FILE);
  properties += logProperty('process.env.LOG_XUNIT', process.env.LOG_XUNIT);

  appendLine(fd, tag('properties', {}, false, properties));
}

/**
 * Formats a single property value.
 */

function logProperty( name, value) {
  return '  ' + tag('property', { name: name, value: value }, true) + '\n';
}

/**
 * Simple utility to convert a flat Object to a readable string.
 */

function objectToString( obj) {
  var arrayString = '';

  if( obj) {
    for (var prop in obj) {
      var propValue = '' + obj[prop];
      if( arrayString.length > 0) {
    	  arrayString += ', ';
      }
      arrayString += prop + ": '" + propValue.replace( "'", "\\'") + "'";
    }
  }
  return '[ ' + arrayString + ']';
}

/**
 * Output tag for the given `test.`
 */

function writeTest(fd, test) {
  var attrs = {
      classname: test.parent.fullTitle()
    , name: test.title
    // , time: test.duration / 1000 //old
    ,time: test.duration ? test.duration / 1000 : 0 //new
  };

  if ('failed' == test.state) {
    var err = test.err;
    appendLine(fd, tag('testcase', attrs, false, tag('failure', { message: escape(err.message) }, false, cdata(err.stack))));
  } else if (test.pending) {
    delete attrs.time;
    appendLine(fd, tag('testcase', attrs, false, tag('skipped', {}, true)));
  } else {
    appendLine(fd, tag('testcase', attrs, true) );
  }
}

/**
 * HTML tag helper.
 */

function tag(name, attrs, close, content) {
  var end = close ? '/>' : '>'
    , pairs = []
    , result;

  for (var key in attrs) {
    pairs.push(key + '="' + escape(attrs[key]) + '"');
  }

  result = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
  if (content) result += content + '</' + name + end;
  return result;
}

/**
 * Return cdata escaped CDATA `str`.
 */

function cdata(str) {
  return '<![CDATA[' + escape(str) + ']]>';
}

function appendLine(fd, line) {
    if (process.env.LOG_XUNIT) {
        console.log(line);
    }
    fs.writeSync(fd, line + "\n", null, 'utf8');
}
<commit_msg>fix: Dont count xunit failures as errors (#27)<commit_after>/**
 * Module dependencies.
 */

var mocha = require("mocha")
  , Base = mocha.reporters.Base
  , utils = mocha.utils
  , escape = utils.escape
  , config = require("../config.json")
  , fs = require("fs")
  , path = require("path")
  , mkdirp = require("mkdirp")
  , dateFormat = require('dateformat')
  , filePathParser = require('./file-path-parser')(process, global.Date, dateFormat)
  , filePath = filePathParser(process.env.XUNIT_FILE || config.file || "${cwd}/xunit.xml")
  , consoleOutput = process.env.XUNIT_SILENT ? {} : config.consoleOutput || {};

/**
 * Save timer references to avoid Sinon interfering (see GH-237).
 */

var Date = global.Date
  , setTimeout = global.setTimeout
  , setInterval = global.setInterval
  , clearTimeout = global.clearTimeout
  , clearInterval = global.clearInterval;

/**
 * Expose `XUnitFile`.
 */

exports = module.exports = XUnitFile;

/**
 * Initialize a new `XUnitFile` reporter.
 *
 * @param {Runner} runner
 * @api public
 */

function XUnitFile(runner) {
  Base.call(this, runner);
  var stats = this.stats
    , tests = []
    , fd;

  mkdirp.sync(path.dirname(filePath));
  fd = fs.openSync(filePath, 'w', 0755);

  runner.on('suite', function(suite){
    if(consoleOutput.suite){
      console.log('  ' + suite.title);
    }
  });

  runner.on('test', function(test){
    if(consoleOutput.test){
      console.log('  ◦ ' + test.title);
    }
  });

  runner.on('pass', function(test){
    tests.push(test);
  });

  runner.on('fail', function(test){
    if(consoleOutput.fail){
      console.log('  - ' + test.title);
    }
    tests.push(test);
  });

  runner.on('pending', function(test) {
      tests.push(test);
  });

  runner.on('end', function(){
    var timestampStr = (new Date).toISOString().split('.', 1)[0];
    appendLine(fd, tag('testsuite', {
        name: process.env.SUITE_NAME || 'Mocha Tests'
      , tests: stats.tests
      , failures: stats.failures
      , errors: 0
      , skipped: stats.tests - stats.failures - stats.passes
      , timestamp: timestampStr
      , time: stats.duration / 1000
    }, false));

    if( process.env.XUNIT_LOG_ENV) {
      logProperties(fd);
    }

    tests.forEach(function(test){
      writeTest(fd, test);
    });

    appendLine(fd, '</testsuite>');
    fs.closeSync(fd);
  });
}

/**
 * Inherit from `Base.prototype`.
 */

XUnitFile.prototype.__proto__ = Base.prototype;

/**
 * Writes a list of process and environment variables to the <properties> section in the XML.
 */
function logProperties(fd) {
  var attrs = new Object();
  var properties = "\n";

  properties += logProperty('process.arch', process.arch);
  properties += logProperty('process.platform', process.platform);
  properties += logProperty('process.memoryUsage', objectToString(process.memoryUsage()));
  properties += logProperty('process.cwd', process.cwd());
  properties += logProperty('process.execPath', process.execPath)
  properties += logProperty('process.execArgv', process.execArgv.join( ' '));
  properties += logProperty('process.argv', process.argv.join( ' '));
  properties += logProperty('process.version', process.version.replace('"',''));
  properties += logProperty('process.versions', objectToString(process.versions));
  properties += logProperty('process.env.PATH', process.env.PATH);
  properties += logProperty('process.env.NODE_PATH', process.env.NODE_PATH);
  properties += logProperty('process.env.SUITE_NAME', process.env.SUITE_NAME);
  properties += logProperty('process.env.XUNIT_FILE', process.env.XUNIT_FILE);
  properties += logProperty('process.env.LOG_XUNIT', process.env.LOG_XUNIT);

  appendLine(fd, tag('properties', {}, false, properties));
}

/**
 * Formats a single property value.
 */

function logProperty( name, value) {
  return '  ' + tag('property', { name: name, value: value }, true) + '\n';
}

/**
 * Simple utility to convert a flat Object to a readable string.
 */

function objectToString( obj) {
  var arrayString = '';

  if( obj) {
    for (var prop in obj) {
      var propValue = '' + obj[prop];
      if( arrayString.length > 0) {
    	  arrayString += ', ';
      }
      arrayString += prop + ": '" + propValue.replace( "'", "\\'") + "'";
    }
  }
  return '[ ' + arrayString + ']';
}

/**
 * Output tag for the given `test.`
 */

function writeTest(fd, test) {
  var attrs = {
      classname: test.parent.fullTitle()
    , name: test.title
    // , time: test.duration / 1000 //old
    ,time: test.duration ? test.duration / 1000 : 0 //new
  };

  if ('failed' == test.state) {
    var err = test.err;
    appendLine(fd, tag('testcase', attrs, false, tag('failure', { message: escape(err.message) }, false, cdata(err.stack))));
  } else if (test.pending) {
    delete attrs.time;
    appendLine(fd, tag('testcase', attrs, false, tag('skipped', {}, true)));
  } else {
    appendLine(fd, tag('testcase', attrs, true) );
  }
}

/**
 * HTML tag helper.
 */

function tag(name, attrs, close, content) {
  var end = close ? '/>' : '>'
    , pairs = []
    , result;

  for (var key in attrs) {
    pairs.push(key + '="' + escape(attrs[key]) + '"');
  }

  result = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
  if (content) result += content + '</' + name + end;
  return result;
}

/**
 * Return cdata escaped CDATA `str`.
 */

function cdata(str) {
  return '<![CDATA[' + escape(str) + ']]>';
}

function appendLine(fd, line) {
    if (process.env.LOG_XUNIT) {
        console.log(line);
    }
    fs.writeSync(fd, line + "\n", null, 'utf8');
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import EditFormController from 'ember-flexberry/controllers/edit-form';
import { Query } from 'ember-flexberry-data';
import { SimplePredicate } from 'ember-flexberry-data/query/predicate';
const { getOwner } = Ember;

export default EditFormController.extend({

  parentRoute: 'fd-stage-list-form',

  store: Ember.inject.service('store'),

  moduleSetting: {
    FrontendGitRepoUrl: '',
    FrontendLogin: '',
    FrontendPassword: '',
    FrontendBranch: '',
    FrontendPublishGh: false,
    BackendGitRepoUrl: '',
    BackendLogin: '',
    BackendPassword: '',
    BackendBranch: '',
    GenerateCordova: false,
    ProcessMethodology: false,
    ProcessConsoleAddress: '',
    DefaultStorage: '',
  },

  _changedModel: Ember.observer('model.id', function() {
    this.set('moduleSetting.FrontendGitRepoUrl', '');
    this.set('moduleSetting.FrontendLogin', '');
    this.set('moduleSetting.FrontendPassword', '');
    this.set('moduleSetting.FrontendBranch', '');
    this.set('moduleSetting.FrontendPublishGh', false);
    this.set('moduleSetting.BackendGitRepoUrl', '');
    this.set('moduleSetting.BackendLogin', '');
    this.set('moduleSetting.BackendPassword', '');
    this.set('moduleSetting.BackendBranch', '');
    this.set('moduleSetting.GenerateCordova', false);
    this.set('moduleSetting.ProcessMethodology', false);
    this.set('moduleSetting.ProcessConsoleAddress', '');
    this.set('moduleSetting.DefaultStorage', '');
  }),

  _storageType: Ember.computed('model.id', function() {
    let currentStage = this.get('model.id');

    let stageEqualNull = new SimplePredicate('stage', '==', null);
    let stageEqualCurrentStage = new SimplePredicate('stage', '==', currentStage);
    let condition = stageEqualNull.or(stageEqualCurrentStage);

    let store = this.get('store');

    let builder = new Query.Builder(store)
      .from('fd-storage-type')
      .selectByProjection('EditFormView')
      .where(condition);

    return store.query('fd-storage-type', builder.build()).then(function(data) {
      let results = data.map(item => item.get('shortName'));
      return results;
    });
  }),

  save() {
    this._super(...arguments);

    let stagePk = this.get('model.id');
    let moduleSetting = this.get('moduleSetting');
    let moduleSettingTypes = Object.keys(moduleSetting);

    let valueModuleSetting = Ember.A();
    let moduleSettingData = Ember.A();

    for (let i = 0; i < moduleSettingTypes.length; i++) {
      let valueModuleSettingData = {
        'ValueXML': moduleSetting[moduleSettingTypes[i]]
      };

      let moduleSettingTypeData = {
        'Name': moduleSettingTypes[i]
      };

      valueModuleSetting.pushObject(valueModuleSettingData);
      moduleSettingData.pushObject(moduleSettingTypeData);
    }

    let data = { 'project': stagePk.toString(), 'moduleSettingType': moduleSettingData, 'valueModuleSetting': valueModuleSetting };
    let adapter = getOwner(this).lookup('adapter:application');

    adapter.callAction('SaveCurrentModuleSetting', data, null, { withCredentials: true });
  }
});
<commit_msg>Fix fd-stage-edit-form controller call custom logic after save<commit_after>import Ember from 'ember';
import EditFormController from 'ember-flexberry/controllers/edit-form';
import { Query } from 'ember-flexberry-data';
import { SimplePredicate } from 'ember-flexberry-data/query/predicate';
const { getOwner } = Ember;

export default EditFormController.extend({

  parentRoute: 'fd-stage-list-form',

  store: Ember.inject.service('store'),

  moduleSetting: {
    FrontendGitRepoUrl: '',
    FrontendLogin: '',
    FrontendPassword: '',
    FrontendBranch: '',
    FrontendPublishGh: false,
    BackendGitRepoUrl: '',
    BackendLogin: '',
    BackendPassword: '',
    BackendBranch: '',
    GenerateCordova: false,
    ProcessMethodology: false,
    ProcessConsoleAddress: '',
    DefaultStorage: '',
  },

  _changedModel: Ember.observer('model.id', function() {
    this.set('moduleSetting.FrontendGitRepoUrl', '');
    this.set('moduleSetting.FrontendLogin', '');
    this.set('moduleSetting.FrontendPassword', '');
    this.set('moduleSetting.FrontendBranch', '');
    this.set('moduleSetting.FrontendPublishGh', false);
    this.set('moduleSetting.BackendGitRepoUrl', '');
    this.set('moduleSetting.BackendLogin', '');
    this.set('moduleSetting.BackendPassword', '');
    this.set('moduleSetting.BackendBranch', '');
    this.set('moduleSetting.GenerateCordova', false);
    this.set('moduleSetting.ProcessMethodology', false);
    this.set('moduleSetting.ProcessConsoleAddress', '');
    this.set('moduleSetting.DefaultStorage', '');
  }),

  _storageType: Ember.computed('model.id', function() {
    let currentStage = this.get('model.id');

    let stageEqualNull = new SimplePredicate('stage', '==', null);
    let stageEqualCurrentStage = new SimplePredicate('stage', '==', currentStage);
    let condition = stageEqualNull.or(stageEqualCurrentStage);

    let store = this.get('store');

    let builder = new Query.Builder(store)
      .from('fd-storage-type')
      .selectByProjection('EditFormView')
      .where(condition);

    return store.query('fd-storage-type', builder.build()).then(function(data) {
      let results = data.map(item => item.get('shortName'));
      return results;
    });
  }),

  save() {
    this._super(...arguments).then(() => {
      let stagePk = this.get('model.id');
      let moduleSetting = this.get('moduleSetting');
      let moduleSettingTypes = Object.keys(moduleSetting);

      let valueModuleSetting = Ember.A();
      let moduleSettingData = Ember.A();

      for (let i = 0; i < moduleSettingTypes.length; i++) {
        let valueModuleSettingData = {
          'ValueXML': moduleSetting[moduleSettingTypes[i]]
        };

        let moduleSettingTypeData = {
          'Name': moduleSettingTypes[i]
        };

        valueModuleSetting.pushObject(valueModuleSettingData);
        moduleSettingData.pushObject(moduleSettingTypeData);
      }

      let data = { 'project': stagePk.toString(), 'moduleSettingType': moduleSettingData, 'valueModuleSetting': valueModuleSetting };
      let adapter = getOwner(this).lookup('adapter:application');

      adapter.callAction('SaveCurrentModuleSetting', data, null, { withCredentials: true });
    });
  }
});
<|endoftext|>