<commit_before>define([
  './base',
  '../utils',
  'jquery'
], function (BaseAdapter, Utils, $) {
  function SelectAdapter ($element, options) {
    this.$element = $element;
    this.options = options;

    SelectAdapter.__super__.constructor.call(this);
  }

  Utils.Extend(SelectAdapter, BaseAdapter);

  SelectAdapter.prototype.current = function (callback) {
    var self = this;

    var data = Array.prototype.map.call(
      this.$element[0].querySelectorAll(':checked'),
      function (selectedElement) {
        return self.item($(selectedElement));
      }
    );

    callback(data);
  };

  SelectAdapter.prototype.select = function (data) {
    var self = this;

    data.selected = true;

    // If data.element is a DOM node, use it instead
    if (
      data.element != null && data.element.tagName.toLowerCase() === 'option'
    ) {
      data.element.selected = true;

      this.$element.trigger('input').trigger('change');

      return;
    }

    if (this.$element.prop('multiple')) {
      this.current(function (currentData) {
        var val = [];

        data = [data];
        data.push.apply(data, currentData);

        for (var d = 0; d < data.length; d++) {
          var id = data[d].id;

          if (val.indexOf(id) === -1) {
            val.push(id);
          }
        }

        self.$element.val(val);
        self.$element.trigger('input').trigger('change');
      });
    } else {
      var val = data.id;

      this.$element.val(val);
      this.$element.trigger('input').trigger('change');
    }
  };

  SelectAdapter.prototype.unselect = function (data) {
    var self = this;

    if (!this.$element.prop('multiple')) {
      return;
    }

    data.selected = false;

    if (
      data.element != null &&
      data.element.tagName.toLowerCase() === 'option'
    ) {
      data.element.selected = false;

      this.$element.trigger('input').trigger('change');

      return;
    }

    this.current(function (currentData) {
      var val = [];

      for (var d = 0; d < currentData.length; d++) {
        var id = currentData[d].id;

        if (id !== data.id && val.indexOf(id) === -1) {
          val.push(id);
        }
      }

      self.$element.val(val);

      self.$element.trigger('input').trigger('change');
    });
  };

  SelectAdapter.prototype.bind = function (container, $container) {
    var self = this;

    this.container = container;

    container.on('select', function (params) {
      self.select(params.data);
    });

    container.on('unselect', function (params) {
      self.unselect(params.data);
    });
  };

  SelectAdapter.prototype.destroy = function () {
    // Remove anything added to child elements
    this.$element.find('*').each(function () {
      // Remove any custom data set by Select2
      Utils.RemoveData(this);
    });
  };

  SelectAdapter.prototype.query = function (params, callback) {
    var data = [];
    var self = this;

    var $options = this.$element.children();

    $options.each(function () {
      if (
        this.tagName.toLowerCase() !== 'option' &&
        this.tagName.toLowerCase() !== 'optgroup'
      ) {
        return;
      }

      var $option = $(this);

      var option = self.item($option);

      var matches = self.matches(params, option);

      if (matches !== null) {
        data.push(matches);
      }
    });

    callback({
      results: data
    });
  };

  SelectAdapter.prototype.addOptions = function ($options) {
    this.$element.append($options);
  };

  SelectAdapter.prototype.option = function (data) {
    var option;

    if (data.children) {
      option = document.createElement('optgroup');
      option.label = data.text;
    } else {
      option = document.createElement('option');

      if (option.textContent !== undefined) {
        option.textContent = data.text;
      } else {
        option.innerText = data.text;
      }
    }

    if (data.id !== undefined) {
      option.value = data.id;
    }

    if (data.disabled) {
      option.disabled = true;
    }

    if (data.selected) {
      option.selected = true;
    }

    if (data.title) {
      option.title = data.title;
    }

    var normalizedData = this._normalizeItem(data);
    normalizedData.element = option;

    // Override the option's data with the combined data
    Utils.StoreData(option, 'data', normalizedData);

    return $(option);
  };

  SelectAdapter.prototype.item = function ($option) {
    var data = {};

    data = Utils.GetData($option[0], 'data');

    if (data != null) {
      return data;
    }

    var option = $option[0];

    if (option.tagName.toLowerCase() === 'option') {
      data = {
        id: $option.val(),
        text: $option.text(),
        disabled: $option.prop('disabled'),
        selected: $option.prop('selected'),
        title: $option.prop('title')
      };
    } else if (option.tagName.toLowerCase() === 'optgroup') {
      data = {
        text: $option.prop('label'),
        children: [],
        title: $option.prop('title')
      };

      var $children = $option.children('option');
      var children = [];

      for (var c = 0; c < $children.length; c++) {
        var $child = $($children[c]);

        var child = this.item($child);

        children.push(child);
      }

      data.children = children;
    }

    data = this._normalizeItem(data);
    data.element = $option[0];

    Utils.StoreData($option[0], 'data', data);

    return data;
  };

  SelectAdapter.prototype._normalizeItem = function (item) {
    if (item !== Object(item)) {
      item = {
        id: item,
        text: item
      };
    }

    item = $.extend({}, {
      text: ''
    }, item);

    var defaults = {
      selected: false,
      disabled: false
    };

    if (item.id != null) {
      item.id = item.id.toString();
    }

    if (item.text != null) {
      item.text = item.text.toString();
    }

    if (item._resultId == null && item.id && this.container != null) {
      item._resultId = this.generateResultId(this.container, item);
    }

    return $.extend({}, defaults, item);
  };

  SelectAdapter.prototype.matches = function (params, data) {
    var matcher = this.options.get('matcher');

    return matcher(params, data);
  };

  return SelectAdapter;
});
<commit_msg>Unselect events: cache lookup key fix (#6179)<commit_after>define([
  './base',
  '../utils',
  'jquery'
], function (BaseAdapter, Utils, $) {
  function SelectAdapter ($element, options) {
    this.$element = $element;
    this.options = options;

    SelectAdapter.__super__.constructor.call(this);
  }

  Utils.Extend(SelectAdapter, BaseAdapter);

  SelectAdapter.prototype.current = function (callback) {
    var self = this;

    var data = Array.prototype.map.call(
      this.$element[0].querySelectorAll(':checked'),
      function (selectedElement) {
        return self.item($(selectedElement));
      }
    );

    callback(data);
  };

  SelectAdapter.prototype.select = function (data) {
    var self = this;

    data.selected = true;

    // If data.element is a DOM node, use it instead
    if (
      data.element != null && data.element.tagName.toLowerCase() === 'option'
    ) {
      data.element.selected = true;

      this.$element.trigger('input').trigger('change');

      return;
    }

    if (this.$element.prop('multiple')) {
      this.current(function (currentData) {
        var val = [];

        data = [data];
        data.push.apply(data, currentData);

        for (var d = 0; d < data.length; d++) {
          var id = data[d].id;

          if (val.indexOf(id) === -1) {
            val.push(id);
          }
        }

        self.$element.val(val);
        self.$element.trigger('input').trigger('change');
      });
    } else {
      var val = data.id;

      this.$element.val(val);
      this.$element.trigger('input').trigger('change');
    }
  };

  SelectAdapter.prototype.unselect = function (data) {
    var self = this;

    if (!this.$element.prop('multiple')) {
      return;
    }

    data.selected = false;

    if (
      data.element != null &&
      data.element.tagName.toLowerCase() === 'option'
    ) {
      data.element.selected = false;

      this.$element.trigger('input').trigger('change');

      return;
    }

    this.current(function (currentData) {
      var val = [];

      for (var d = 0; d < currentData.length; d++) {
        var id = currentData[d].id;

        if (id !== data.id && val.indexOf(id) === -1) {
          val.push(id);
        }
      }

      self.$element.val(val);

      self.$element.trigger('input').trigger('change');
    });
  };

  SelectAdapter.prototype.bind = function (container, $container) {
    var self = this;

    this.container = container;

    container.on('select', function (params) {
      self.select(params.data);
    });

    container.on('unselect', function (params) {
      container.$element.find('option').each(function () {
        if ($(this).val() == params.data.id) {
          var data = Utils.GetData(this, 'data');
          self.unselect(data);
        }
      });
    });
  };

  SelectAdapter.prototype.destroy = function () {
    // Remove anything added to child elements
    this.$element.find('*').each(function () {
      // Remove any custom data set by Select2
      Utils.RemoveData(this);
    });
  };

  SelectAdapter.prototype.query = function (params, callback) {
    var data = [];
    var self = this;

    var $options = this.$element.children();

    $options.each(function () {
      if (
        this.tagName.toLowerCase() !== 'option' &&
        this.tagName.toLowerCase() !== 'optgroup'
      ) {
        return;
      }

      var $option = $(this);

      var option = self.item($option);

      var matches = self.matches(params, option);

      if (matches !== null) {
        data.push(matches);
      }
    });

    callback({
      results: data
    });
  };

  SelectAdapter.prototype.addOptions = function ($options) {
    this.$element.append($options);
  };

  SelectAdapter.prototype.option = function (data) {
    var option;

    if (data.children) {
      option = document.createElement('optgroup');
      option.label = data.text;
    } else {
      option = document.createElement('option');

      if (option.textContent !== undefined) {
        option.textContent = data.text;
      } else {
        option.innerText = data.text;
      }
    }

    if (data.id !== undefined) {
      option.value = data.id;
    }

    if (data.disabled) {
      option.disabled = true;
    }

    if (data.selected) {
      option.selected = true;
    }

    if (data.title) {
      option.title = data.title;
    }

    var normalizedData = this._normalizeItem(data);
    normalizedData.element = option;

    // Override the option's data with the combined data
    Utils.StoreData(option, 'data', normalizedData);

    return $(option);
  };

  SelectAdapter.prototype.item = function ($option) {
    var data = {};

    data = Utils.GetData($option[0], 'data');

    if (data != null) {
      return data;
    }

    var option = $option[0];

    if (option.tagName.toLowerCase() === 'option') {
      data = {
        id: $option.val(),
        text: $option.text(),
        disabled: $option.prop('disabled'),
        selected: $option.prop('selected'),
        title: $option.prop('title')
      };
    } else if (option.tagName.toLowerCase() === 'optgroup') {
      data = {
        text: $option.prop('label'),
        children: [],
        title: $option.prop('title')
      };

      var $children = $option.children('option');
      var children = [];

      for (var c = 0; c < $children.length; c++) {
        var $child = $($children[c]);

        var child = this.item($child);

        children.push(child);
      }

      data.children = children;
    }

    data = this._normalizeItem(data);
    data.element = $option[0];

    Utils.StoreData($option[0], 'data', data);

    return data;
  };

  SelectAdapter.prototype._normalizeItem = function (item) {
    if (item !== Object(item)) {
      item = {
        id: item,
        text: item
      };
    }

    item = $.extend({}, {
      text: ''
    }, item);

    var defaults = {
      selected: false,
      disabled: false
    };

    if (item.id != null) {
      item.id = item.id.toString();
    }

    if (item.text != null) {
      item.text = item.text.toString();
    }

    if (item._resultId == null && item.id && this.container != null) {
      item._resultId = this.generateResultId(this.container, item);
    }

    return $.extend({}, defaults, item);
  };

  SelectAdapter.prototype.matches = function (params, data) {
    var matcher = this.options.get('matcher');

    return matcher(params, data);
  };

  return SelectAdapter;
});
<|endoftext|>


<commit_before>import FluidGeminiScrollbar from '../components/FluidGeminiScrollbar';

let scrollbarWidth = null;

const ScrollbarUtil = {
  /**
   * Taken from Gemini's source code with some edits
   * https://github.com/noeldelgado/gemini-scrollbar/blob/master/index.js#L22
   * @param  {Object} options
   * @return {Number} The width of the native scrollbar
   */
  getScrollbarWidth(options = {}) {
    if (scrollbarWidth == null || options.forceUpdate) {
      let element = global.document.createElement('div');

      element.style.position = 'absolute';
      element.style.top = '-9999px';
      element.style.width = '100px';
      element.style.height = '100px';
      element.style.overflow = 'scroll';
      element.style.msOverflowStyle = 'scrollbar';

      global.document.body.appendChild(element);
      scrollbarWidth = (element.offsetWidth - element.clientWidth);
      global.document.body.removeChild(element);
    }

    return scrollbarWidth;
  },

  updateWithRef(containerRef) {
    if (containerRef) {
      if (containerRef instanceof FluidGeminiScrollbar.constructor
        && containerRef.geminiRef != null) {
        containerRef.geminiRef.scrollbar.update();
      } else {
        containerRef.scrollbar.update();
      }
    }
  }
};

module.exports = ScrollbarUtil;
<commit_msg>Fix scrollbar update with ref util<commit_after>import GeminiScrollbar from 'react-gemini-scrollbar';

let scrollbarWidth = null;

const ScrollbarUtil = {
  /**
   * Taken from Gemini's source code with some edits
   * https://github.com/noeldelgado/gemini-scrollbar/blob/master/index.js#L22
   * @param  {Object} options
   * @return {Number} The width of the native scrollbar
   */
  getScrollbarWidth(options = {}) {
    if (scrollbarWidth == null || options.forceUpdate) {
      let element = global.document.createElement('div');

      element.style.position = 'absolute';
      element.style.top = '-9999px';
      element.style.width = '100px';
      element.style.height = '100px';
      element.style.overflow = 'scroll';
      element.style.msOverflowStyle = 'scrollbar';

      global.document.body.appendChild(element);
      scrollbarWidth = (element.offsetWidth - element.clientWidth);
      global.document.body.removeChild(element);
    }

    return scrollbarWidth;
  },

  updateWithRef(containerRef) {
    // Use the containers  gemini ref if present
    if (containerRef.geminiRef != null) {
      this.updateWithRef(containerRef.geminiRef);

      return;
    }

    if (containerRef instanceof GeminiScrollbar) {
      containerRef.scrollbar.update();
    }
  }
};

module.exports = ScrollbarUtil;
<|endoftext|>


<commit_before>//////////////////////////////////////////
// Needle -- Node.js HTTP Client
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2012-2013 - Fork Ltd.
// MIT Licensed
//////////////////////////////////////////

var fs          = require('fs'),
    http        = require('http'),
    https       = require('https'),
    url         = require('url'),
    stream      = require('stream'),
    stringify   = require('./querystring').build,
    multipart   = require('./multipart'),
    auth        = require('./auth'),
    parsers     = require('./parsers'),
    decoder     = require('./decoder');

//////////////////////////////////////////
// variabilia
//////////////////////////////////////////

var version     = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString()).version,
    debugging   = !!process.env.DEBUG,
    debug       = debugging ? console.log : function() { /* noop */ };

var user_agent  = 'Needle/' + version;
user_agent     += ' (Node.js ' + process.version + '; ' + process.platform + ' ' + process.arch + ')';

var node_tls_opts = 'agent pfx key passphrase cert ca ciphers rejectUnauthorized secureProtocol';

//////////////////////////////////////////
// decompressors for gzip/deflate bodies
//////////////////////////////////////////

var decompressors = {};

try {

  var zlib = require('zlib')

  decompressors['x-deflate'] = zlib.Inflate;
  decompressors['deflate']   = zlib.Inflate;
  decompressors['x-gzip']    = zlib.Gunzip;
  decompressors['gzip']      = zlib.Gunzip;

} catch(e) { /* zlib not available */ }

//////////////////////////////////////////
// defaults
//////////////////////////////////////////

var defaults = {
  accept          : '*/*',
  connection      : 'close',
  user_agent      : user_agent,
  follow          : 0,
  decode_response : true,
  parse_response  : true,
  timeout         : 10000,
  encoding        : 'utf8',
  boundary        : '--------------------NODENEEDLEHTTPCLIENT'
}

//////////////////////////////////////////
// the main act
//////////////////////////////////////////

var Needle = {

  request: function(method, uri, data, options, callback) {

    var self     = this,
        out      = new stream.PassThrough({ objectMode: false }),
        callback = (typeof options == 'function') ? options : callback,
        options  = options || {};

    // if no 'http' is found on URL, prepend it
    if (uri.indexOf('http') == -1) uri = 'http://' + uri;

    var config = {
      base_opts       : {},
      proxy           : options.proxy,
      output          : options.output,
      encoding        : options.encoding || (options.multipart ? 'binary' : defaults.encoding),
      decode_response : options.decode === false ? false : defaults.decode_response,
      parse_response  : options.parse === false ? false : defaults.parse_response,
      follow          : options.follow === true ? 10 : typeof options.follow == 'number' ? options.follow : defaults.follow,
      timeout         : (typeof options.timeout == 'number') ? options.timeout : defaults.timeout
    }

    // if any of node's TLS options are passed, let them be passed to https.request()
    node_tls_opts.split(' ').forEach(function(key) {
      if (typeof options[key] != 'undefined') {
        config.base_opts[key] = options[key];
        if (typeof options.agent == 'undefined')
          config.base_opts.agent = false; // otherwise tls options are skipped
      }
    });

    config.headers = {
      'Accept'     : options.accept     || defaults.accept,
      'Connection' : options.connection || defaults.connection,
      'User-Agent' : options.user_agent || defaults.user_agent
    }

    if (options.compressed && typeof zlib != 'undefined')
      config.headers['Accept-Encoding'] = 'gzip,deflate';

    for (var h in options.headers)
      config.headers[h] = options.headers[h];

    if (options.username) {
      if (options.auth && (options.auth == 'auto' || options.auth == 'digest')) {
        config.credentials = [options.username, options.password];
      } else {
        var auth_header = options.proxy ? 'Proxy-Authorization' : 'Authorization';
        config.headers[auth_header] = auth.basic(options.username, options.password);
      }
    }

    if (data) {
      if (method.toUpperCase() == 'GET') { // build query string and append to URI

        uri  = uri.replace(/\?.*|$/, '?' + stringify(data));
        post_data = null;

      } else if (options.multipart) { // build multipart body for request

        var boundary = options.boundary || defaults.boundary;

        multipart.build(data, boundary, function(err, body) {
          if (err) throw(err);

          config.headers['Content-Type']   = 'multipart/form-data; boundary=' + boundary;
          config.headers['Content-Length'] = body.length;
          self.send_request(1, method, uri, config, body, out, callback);
        });

        return out; // stream

      } else if (this.is_stream(data) || Buffer.isBuffer(data)) {

        post_data = data;

      } else { // format data according to content type, and send request

        var post_data = (typeof(data) === 'string') ? data :
            options.json ? JSON.stringify(data) : stringify(data);

        if (!config.headers['Content-Type']) {
          config.headers['Content-Type'] = options.json
          ? 'application/json'
          : 'application/x-www-form-urlencoded';
        }

        post_data = new Buffer(post_data, config.encoding);
        config.headers['Content-Length'] = post_data.length;

        if (options.json && config.headers['Accept'] === defaults.accept)
          config.headers['Accept'] = 'application/json';
      }
    }

    return this.send_request(1, method, uri, config, post_data, out, callback);
  },

  get_request_opts: function(method, uri, config) {
    var opts      = config.base_opts,
        proxy     = config.proxy,
        remote    = proxy ? url.parse(proxy) : url.parse(uri);

    opts.protocol = remote.protocol;
    opts.host     = remote.hostname;
    opts.port     = remote.port || (remote.protocol == 'https:' ? 443 : 80);
    opts.path     = proxy ? uri : remote.pathname + (remote.search || '');
    opts.method   = method;
    opts.headers  = config.headers;

    opts.headers['Host'] = proxy ? url.parse(uri).hostname : remote.hostname;
    if (opts.port != 80 && opts.port != 443)
      opts.headers['Host'] += ':' + opts.port;

    return opts;
  },

  get_auth_header: function(header, credentials, request_opts) {
    var type = header.split(' ')[0],
        user = credentials[0],
        pass = credentials[1];

    if (type == 'Digest') {
      return auth.digest(header, user, pass, request_opts.method, request_opts.path);
    } else if (type == 'Basic') {
      return auth.basic(user, pass);
    }
  },

  send_request: function(count, method, uri, config, post_data, out, callback) {

    var timer,
        returned     = 0,
        self         = this,
        request_opts = this.get_request_opts(method, uri, config),
        protocol     = request_opts.protocol == 'https:' ? https : http;

    var done = function(err, resp, body) {
      if (returned++ > 0) return;

      if (callback)
        callback(err, resp, body);
      else
        out.emit('end', err, resp, body);
    }

    debug('Making request #' + count, request_opts);
    var request = protocol.request(request_opts, function(resp) {

      var headers = resp.headers;
      debug('Got response', headers);
      if (timer) clearTimeout(timer);

      // if redirect code is found, send a GET request to that location if enabled via 'follow' option
      if ([301, 302].indexOf(resp.statusCode) != -1 && headers.location) {
        if (count <= config.follow) {
          out.emit('redirect', headers.location);
          delete config.headers['Content-Length']; // in case the original was a multipart POST request.
          return self.send_request(++count, 'GET', url.resolve(uri, headers.location), config, null, out, callback);
        } else if (config.follow > 0) {
          return done(new Error('Max redirects reached. Possible loop in: ' + headers.location));
        }
      }

      // if authentication is requested and credentials were not passed, resend request if we have user/pass
      if (resp.statusCode == 401 && headers['www-authenticate'] && config.credentials) {
        if (!config.headers['Authorization']) { // only if authentication hasn't been sent
          var auth_header = self.get_auth_header(headers['www-authenticate'], config.credentials, request_opts);

          if (auth_header) {
            config.headers['Authorization'] = auth_header;
            return self.send_request(count, method, uri, config, post_data, out, callback);
          }
        }
      }

      // ok so we got a valid (non-redirect & authorized) response. notify the stream guys.
      out.emit('headers', headers);

      var pipeline      = [],
          parsed        = false,
          mime          = self.parse_content_type(headers['content-type']),
          text_response = mime.type && mime.type.indexOf('text/') != -1;

      // To start, if our body is compressed and we're able to inflate it, do it.
      if (headers['content-encoding'] && decompressors[headers['content-encoding']]) {
        pipeline.push(decompressors[headers['content-encoding']]());
      }

      // If parse is enabled and we have a parser for it, then go for it.
      if (config.parse_response && parsers[mime.type]) {
        parsed = true;
        pipeline.push(parsers[mime.type]());

        // set objectMode on out stream to improve performance
        out._writableState.objectMode = true;
        out._readableState.objectMode = true;

      // If we're not parsing, and unless decoding was disabled, we'll try
      // decoding non UTF-8 bodies to UTF-8, using the iconv-lite library.
      } else if (text_response && config.decode_response
        && mime.charset && !mime.charset.match(/utf-?8$/i)) {
          pipeline.push(decoder(mime.charset));
      }

      // And `out` is the stream we finally push the decoded/parsed output to.
      pipeline.push(out);

      // Process the pipeline!
      var tmp = resp;
      while (pipeline.length) {
        tmp = tmp.pipe(pipeline.shift());
      }

      // If the user has requested and output file, pipe the output stream to it.
      // In stream mode, we will still get the response stream to play with.
      if (config.output && resp.statusCode == 200) {
        resp.pipe(fs.createWriteStream(config.output))
      }

      // Only aggregate the full body if a callback was requested.
      if (callback) {
        resp.raw   = [];
        resp.body  = [];
        resp.bytes = 0;

        // Count the amount of (raw) bytes passed using a PassThrough stream.
        var clean_pipe = new stream.PassThrough();
        resp.pipe(clean_pipe);

        clean_pipe.on('readable', function() {
          var chunk;
          while (chunk = this.read()) {
            resp.bytes += chunk.length;
            resp.raw.push(chunk);
          }
        })

        // Listen on the 'readable' event to aggregate the chunks.
        out.on('readable', function() {
          var chunk;
          while (chunk = this.read()) {
            // We're either pushing buffers or objects, never strings.
            if (typeof chunk == 'string') chunk = new Buffer(chunk);

            // Push all chunks to resp.body. We'll bind them in resp.end().
            resp.body.push(chunk);
          }
        })

        // And set the .body property once all data is in.
        out.on('end', function() {

          resp.raw = Buffer.concat(resp.raw);

          // if parse was successful, we should have an array with one object
          if (resp.body[0] && !Buffer.isBuffer(resp.body[0])) {
            resp.body = resp.body[0];
          } else { // we got one or several buffers. string or binary.
            resp.body = Buffer.concat(resp.body);

            // if we're here and parsed is true, it means we tried to but it didn't work.
            // so given that we got a text response, let's stringify it.
            if (text_response || parsed) {
              resp.body = resp.body.toString();
            }
          }

          done(null, resp, resp.body);
        });

      }

    }); // end request call

    // unless timeout was disabled, set a timeout to abort the request
    if (config.timeout > 0) {
      timer = setTimeout(function() {
        request.abort();
      }, config.timeout)
    }

    request.on('error', function(err) {
      debug('Request error', err);
      if (timer) clearTimeout(timer);

      done(err || new Error('Unknown error when making request.'));
    });

    if (post_data) {
      if (this.is_stream(post_data)) {
        post_data.pipe(request);
      } else {
        request.write(post_data, config.encoding);
        request.end();
      }
    } else {
      request.end();
    }

    out.request = request;
    return out;
  },

  parse_content_type: function(header) {
    if (!header || header == '') return {};

    var charset = 'iso-8859-1', arr = header.split(';');
    try { charset = arr[1].match(/charset=(.+)/)[1] } catch (e) { /* not found */ }

    return { type: arr[0], charset: charset };
  },

  is_stream: function(obj) {
    return typeof obj.pipe === 'function';
  }
}

exports.version = version;

exports.defaults = function(obj) {
  for (var key in obj) {
    if (defaults[key] && typeof obj[key] != 'undefined')
      defaults[key] = obj[key];
  }
  return defaults;
}

'head get'.split(' ').forEach(function(method) {
  exports[method] = function(uri, options, callback) {
    return Needle.request(method, uri, null, options, callback);
  }
})

'post put delete'.split(' ').forEach(function(method) {
  exports[method] = function(uri, data, options, callback) {
    return Needle.request(method, uri, data, options, callback);
  }
})

exports.request = function(method, uri, data, opts, callback) {
  return Needle.request(method.toUpperCase(), uri, data, opts, callback);
};
<commit_msg>Some comments.<commit_after>//////////////////////////////////////////
// Needle -- Node.js HTTP Client
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2012-2013 - Fork Ltd.
// MIT Licensed
//////////////////////////////////////////

var fs          = require('fs'),
    http        = require('http'),
    https       = require('https'),
    url         = require('url'),
    stream      = require('stream'),
    stringify   = require('./querystring').build,
    multipart   = require('./multipart'),
    auth        = require('./auth'),
    parsers     = require('./parsers'),
    decoder     = require('./decoder');

//////////////////////////////////////////
// variabilia
//////////////////////////////////////////

var version     = JSON.parse(fs.readFileSync(__dirname + '/../package.json').toString()).version,
    debugging   = !!process.env.DEBUG,
    debug       = debugging ? console.log : function() { /* noop */ };

var user_agent  = 'Needle/' + version;
user_agent     += ' (Node.js ' + process.version + '; ' + process.platform + ' ' + process.arch + ')';

var node_tls_opts = 'agent pfx key passphrase cert ca ciphers rejectUnauthorized secureProtocol';

//////////////////////////////////////////
// decompressors for gzip/deflate bodies
//////////////////////////////////////////

var decompressors = {};

try {

  var zlib = require('zlib')

  decompressors['x-deflate'] = zlib.Inflate;
  decompressors['deflate']   = zlib.Inflate;
  decompressors['x-gzip']    = zlib.Gunzip;
  decompressors['gzip']      = zlib.Gunzip;

} catch(e) { /* zlib not available */ }

//////////////////////////////////////////
// defaults
//////////////////////////////////////////

var defaults = {
  accept          : '*/*',
  connection      : 'close',
  user_agent      : user_agent,
  follow          : 0,
  decode_response : true,
  parse_response  : true,
  timeout         : 10000,
  encoding        : 'utf8',
  boundary        : '--------------------NODENEEDLEHTTPCLIENT'
}

//////////////////////////////////////////
// the main act
//////////////////////////////////////////

var Needle = {

  request: function(method, uri, data, options, callback) {

    var self     = this,
        out      = new stream.PassThrough({ objectMode: false }),
        callback = (typeof options == 'function') ? options : callback,
        options  = options || {};

    // if no 'http' is found on URL, prepend it
    if (uri.indexOf('http') == -1) uri = 'http://' + uri;

    var config = {
      base_opts       : {},
      proxy           : options.proxy,
      output          : options.output,
      encoding        : options.encoding || (options.multipart ? 'binary' : defaults.encoding),
      decode_response : options.decode === false ? false : defaults.decode_response,
      parse_response  : options.parse === false ? false : defaults.parse_response,
      follow          : options.follow === true ? 10 : typeof options.follow == 'number' ? options.follow : defaults.follow,
      timeout         : (typeof options.timeout == 'number') ? options.timeout : defaults.timeout
    }

    // if any of node's TLS options are passed, let them be passed to https.request()
    node_tls_opts.split(' ').forEach(function(key) {
      if (typeof options[key] != 'undefined') {
        config.base_opts[key] = options[key];
        if (typeof options.agent == 'undefined')
          config.base_opts.agent = false; // otherwise tls options are skipped
      }
    });

    config.headers = {
      'Accept'     : options.accept     || defaults.accept,
      'Connection' : options.connection || defaults.connection,
      'User-Agent' : options.user_agent || defaults.user_agent
    }

    if (options.compressed && typeof zlib != 'undefined')
      config.headers['Accept-Encoding'] = 'gzip,deflate';

    for (var h in options.headers)
      config.headers[h] = options.headers[h];

    if (options.username) {
      if (options.auth && (options.auth == 'auto' || options.auth == 'digest')) {
        config.credentials = [options.username, options.password];
      } else {
        var auth_header = options.proxy ? 'Proxy-Authorization' : 'Authorization';
        config.headers[auth_header] = auth.basic(options.username, options.password);
      }
    }

    if (data) {
      if (method.toUpperCase() == 'GET') { // build query string and append to URI

        uri  = uri.replace(/\?.*|$/, '?' + stringify(data));
        post_data = null;

      } else if (options.multipart) { // build multipart body for request

        var boundary = options.boundary || defaults.boundary;

        multipart.build(data, boundary, function(err, body) {
          if (err) throw(err);

          config.headers['Content-Type']   = 'multipart/form-data; boundary=' + boundary;
          config.headers['Content-Length'] = body.length;
          self.send_request(1, method, uri, config, body, out, callback);
        });

        return out; // stream

      } else if (this.is_stream(data) || Buffer.isBuffer(data)) {

        post_data = data;

      } else { // string or object data, no multipart.

        // format data according to content type
        var post_data = (typeof(data) === 'string') ? data :
            options.json ? JSON.stringify(data) : stringify(data);

        // if no content-type was passed, determine if json or not.
        if (!config.headers['Content-Type']) {
          config.headers['Content-Type'] = options.json
          ? 'application/json'
          : 'application/x-www-form-urlencoded';
        }

        post_data = new Buffer(post_data, config.encoding);
        config.headers['Content-Length'] = post_data.length;

        // unless a specific accept header was passed, assume we want a json response back.
        if (options.json && config.headers['Accept'] === defaults.accept)
          config.headers['Accept'] = 'application/json';
      }
    }

    return this.send_request(1, method, uri, config, post_data, out, callback);
  },

  get_request_opts: function(method, uri, config) {
    var opts      = config.base_opts,
        proxy     = config.proxy,
        remote    = proxy ? url.parse(proxy) : url.parse(uri);

    opts.protocol = remote.protocol;
    opts.host     = remote.hostname;
    opts.port     = remote.port || (remote.protocol == 'https:' ? 443 : 80);
    opts.path     = proxy ? uri : remote.pathname + (remote.search || '');
    opts.method   = method;
    opts.headers  = config.headers;

    opts.headers['Host'] = proxy ? url.parse(uri).hostname : remote.hostname;
    if (opts.port != 80 && opts.port != 443)
      opts.headers['Host'] += ':' + opts.port;

    return opts;
  },

  get_auth_header: function(header, credentials, request_opts) {
    var type = header.split(' ')[0],
        user = credentials[0],
        pass = credentials[1];

    if (type == 'Digest') {
      return auth.digest(header, user, pass, request_opts.method, request_opts.path);
    } else if (type == 'Basic') {
      return auth.basic(user, pass);
    }
  },

  send_request: function(count, method, uri, config, post_data, out, callback) {

    var timer,
        returned     = 0,
        self         = this,
        request_opts = this.get_request_opts(method, uri, config),
        protocol     = request_opts.protocol == 'https:' ? https : http;

    var done = function(err, resp, body) {
      if (returned++ > 0) return;

      if (callback)
        callback(err, resp, body);
      else
        out.emit('end', err, resp, body);
    }

    debug('Making request #' + count, request_opts);
    var request = protocol.request(request_opts, function(resp) {

      var headers = resp.headers;
      debug('Got response', headers);
      if (timer) clearTimeout(timer);

      // if redirect code is found, send a GET request to that location if enabled via 'follow' option
      if ([301, 302].indexOf(resp.statusCode) != -1 && headers.location) {
        if (count <= config.follow) {
          out.emit('redirect', headers.location);
          delete config.headers['Content-Length']; // in case the original was a multipart POST request.
          return self.send_request(++count, 'GET', url.resolve(uri, headers.location), config, null, out, callback);
        } else if (config.follow > 0) {
          return done(new Error('Max redirects reached. Possible loop in: ' + headers.location));
        }
      }

      // if authentication is requested and credentials were not passed, resend request if we have user/pass
      if (resp.statusCode == 401 && headers['www-authenticate'] && config.credentials) {
        if (!config.headers['Authorization']) { // only if authentication hasn't been sent
          var auth_header = self.get_auth_header(headers['www-authenticate'], config.credentials, request_opts);

          if (auth_header) {
            config.headers['Authorization'] = auth_header;
            return self.send_request(count, method, uri, config, post_data, out, callback);
          }
        }
      }

      // ok so we got a valid (non-redirect & authorized) response. notify the stream guys.
      out.emit('headers', headers);

      var pipeline      = [],
          parsed        = false,
          mime          = self.parse_content_type(headers['content-type']),
          text_response = mime.type && mime.type.indexOf('text/') != -1;

      // To start, if our body is compressed and we're able to inflate it, do it.
      if (headers['content-encoding'] && decompressors[headers['content-encoding']]) {
        pipeline.push(decompressors[headers['content-encoding']]());
      }

      // If parse is enabled and we have a parser for it, then go for it.
      if (config.parse_response && parsers[mime.type]) {
        parsed = true;
        pipeline.push(parsers[mime.type]());

        // set objectMode on out stream to improve performance
        out._writableState.objectMode = true;
        out._readableState.objectMode = true;

      // If we're not parsing, and unless decoding was disabled, we'll try
      // decoding non UTF-8 bodies to UTF-8, using the iconv-lite library.
      } else if (text_response && config.decode_response
        && mime.charset && !mime.charset.match(/utf-?8$/i)) {
          pipeline.push(decoder(mime.charset));
      }

      // And `out` is the stream we finally push the decoded/parsed output to.
      pipeline.push(out);

      // Now release the kraken!
      var tmp = resp;
      while (pipeline.length) {
        tmp = tmp.pipe(pipeline.shift());
      }

      // If the user has requested and output file, pipe the output stream to it.
      // In stream mode, we will still get the response stream to play with.
      if (config.output && resp.statusCode == 200) {
        resp.pipe(fs.createWriteStream(config.output))
      }

      // Only aggregate the full body if a callback was requested.
      if (callback) {
        resp.raw   = [];
        resp.body  = [];
        resp.bytes = 0;

        // Count the amount of (raw) bytes passed using a PassThrough stream.
        var clean_pipe = new stream.PassThrough();
        resp.pipe(clean_pipe);

        clean_pipe.on('readable', function() {
          var chunk;
          while (chunk = this.read()) {
            resp.bytes += chunk.length;
            resp.raw.push(chunk);
          }
        })

        // Listen on the 'readable' event to aggregate the chunks.
        out.on('readable', function() {
          var chunk;
          while (chunk = this.read()) {
            // We're either pushing buffers or objects, never strings.
            if (typeof chunk == 'string') chunk = new Buffer(chunk);

            // Push all chunks to resp.body. We'll bind them in resp.end().
            resp.body.push(chunk);
          }
        })

        // And set the .body property once all data is in.
        out.on('end', function() {

          // we may want access to the raw data, so keep a reference.
          resp.raw = Buffer.concat(resp.raw);

          // if parse was successful, we should have an array with one object
          if (resp.body[0] && !Buffer.isBuffer(resp.body[0])) {
            resp.body = resp.body[0];
          } else { // we got one or several buffers. string or binary.
            resp.body = Buffer.concat(resp.body);

            // if we're here and parsed is true, it means we tried to but it didn't work.
            // so given that we got a text response, let's stringify it.
            if (text_response || parsed) {
              resp.body = resp.body.toString();
            }
          }

          // time to call back, junior.
          done(null, resp, resp.body);
        });

      }

    }); // end request call

    // unless timeout was disabled, set a timeout to abort the request
    if (config.timeout > 0) {
      timer = setTimeout(function() {
        request.abort();
      }, config.timeout)
    }

    request.on('error', function(err) {
      debug('Request error', err);
      if (timer) clearTimeout(timer);

      done(err || new Error('Unknown error when making request.'));
    });

    if (post_data) {
      if (this.is_stream(post_data)) {
        post_data.pipe(request);
      } else {
        request.write(post_data, config.encoding);
        request.end();
      }
    } else {
      request.end();
    }

    out.request = request;
    return out;
  },

  parse_content_type: function(header) {
    if (!header || header == '') return {};

    var charset = 'iso-8859-1', arr = header.split(';');
    try { charset = arr[1].match(/charset=(.+)/)[1] } catch (e) { /* not found */ }

    return { type: arr[0], charset: charset };
  },

  is_stream: function(obj) {
    return typeof obj.pipe === 'function';
  }
}

exports.version = version;

exports.defaults = function(obj) {
  for (var key in obj) {
    if (defaults[key] && typeof obj[key] != 'undefined')
      defaults[key] = obj[key];
  }
  return defaults;
}

'head get'.split(' ').forEach(function(method) {
  exports[method] = function(uri, options, callback) {
    return Needle.request(method, uri, null, options, callback);
  }
})

'post put delete'.split(' ').forEach(function(method) {
  exports[method] = function(uri, data, options, callback) {
    return Needle.request(method, uri, data, options, callback);
  }
})

exports.request = function(method, uri, data, opts, callback) {
  return Needle.request(method, uri, data, opts, callback);
};
<|endoftext|>


<commit_before>/*jslint node:true, browser:true, bitwise:true */

'use strict';

/**
 * MIDI output wrapper
 *
 * Add helper methods to ease working with
 * native MIDI output.
 *
 * @param {native: MIDIOutput}  Native MIDIOutput to wrap around
 */
function Output(native) {
    this.native = native || {
        send: function () {
            return undefined;
        }
    };
}

/**
 * Release a note on a channel
 *
 * Send a MIDI message to release a note given channel, after
 * given delay.
 *
 * @param {channel: int}    Channel ID
 * @param {program: int}    Note ID (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.noteOff = function (channel, note, delay) {
    this.native.send([0x80 + channel, note, 0], delay || 0);
};

/**
 * Press a note on a channel
 *
 * Send a MIDI message to press a note on given channel, with
 * given velocity, after given delay.
 *
 * @param {channel: int}    Channel ID (0 - 15)
 * @param {note: int}       Note ID (0 - 127)
 * @param {velocity: int}   Pressing velocity (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.noteOn = function (channel, note, velocity, delay) {
    this.native.send([0x90 + channel, note, velocity], delay || 0);
};

/**
 * Set note aftertouch on a channel
 *
 * Send a MIDI message to change note pressure, after given delay,
 * on given channel.
 *
 * @param {channel: int}    Channel ID
 * @param {program: int}    Note ID (0 - 127)
 * @param {pressure: int}   Pressure amount (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.noteAftertouch = function (channel, note, pressure, delay) {
    this.native.send([0xA0 + channel, note, pressure], delay || 0);
};

/**
 * Apply a controller to a channel
 *
 * Send a MIDI message to change channel state using
 * a controller, after given delay (see Output.controllers for
 * a list of controllers).
 *
 * @param {channel: int}    Channel ID
 * @param {controller: int} Controller type (0 - 127)
 * @param {value: int}      Controller value (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.controller = function (channel, controller, value, delay) {
    this.native.send([0xB0 + channel, controller, value], delay || 0);
};

/**
 * Change program on a channel
 *
 * Send a MIDI message to change program on given channel, after
 * given delay.
 *
 * @param {channel: int}    Channel ID (0 - 15)
 * @param {program: int}    Program ID (0 - 127; see programs.js)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.programChange = function (channel, program, delay) {
    this.native.send([0xC0 + channel, program], delay || 0);
};

/**
 * Set global note aftertouch on a channel
 *
 * Send a MIDI message to change all notes pressure,
 * after given delay, on given channel.
 *
 * @param {channel: int}    Channel ID
 * @param {pressure: int}   Pressure amount (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.channelAftertouch = function (channel, pressure, delay) {
    this.native.send([0xD0 + channel, pressure], delay || 0);
};

/**
 * Increase or decrease pitch on a channel
 *
 * Send a MIDI message to increase or decrease pitch value, after given delay,
 * on given channel.
 *
 * @param {channel: int}    Channel ID
 * @param {value: int}      Pitch variation (-8192 - 8191)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.pitchBend = function (channel, value, delay) {
    var lsb, msb;

    value += 8192;
    lsb = value & 127;
    msb = value >> 7;

    this.native.send([0xE0 + channel, lsb, msb], delay || 0);
};

module.exports = Output;<commit_msg>:bulb: Add velocity param to noteOff()<commit_after>/*jslint node:true, browser:true, bitwise:true */

'use strict';

/**
 * MIDI output wrapper
 *
 * Add helper methods to ease working with
 * native MIDI output.
 *
 * @param {native: MIDIOutput}  Native MIDIOutput to wrap around
 */
function Output(native) {
    this.native = native || {
        send: function () {
            return undefined;
        }
    };
}

/**
 * Release a note on a channel
 *
 * Send a MIDI message to release a note given channel, after
 * given delay.
 *
 * @param {channel: int}    Channel ID
 * @param {program: int}    Note ID (0 - 127)
 * @param {velocity: int}   Pressing velocity (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.noteOff = function (channel, note, velocity, delay) {
    this.native.send([0x80 + channel, note, velocity], delay || 0);
};

/**
 * Press a note on a channel
 *
 * Send a MIDI message to press a note on given channel, with
 * given velocity, after given delay.
 *
 * @param {channel: int}    Channel ID (0 - 15)
 * @param {note: int}       Note ID (0 - 127)
 * @param {velocity: int}   Pressing velocity (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.noteOn = function (channel, note, velocity, delay) {
    this.native.send([0x90 + channel, note, velocity], delay || 0);
};

/**
 * Set note aftertouch on a channel
 *
 * Send a MIDI message to change note pressure, after given delay,
 * on given channel.
 *
 * @param {channel: int}    Channel ID
 * @param {program: int}    Note ID (0 - 127)
 * @param {pressure: int}   Pressure amount (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.noteAftertouch = function (channel, note, pressure, delay) {
    this.native.send([0xA0 + channel, note, pressure], delay || 0);
};

/**
 * Apply a controller to a channel
 *
 * Send a MIDI message to change channel state using
 * a controller, after given delay (see Output.controllers for
 * a list of controllers).
 *
 * @param {channel: int}    Channel ID
 * @param {controller: int} Controller type (0 - 127)
 * @param {value: int}      Controller value (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.controller = function (channel, controller, value, delay) {
    this.native.send([0xB0 + channel, controller, value], delay || 0);
};

/**
 * Change program on a channel
 *
 * Send a MIDI message to change program on given channel, after
 * given delay.
 *
 * @param {channel: int}    Channel ID (0 - 15)
 * @param {program: int}    Program ID (0 - 127; see programs.js)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.programChange = function (channel, program, delay) {
    this.native.send([0xC0 + channel, program], delay || 0);
};

/**
 * Set global note aftertouch on a channel
 *
 * Send a MIDI message to change all notes pressure,
 * after given delay, on given channel.
 *
 * @param {channel: int}    Channel ID
 * @param {pressure: int}   Pressure amount (0 - 127)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.channelAftertouch = function (channel, pressure, delay) {
    this.native.send([0xD0 + channel, pressure], delay || 0);
};

/**
 * Increase or decrease pitch on a channel
 *
 * Send a MIDI message to increase or decrease pitch value, after given delay,
 * on given channel.
 *
 * @param {channel: int}    Channel ID
 * @param {value: int}      Pitch variation (-8192 - 8191)
 * @param {delay: int}      Delay in seconds
 */
Output.prototype.pitchBend = function (channel, value, delay) {
    var lsb, msb;

    value += 8192;
    lsb = value & 127;
    msb = value >> 7;

    this.native.send([0xE0 + channel, lsb, msb], delay || 0);
};

module.exports = Output;<|endoftext|>


<commit_before>var formPage=function()
{
    var questions=[];
    var corForm;
    var numOfQuestions
    var plus_btn=''+
                  ''+
                '';
    var finish_btn='<button type="button" class="btn btn-success btn-lg " id="finish_btn" >סיום</button>'

//================================================================================================
    var addQuestion=function()
    {
        var n=document.getElementById("numOfOption").value;
        var q=document.getElementById("newQuestion").value;
       // var question=Question.create(q,n);
       questions.push(Question.create(q,n));
        //firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+corForm.key+'/questions').push(question);
        Form.allQ(questions);
        $(".addQPage").remove();
       $("#body").append(plus_btn);
       $("#plus_btn").click(addQuestionPage);
       // p(corForm.key);
    }

//================================================================================================
    var addQuestionPage=function()
    {
        $("#plus_btn").remove();
        var str='
'+
                    '
'+
                        '<input type="text" class="form-control" name="name" id="newQuestion" placeholder="הכנס שאלה " dir="rtl" />'+
                    '
'+
                    '<select class = "userCkecklist" id = "numOfOption" required >'+
                        '<option value="5" >5</option>'+
                        '<option value="10">10</option>'+
                    '</select>
'+
                    'הוסף'+
                '
';
        $(".listQue").append(str);
        $("#addQuestion_btn").click(addQuestion);
    }
//================================================================================================
    var create=function()
    {
        $('.NAV').collapse('hide');
        $("#body").html("");
        questions=[];
        var str=
        '
'+
            'בחר נושא:
'+
            '<input type="text" class="form-control" name="name" id="formSubject" maxlength="40" placeholder="הכנס נושא - טופס חדש " dir="rtl" />'+
            '
בחר מועדונית:
'+
            "
"+
                ""+
                "<select type='text' id='clubhousesForm' class='form-control clubHouseName'></select>"+
            "
"+
            '
בחר מספר שאלות:
'+
             "
"+
                ""+
                "<select type='text'  id='numOfQustions' class='form-control'>"+
                    "<option value='1'>1</option>"+
                    "<option value='2'>2</option>"+
                    "<option value='3'>3</option>"+
                    "<option value='4'>4</option>"+
                    "<option value='5'>5</option>"+
                    "<option value='6'>6</option>"+
                    "<option value='7'>7</option>"+
                    "<option value='8'>8</option>"+
                    "<option value='9'>9</option>"+
                    "<option value='10'>10</option>"+
                    "<option value='11'>11</option>"+
                    "<option value='12'>12</option>"+
                    "<option value='13'>13</option>"+
                    "<option value='14'>14</option>"+
                    "<option value='15'>15</option>"+
                "</select>"+
            "
"+
        '
'+

        '
צור טופס חדש';
        $("#body").html(str);
        updateClubs();
        $("#createForm_btn").click(createForm);
    }
//================================================================================================

    var finish=function()
    {
        firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+corForm.key+'/questions').set(questions);
        alert("טופס הוזן בהצלחה");
        create();
    }

//================================================================================================
    var createForm=function()
    {
        var sub = document.getElementById("formSubject").value;
        var numOfQustions = document.getElementById("numOfQustions").value;
        var toClubHouse = document.getElementById("clubhousesForm").value;
        if(sub == "" || sub == undefined)
        {
            alert("אנה הזן שם לטופס");
            return;
        }
        if(toClubHouse == "nun")
        {
            alert("אנה בחר מועדונית");
            return;
        }
        if(numOfQustions == "nun")
        {
            alert("אנה בחר מספר שאלות");
            return;
        }

        var new_form=Form.create(sub);
        corForm = firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms').push(new_form);

        var str='
'+sub+'

'
        $("#body").html(str+"

");
        for(var i=0;i<numOfQustions;i++)
        {
            var k=i+1;
            var list='
'+
                    '<input type="text" class="form-control qestionInput" maxlength="100" placeholder="שאלה '+k+'" dir="rtl" />'+
                    "<select type='text' value='nan'  id='numOfAnswers' class='form-control clubHouseName' placeholder='בחר מועדונית מתוך הרשימה'>"+
                        "<option value='5_Options'>5</option>"+
                        "<option value='10_Options'>10</option>"+
                    "</select>"+
                '
';
            $("#listQue").append(list);
        }
        $("#body").append(finish_btn);

        /*
        var str='
'+sub+'

'+
                '

'+plus_btn+finsh_btn;
        Form.allQ(corForm.key);
        $("#body").html(str);
        $("#plus_btn").click(addQuestionPage);
        $("#finsh_btn").click(finish);*/
    }

//================================================================================================
    var addQtoTable=function(q,a,i)
    {
        var queStr='
'+
					  ''+i+'	'+
					  ''+q.question+'	'+
					  ''+a[i]+'/'+q.numOfvalues+'	'+
		    '
`';
        return queStr;
    }
    var keysArray=function(objects)
    {
        var keys=[];
        for(obj in objects )
            keys.push(obj);
        return keys;
    }
    var showForm = function(form,user)
    {
        $('.NAV').collapse('hide');
        $("#body").html("");
        var showExactForm =

        `<h2 id='allTitles'>`+form.subject+`

					  #
					  תוכן השאלה
					  ערך

`;
        var answers=keysArray(form.result);
        var ans;
        for(var i=0;i<answers.length;i++)//find the user answer
            if(form.result[answers[i]].user===user)
                ans=form.result[answers[i]].answers;
        var keys=keysArray(form.questions);
		for(i=0;i<keys.length;i++)
            showExactForm+=addQtoTable(form.questions[keys[i]],ans,i);
          firebase.database().ref('users/'+user).once("value")
        .then(function(data)
        {
            var thisUser=data.val();
            p(thisUser);
            showExactForm+=  `

מאת :`+thisUser.lastName+` `+thisUser.firstName+` `+`

ילד :`+thisUser.childName+` `+`

תאריך: 29/5/17

`;
            $("#body").html(showExactForm);
        });
    }
//================================================================================================
    var p=function(s)
    {
        console.log(s);
    }
//================================================================================================
    var fillFrom = function(key)
    {
    firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+key).once("value")
        .then(function(data)
        {
            var keys=[];
            for(obj in data.be().questions )
                keys.push(obj);
            numOfQuestions=keys.length;
            var form=data.val();
            var str;
            $("#body").html('');
            $("#body").css("text-align", "right");

            for(var i=0;i<keys.length;i++)
            {
            firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+key).once("value")
                .then(function(data)
                {
                    var keys=[];
                    for(obj in data.be().questions )
                        keys.push(obj);
                    var form=data.val();
                    var str;
                    $("#body").html('');
                    $("#body").html('
'+form.subject+'

');
                    $("#body").css("text-align", "right");

                    for(var i=0;i<keys.length;i++)
                    {
                        str='
'+
                                '
'+form.questions[keys[i]].question+'
'+
                                '<form id="form_'+i+'"class="choiceList">'+
                                '</form>'+
                            '
'

                        $("#body").append(str);
                        for(var j=0;j<form.questions[keys[i]].numOfvalues;j++)
                        {
                            str=''+(j+1)+'
'+
                                '<input type="radio" name="select" value="'+(j+1)+'"/>'+
                                '';
                            $("#form_"+i).append(str);
                        }
                    }
                });
                var btn='שלח';
                $("body").append(btn);
            }
        });
    }
    var loadAllForms=function()
    {
        $('.Nav').collapse('hide');
        firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms').once("value")
        .then(function(data)
        {
            var str="";
            var forms=data.val();
            var i=0;
            $("#body").html("");
            for(key in forms)
            {
                var tempId ="form_"+i;
                str=''+
                '
'+forms[key].subject+'';
                $("#body").append(str);
                mainPage.paintButton(i,tempId);
                $("#form_"+(i++)).click(function()
                {
                    fillFrom(key);
                });

            }

        });
    }

    var test=function(id)
    {
        fillFrom(id);
    }

    var updateClubs=function()				//update the list of clubHouses
	{
        firebase.database().ref("clubhouse/").once("value")
        .then(function(data)
        {
            var clubs=data.val();
            var keys=Object.keys(clubs);
            for(var i=0;i<keys.length;i++)
                $("#clubhousesForm").append('<option value='+clubs[keys[i]].ClubhouseDBkey+'>'+clubs[keys[i]].name+'</option>');

            $("#clubhousesForm").append('<option value="allClubs">כל המועדוניות</option>');
        });
	}

//================================================================================================
    var sendForm=function(questions)
    {
        var ans=[];
        for(var i=0;i<numOfQuestions;i++)
        {
            var maxSelect=questions[i].numOfvalues;
            for(var j=0;j<maxSelect;j++)
            {
                var check =document.getElementById("rbtn_"+i+"_"+j);
                if(check.checked)
                    ans.push(j+1
                    );
            }
        }
        if(ans.length<numOfQuestions)
        {
            alert("יש לענות על כל השאלות");
            return;
        }
        p(corForm);
        var result={user:login.correntUser[1],answers:ans};
        firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+corForm+'/result').push(result);
    }

    var test=function()
    {
        firebase.database().ref('clubhouse/-Kn-BZInu3pPlaW11V7v/forms/-Kn6CV3USW-N-KeL1up2').once("value")
        .then(function(data)
        {
            var form=data.val();
            var user="DIgNUeLsWndpOtrvK49MUbS3rgg2";
            showForm(form,user);
        });
    }
    return {
        create:create,showForm:showForm,
        loadAllForms:loadAllForms,
        test:test
    }
}();<commit_msg>more design<commit_after>var formPage=function()
{
    var questions=[];
    var corForm;
    var numOfQuestions
    var plus_btn=''+
                  ''+
                '';
    var finish_btn='<button type="button" class="btn btn-success btn-lg " id="finish_btn" >סיום</button>'

//================================================================================================
    var addQuestion=function()
    {
        var n=document.getElementById("numOfOption").value;
        var q=document.getElementById("newQuestion").value;
       // var question=Question.create(q,n);
       questions.push(Question.create(q,n));
        //firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+corForm.key+'/questions').push(question);
        Form.allQ(questions);
        $(".addQPage").remove();
       $("#body").append(plus_btn);
       $("#plus_btn").click(addQuestionPage);
       // p(corForm.key);
    }

//================================================================================================
    var addQuestionPage=function()
    {
        $("#plus_btn").remove();
        var str='
'+
                    '
'+
                        '<input type="text" class="form-control" name="name" id="newQuestion" placeholder="הכנס שאלה " dir="rtl" />'+
                    '
'+
                    '<select class = "userCkecklist" id = "numOfOption" required >'+
                        '<option value="5" >5</option>'+
                        '<option value="10">10</option>'+
                    '</select>
'+
                    'הוסף'+
                '
';
        $(".listQue").append(str);
        $("#addQuestion_btn").click(addQuestion);
    }
//================================================================================================
    var create=function()
    {
        $('.NAV').collapse('hide');
        $("#body").html("");
        questions=[];
        var str=
        '
'+
            'בחר נושא:
'+
            '<input type="text" class="form-control" name="name" id="formSubject" maxlength="40" placeholder="הכנס נושא - טופס חדש " dir="rtl" />'+
            '
בחר מועדונית:
'+
            "
"+
                ""+
                "<select type='text' id='clubhousesForm' class='form-control clubHouseName'></select>"+
            "
"+
            '
בחר מספר שאלות:
'+
             "
"+
                ""+
                "<select type='text'  id='numOfQustions' class='form-control'>"+
                    "<option value='1'>1</option>"+
                    "<option value='2'>2</option>"+
                    "<option value='3'>3</option>"+
                    "<option value='4'>4</option>"+
                    "<option value='5'>5</option>"+
                    "<option value='6'>6</option>"+
                    "<option value='7'>7</option>"+
                    "<option value='8'>8</option>"+
                    "<option value='9'>9</option>"+
                    "<option value='10'>10</option>"+
                    "<option value='11'>11</option>"+
                    "<option value='12'>12</option>"+
                    "<option value='13'>13</option>"+
                    "<option value='14'>14</option>"+
                    "<option value='15'>15</option>"+
                "</select>"+
            "
"+
        '
'+

        '
צור טופס חדש';
        $("#body").html(str);
        updateClubs();
        $("#createForm_btn").click(createForm);
    }
//================================================================================================

    var finish=function()
    {
        firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+corForm.key+'/questions').set(questions);
        alert("טופס הוזן בהצלחה");
        create();
    }

//================================================================================================
    var createForm=function()
    {
        var sub = document.getElementById("formSubject").value;
        var numOfQustions = document.getElementById("numOfQustions").value;
        var toClubHouse = document.getElementById("clubhousesForm").value;
        if(sub == "" || sub == undefined)
        {
            alert("אנה הזן שם לטופס");
            return;
        }
        if(toClubHouse == "nun")
        {
            alert("אנה בחר מועדונית");
            return;
        }
        if(numOfQustions == "nun")
        {
            alert("אנה בחר מספר שאלות");
            return;
        }

        var new_form=Form.create(sub);
        corForm = firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms').push(new_form);

        var str='
'+sub+'

'
        $("#body").html(str+"

");
        for(var i=0;i<numOfQustions;i++)
        {
            var k=i+1;
            var list='
'+
                    '<input type="text" class="form-control qestionInput" maxlength="100" placeholder="שאלה '+k+'" dir="rtl" />'+
                    "<select type='text' value='nan'  id='numOfAnswers' class='form-control clubHouseName' placeholder='בחר מועדונית מתוך הרשימה'>"+
                        "<option value='5_Options'>5</option>"+
                        "<option value='10_Options'>10</option>"+
                    "</select>"+
                '
';
            $("#listQue").append(list);
        }
        $("#body").append(finish_btn);

        /*
        var str='
'+sub+'

'+
                '

'+plus_btn+finsh_btn;
        Form.allQ(corForm.key);
        $("#body").html(str);
        $("#plus_btn").click(addQuestionPage);
        $("#finsh_btn").click(finish);*/
    }

//================================================================================================
    var addQtoTable=function(q,a,i)
    {
        var queStr='
'+
					  ''+i+'	'+
					  ''+q.question+'	'+
					  ''+a[i]+'/'+q.numOfvalues+'	'+
		    '
`';
        return queStr;
    }
    var keysArray=function(objects)
    {
        var keys=[];
        for(obj in objects )
            keys.push(obj);
        return keys;
    }
    var showForm = function(form,user)
    {
        $('.NAV').collapse('hide');
        $("#body").html("");
        var showExactForm =

        `<h2 id='allTitles'>`+form.subject+`

					  #
					  תוכן השאלה
					  ערך

`;
        var answers=keysArray(form.result);
        var ans;
        for(var i=0;i<answers.length;i++)//find the user answer
            if(form.result[answers[i]].user===user)
                ans=form.result[answers[i]].answers;
        var keys=keysArray(form.questions);
		for(i=0;i<keys.length;i++)
            showExactForm+=addQtoTable(form.questions[keys[i]],ans,i);
          firebase.database().ref('users/'+user).once("value")
        .then(function(data)
        {
            var thisUser=data.val();
            p(thisUser);
            showExactForm+=  `

מאת :`+thisUser.lastName+` `+thisUser.firstName+` `+`

ילד :`+thisUser.childName+` `+`

תאריך: 29/5/17

`;
            $("#body").html(showExactForm);
        });
    }
//================================================================================================
    var p=function(s)
    {
        console.log(s);
    }
//================================================================================================
    var fillFrom = function(key)
    {
    firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+key).once("value")
        .then(function(data)
        {
            var keys=[];
            for(obj in data.be().questions )
                keys.push(obj);
            numOfQuestions=keys.length;
            var form=data.val();
            var str;
            $("#body").html('');
            $("#body").css("text-align", "right");

            for(var i=0;i<keys.length;i++)
            {
            firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+key).once("value")
                .then(function(data)
                {
                    var keys=[];
                    for(obj in data.be().questions )
                        keys.push(obj);
                    var form=data.val();
                    var str;
                    $("#body").html('');
                    $("#body").html('
'+form.subject+'

');
                    $("#body").css("text-align", "right");

                    for(var i=0;i<keys.length;i++)
                    {
                        str='
'+
                                '
'+form.questions[keys[i]].question+'
'+
                                '<form id="form_'+i+'"class="choiceList">'+
                                '</form>'+
                            '
'

                        $("#body").append(str);
                        for(var j=0;j<form.questions[keys[i]].numOfvalues;j++)
                        {
                            str=''+(j+1)+'
'+
                                '<input type="radio" name="select" value="'+(j+1)+'"/>'+
                                '';
                            $("#form_"+i).append(str);
                        }
                    }
                });
            }
            var btn='שלח';
            $("body").append(btn);
        });
    }
    var loadAllForms=function()
    {
        $('.Nav').collapse('hide');
        firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms').once("value")
        .then(function(data)
        {
            var str="";
            var forms=data.val();
            var i=0;
            $("#body").html("");
            for(key in forms)
            {
                var tempId ="form_"+i;
                str=''+
                '
'+forms[key].subject+'';
                $("#body").append(str);
                mainPage.paintButton(i,tempId);
                $("#form_"+(i++)).click(function()
                {
                    fillFrom(key);
                });

            }

        });
    }

    var test=function(id)
    {
        fillFrom(id);
    }

    var updateClubs=function()				//update the list of clubHouses
	{
        firebase.database().ref("clubhouse/").once("value")
        .then(function(data)
        {
            var clubs=data.val();
            var keys=Object.keys(clubs);
            for(var i=0;i<keys.length;i++)
                $("#clubhousesForm").append('<option value='+clubs[keys[i]].ClubhouseDBkey+'>'+clubs[keys[i]].name+'</option>');

            $("#clubhousesForm").append('<option value="allClubs">כל המועדוניות</option>');
        });
	}

//================================================================================================
    var sendForm=function(questions)
    {
        var ans=[];
        for(var i=0;i<numOfQuestions;i++)
        {
            var maxSelect=questions[i].numOfvalues;
            for(var j=0;j<maxSelect;j++)
            {
                var check =document.getElementById("rbtn_"+i+"_"+j);
                if(check.checked)
                    ans.push(j+1
                    );
            }
        }
        if(ans.length<numOfQuestions)
        {
            alert("יש לענות על כל השאלות");
            return;
        }
        p(corForm);
        var result={user:login.correntUser[1],answers:ans};
        firebase.database().ref('clubhouse/'+login.correntClub[0]+'/forms/'+corForm+'/result').push(result);
    }

    var test=function()
    {
        firebase.database().ref('clubhouse/-Kn-BZInu3pPlaW11V7v/forms/-Kn6CV3USW-N-KeL1up2').once("value")
        .then(function(data)
        {
            var form=data.val();
            var user="DIgNUeLsWndpOtrvK49MUbS3rgg2";
            showForm(form,user);
        });
    }
    return {
        create:create,showForm:showForm,
        loadAllForms:loadAllForms,
        test:test
    }
}();<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

function TChannelStatsd(channel, statsd) {
    if (!(this instanceof TChannelStatsd)) {
        return new TChannelStatsd(channel, statsd);
    }

    var self = this;
    self.statsd = statsd;
    self.channel = channel;
    self.channel.on('stat', onStat);

    function onStat(stat) {
        self.onStat(stat);
    }
}

function clean(str, field) {
    if (!str) {
        return 'no-' + field;
    } else {
        // TODO generalize
        return str
            .replace(/:/g, '-')
            .replace(/\//g, '-')
            .replace(/\./g, '-')
            .replace(/{|}/g, '-');
    }
}

function getKey(common, stat) {
    var prefix = 'tchannel' + '.' + stat.name;
    switch (stat.name) {
        // outbound
        case 'outbound.calls.sent':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint');
        case 'outbound.calls.success':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint');
        case 'outbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'outbound.calls.operational-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'outbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'outbound.calls.retries':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                stat.tags['retry-count'];
        case 'outbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint');
        case 'outbound.calls.per-attempt-latency':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.peer) + '.' +
                stat.tags['retry-count'];

        // inbound
        case 'inbound.calls.recvd':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint');
        case 'inbound.calls.success':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint');
        case 'inbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'inbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'inbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint');

        // connection
        case 'connections.active':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.initiated':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.connect-errors':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.accepted':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.accept-errors':
            return prefix + '.' +
                clean(stat.tags['host-port'], 'host-port');

        case 'connections.errors':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port') + '.' +
                clean(stat.tags.type, 'type');

        case 'connections.closed':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port') + '.' +
                clean(stat.tags.reason, 'reason');

        // other types
        default:
            return 'tchannel.bad-stat-object';
    }
}

TChannelStatsd.prototype.onStat = function onStat(stat) {
    var self = this;
    var key = getKey(self.channel.statTags, stat);
    switch (stat.type) {
        case 'counter':
            return self.statsd.increment(key, stat.value);

        case 'gauge':
            return self.statsd.gauge(key, stat.value);

        case 'timing':
            return self.statsd.timing(key, stat.value);

        default:
            self.channel.logger.error('Trying to emit an invalid stat object', {
                statType: stat.type,
                statName: stat.name
            });
            break;
    }
};

module.exports = TChannelStatsd;
<commit_msg>statsd: do not add peer for per-attempt latency<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

function TChannelStatsd(channel, statsd) {
    if (!(this instanceof TChannelStatsd)) {
        return new TChannelStatsd(channel, statsd);
    }

    var self = this;
    self.statsd = statsd;
    self.channel = channel;
    self.channel.on('stat', onStat);

    function onStat(stat) {
        self.onStat(stat);
    }
}

function clean(str, field) {
    if (!str) {
        return 'no-' + field;
    } else {
        // TODO generalize
        return str
            .replace(/:/g, '-')
            .replace(/\//g, '-')
            .replace(/\./g, '-')
            .replace(/{|}/g, '-');
    }
}

function getKey(common, stat) {
    var prefix = 'tchannel' + '.' + stat.name;
    switch (stat.name) {
        // outbound
        case 'outbound.calls.sent':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint');
        case 'outbound.calls.success':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint');
        case 'outbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'outbound.calls.operational-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'outbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'outbound.calls.retries':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                stat.tags['retry-count'];
        case 'outbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint');
        case 'outbound.calls.per-attempt-latency':
            return prefix + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags['target-service'], 'target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'endpoint') + '.' +
                stat.tags['retry-count'];

        // inbound
        case 'inbound.calls.recvd':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint');
        case 'inbound.calls.success':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint');
        case 'inbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'inbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint') + '.' +
                clean(stat.tags.type, 'type');
        case 'inbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'calling-service') + '.' +
                clean(stat.tags.service, 'service') + '.' +
                clean(stat.tags.endpoint, 'endpoint');

        // connection
        case 'connections.active':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.initiated':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.connect-errors':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.accepted':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port');

        case 'connections.accept-errors':
            return prefix + '.' +
                clean(stat.tags['host-port'], 'host-port');

        case 'connections.errors':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port') + '.' +
                clean(stat.tags.type, 'type');

        case 'connections.closed':
            return prefix + '.' +
                clean(stat.tags['peer-host-port'], 'peer-host-port') + '.' +
                clean(stat.tags.reason, 'reason');

        // other types
        default:
            return 'tchannel.bad-stat-object';
    }
}

TChannelStatsd.prototype.onStat = function onStat(stat) {
    var self = this;
    var key = getKey(self.channel.statTags, stat);
    switch (stat.type) {
        case 'counter':
            return self.statsd.increment(key, stat.value);

        case 'gauge':
            return self.statsd.gauge(key, stat.value);

        case 'timing':
            return self.statsd.timing(key, stat.value);

        default:
            self.channel.logger.error('Trying to emit an invalid stat object', {
                statType: stat.type,
                statName: stat.name
            });
            break;
    }
};

module.exports = TChannelStatsd;
<|endoftext|>