<commit_before>var url = require('url');
var common = require('./common.js');

var Page = function (id, url) {
    this.id = id;
    this.url = url;
    this.entries = {};
    this.startTimestamp = undefined;
    this.domTime = undefined;
    this.endTime = undefined;
    this.originalRequestId = undefined;
    this.error = true;
}

Page.prototype.start = function () {
    this.startTimestamp = new Date();
}

Page.prototype.domLoaded = function () {
    this.domLoadedTime = new Date() - this.startTimestamp;
}

Page.prototype.end = function () {
    this.endTime = new Date() - this.startTimestamp;
}

Page.prototype.isOk = function () {
    return !this.error;
}

// typical sequence:
//
// Network.requestWillBeSent # about to send a request
// Network.responseReceived  # headers received
// Network.dataReceived      # data chunk received
// [...]
// Network.loadingFinished   # full response received
Page.prototype.processMessage = function (message) {
    var id = message.params.requestId;
    switch (message.method) {
        case 'Network.requestWillBeSent':
            if (!this.originalRequestId &&
                sameURL(this.url, message.params.request.url)) {
                this.originalRequestId = id;
            }
            this.entries[id] = {
                'requestEvent': message.params,
                'responseEvent': undefined,
                'responseLength': 0,
                'encodedResponseLength': 0,
                'responseFinished': undefined
            };
            break;
        case 'Network.dataReceived':
            if (id in this.entries) {
                this.entries[id].responseLength += message.params.dataLength;
                this.entries[id].encodedResponseLength += message.params.encodedDataLength;
                break;
            }
            return;
        case 'Network.responseReceived':
            if (id in this.entries) {
                this.entries[id].responseEvent = message.params;
                break;
            }
            return;
        case 'Network.loadingFinished':
            if (id == this.originalRequestId) {
                this.error = false;
            }
            if (id in this.entries) {
                this.entries[id].responseFinished = message.params.timestamp;
                break;
            }
            return;
        case 'Network.loadingFailed':
            if (id in this.entries) {
                break; // just log dump
            }
            return;
        default:
            common.dump('Unhandled message: ' + message.method);
            return;
    }
    common.dump('<-- ' + '[' + id + '] ' + message.method);
}

Page.prototype.getHAR = function () {
    var har = {
        'info': {
            'startedDateTime': this.startTimestamp.toISOString(),
            'id': this.id.toString(),
            'title': this.url,
            'pageTimings': {
                'onContentLoad': this.domLoadedTime,
                'onLoad': this.endTime
            }
        },
        'entries': []
    };

    for (var requestId in this.entries) {
        var entry = this.entries[requestId];

        // skip incomplete entries
        if (!entry.responseEvent || !entry.responseFinished) continue;

        // skip entries with no timing information (it's optional)
        var timing = entry.responseEvent.response.timing;
        if (!timing) continue;

        // analyze headers
        var requestHeaders = convertHeaders(entry.requestEvent.request.headers);
        var responseHeaders = convertHeaders(entry.responseEvent.response.headers);

        // add status line length
        requestHeaders.size += (entry.requestEvent.request.method.length +
                                entry.requestEvent.request.url.length +
                                12); // "HTTP/1.x" + "  " + "\r\n"

        responseHeaders.size += (entry.responseEvent.response.status.toString().length +
                                 entry.responseEvent.response.statusText.length +
                                 12); // "HTTP/1.x" + "  " + "\r\n"

        // query string
        var queryString = convertQueryString(entry.requestEvent.request.url);

        // compute timing informations: input
        var dnsTime = timeDelta(timing.dnsStart, timing.dnsEnd);
        var proxyTime = timeDelta(timing.proxyStart, timing.proxyEnd);
        var connectTime = timeDelta(timing.connectStart, timing.connectEnd);
        var sslTime = timeDelta(timing.sslStart, timing.sslEnd);
        var sendTime = timeDelta(timing.sendStart, timing.sendEnd);

        // compute timing informations: output
        var dns = proxyTime + dnsTime;
        var connect = connectTime;
        var ssl = sslTime;
        var send = sendTime;
        var wait = timing.receiveHeadersEnd - timing.sendEnd;
        var receive = Math.round(entry.responseFinished * 1000 -
                                 timing.requestTime * 1000 -
                                 timing.receiveHeadersEnd);
        var blocked = -1; // TODO
        var totalTime = dns + connect + ssl + send + wait + receive;

        // fill entry
        har.entries.push({
            'pageref': this.id.toString(),
            'startedDateTime': new Date(timing.requestTime * 1000).toISOString(),
            'time': totalTime,
            'request': {
                'method': entry.requestEvent.request.method,
                'url': entry.requestEvent.request.url,
                'httpVersion': 'HTTP/1.1', // TODO
                'cookies': [], // TODO
                'headers': requestHeaders.pairs,
                'queryString': queryString,
                'headersSize': requestHeaders.size,
                'bodySize': entry.requestEvent.request.headers['Content-Length'] || -1,
            },
            'response': {
                'status': entry.responseEvent.response.status,
                'statusText': entry.responseEvent.response.statusText,
                'httpVersion': 'HTTP/1.1', // TODO
                'cookies': [], // TODO
                'headers': responseHeaders.pairs,
                'redirectURL': '', // TODO
                'headersSize': responseHeaders.size,
                'bodySize': entry.encodedResponseLength,
                'content': {
                    'size': entry.responseLength,
                    'mimeType': entry.responseEvent.response.mimeType,
                    'compression': entry.responseLength - entry.encodedResponseLength
                }
            },
            'cache': {},
            'timings': {
                'blocked': blocked,
                'dns': timing.dnsStart == -1 ? -1 : dns, // -1 = n.a.
                'connect': timing.connectStart == -1 ? -1 : connect, // -1 = n.a.
                'send': send,
                'wait': wait,
                'receive': receive,
                'ssl': timing.sslStart == -1 ? -1 : ssl // -1 = n.a.
            }
        });
    }

    return har;
}

function convertQueryString(fullUrl) {
    var query = url.parse(fullUrl, true).query;
    var pairs = [];
    for (var name in query) {
        var value = query[name];
        pairs.push({'name': name, 'value': value.toString()});
    }
    return pairs;
}

function convertHeaders(headers) {
    headersObject = {'pairs': [], 'size': -1};
    if (Object.keys(headers).length) {
        headersObject.size = 2; // trailing "\r\n"
        for (var name in headers) {
            var value = headers[name];
            headersObject.pairs.push({'name': name, 'value': value});
            headersObject.size += name.length + value.length + 4; // ": " + "\r\n"
        }
    }
    return headersObject;
}

function timeDelta(start, end) {
    return start != -1 && end != -1 ? (end - start) : 0;
}

function sameURL(a, b) {
    return JSON.stringify(url.parse(a)) == JSON.stringify(url.parse(b));
}

module.exports = Page;
<commit_msg>Skip data URI scheme requests<commit_after>var url = require('url');
var common = require('./common.js');

var Page = function (id, url) {
    this.id = id;
    this.url = url;
    this.entries = {};
    this.startTimestamp = undefined;
    this.domTime = undefined;
    this.endTime = undefined;
    this.originalRequestId = undefined;
    this.error = true;
}

Page.prototype.start = function () {
    this.startTimestamp = new Date();
}

Page.prototype.domLoaded = function () {
    this.domLoadedTime = new Date() - this.startTimestamp;
}

Page.prototype.end = function () {
    this.endTime = new Date() - this.startTimestamp;
}

Page.prototype.isOk = function () {
    return !this.error;
}

// typical sequence:
//
// Network.requestWillBeSent # about to send a request
// Network.responseReceived  # headers received
// Network.dataReceived      # data chunk received
// [...]
// Network.loadingFinished   # full response received
Page.prototype.processMessage = function (message) {
    var id = message.params.requestId;
    switch (message.method) {
        case 'Network.requestWillBeSent':
            if (!this.originalRequestId &&
                sameURL(this.url, message.params.request.url)) {
                this.originalRequestId = id;
            }
            this.entries[id] = {
                'requestEvent': message.params,
                'responseEvent': undefined,
                'responseLength': 0,
                'encodedResponseLength': 0,
                'responseFinished': undefined
            };
            break;
        case 'Network.dataReceived':
            if (id in this.entries) {
                this.entries[id].responseLength += message.params.dataLength;
                this.entries[id].encodedResponseLength += message.params.encodedDataLength;
                break;
            }
            return;
        case 'Network.responseReceived':
            if (id in this.entries) {
                this.entries[id].responseEvent = message.params;
                break;
            }
            return;
        case 'Network.loadingFinished':
            if (id == this.originalRequestId) {
                this.error = false;
            }
            if (id in this.entries) {
                this.entries[id].responseFinished = message.params.timestamp;
                break;
            }
            return;
        case 'Network.loadingFailed':
            if (id in this.entries) {
                break; // just log dump
            }
            return;
        default:
            common.dump('Unhandled message: ' + message.method);
            return;
    }
    common.dump('<-- ' + '[' + id + '] ' + message.method);
}

Page.prototype.getHAR = function () {
    var har = {
        'info': {
            'startedDateTime': this.startTimestamp.toISOString(),
            'id': this.id.toString(),
            'title': this.url,
            'pageTimings': {
                'onContentLoad': this.domLoadedTime,
                'onLoad': this.endTime
            }
        },
        'entries': []
    };

    for (var requestId in this.entries) {
        var entry = this.entries[requestId];

        // skip incomplete entries
        if (!entry.responseEvent || !entry.responseFinished) continue;

        // skip entries with no timing information (it's optional)
        var timing = entry.responseEvent.response.timing;
        if (!timing) continue;

        // skip data URI scheme requests
        if (entry.requestEvent.request.url.substr(0, 5) == 'data:') continue;

        // analyze headers
        var requestHeaders = convertHeaders(entry.requestEvent.request.headers);
        var responseHeaders = convertHeaders(entry.responseEvent.response.headers);

        // add status line length
        requestHeaders.size += (entry.requestEvent.request.method.length +
                                entry.requestEvent.request.url.length +
                                12); // "HTTP/1.x" + "  " + "\r\n"

        responseHeaders.size += (entry.responseEvent.response.status.toString().length +
                                 entry.responseEvent.response.statusText.length +
                                 12); // "HTTP/1.x" + "  " + "\r\n"

        // query string
        var queryString = convertQueryString(entry.requestEvent.request.url);

        // compute timing informations: input
        var dnsTime = timeDelta(timing.dnsStart, timing.dnsEnd);
        var proxyTime = timeDelta(timing.proxyStart, timing.proxyEnd);
        var connectTime = timeDelta(timing.connectStart, timing.connectEnd);
        var sslTime = timeDelta(timing.sslStart, timing.sslEnd);
        var sendTime = timeDelta(timing.sendStart, timing.sendEnd);

        // compute timing informations: output
        var dns = proxyTime + dnsTime;
        var connect = connectTime;
        var ssl = sslTime;
        var send = sendTime;
        var wait = timing.receiveHeadersEnd - timing.sendEnd;
        var receive = Math.round(entry.responseFinished * 1000 -
                                 timing.requestTime * 1000 -
                                 timing.receiveHeadersEnd);
        var blocked = -1; // TODO
        var totalTime = dns + connect + ssl + send + wait + receive;

        // fill entry
        har.entries.push({
            'pageref': this.id.toString(),
            'startedDateTime': new Date(timing.requestTime * 1000).toISOString(),
            'time': totalTime,
            'request': {
                'method': entry.requestEvent.request.method,
                'url': entry.requestEvent.request.url,
                'httpVersion': 'HTTP/1.1', // TODO
                'cookies': [], // TODO
                'headers': requestHeaders.pairs,
                'queryString': queryString,
                'headersSize': requestHeaders.size,
                'bodySize': entry.requestEvent.request.headers['Content-Length'] || -1,
            },
            'response': {
                'status': entry.responseEvent.response.status,
                'statusText': entry.responseEvent.response.statusText,
                'httpVersion': 'HTTP/1.1', // TODO
                'cookies': [], // TODO
                'headers': responseHeaders.pairs,
                'redirectURL': '', // TODO
                'headersSize': responseHeaders.size,
                'bodySize': entry.encodedResponseLength,
                'content': {
                    'size': entry.responseLength,
                    'mimeType': entry.responseEvent.response.mimeType,
                    'compression': entry.responseLength - entry.encodedResponseLength
                }
            },
            'cache': {},
            'timings': {
                'blocked': blocked,
                'dns': timing.dnsStart == -1 ? -1 : dns, // -1 = n.a.
                'connect': timing.connectStart == -1 ? -1 : connect, // -1 = n.a.
                'send': send,
                'wait': wait,
                'receive': receive,
                'ssl': timing.sslStart == -1 ? -1 : ssl // -1 = n.a.
            }
        });
    }

    return har;
}

function convertQueryString(fullUrl) {
    var query = url.parse(fullUrl, true).query;
    var pairs = [];
    for (var name in query) {
        var value = query[name];
        pairs.push({'name': name, 'value': value.toString()});
    }
    return pairs;
}

function convertHeaders(headers) {
    headersObject = {'pairs': [], 'size': -1};
    if (Object.keys(headers).length) {
        headersObject.size = 2; // trailing "\r\n"
        for (var name in headers) {
            var value = headers[name];
            headersObject.pairs.push({'name': name, 'value': value});
            headersObject.size += name.length + value.length + 4; // ": " + "\r\n"
        }
    }
    return headersObject;
}

function timeDelta(start, end) {
    return start != -1 && end != -1 ? (end - start) : 0;
}

function sameURL(a, b) {
    return JSON.stringify(url.parse(a)) == JSON.stringify(url.parse(b));
}

module.exports = Page;
<|endoftext|>


<commit_before>
var Mysql        = require('../');
var Connection   = require('./Connection');

module.exports = Pool;
function Pool(options) {
  this.config = options.config;
  this.config.connectionConfig.pool = this;

  this._allConnections   = [];
  this._freeConnections  = [];
  this._connectionQueue  = [];
  this._closed           = false;
}

Pool.prototype.getConnection = function(cb) {
  if (this._closed) {
    cb(new Error('Pool is closed.'));
    return;
  }

  if (this._freeConnections.length > 0) {
    var connection = this._freeConnections[0];
    this._freeConnections.shift();
    cb(null, connection);
  } else if (this.config.connectionLimit == 0 || this._allConnections.length < this.config.connectionLimit) {
    var connection = this._createConnection();
    this._allConnections.push(connection);
    connection.connect(function(err) {
      if (self._closed) {
        cb(new Error('Pool is closed.'));
      }
      else if (err) {
        cb(err);
      } else {
        cb(null, connection);
      }
    });
  } else if (this.config.waitForConnections) {
    this._connectionQueue.push(cb);
  } else {
    cb(new Error('No connections available.'));
  }
};

Pool.prototype.releaseConnection = function(connection) {
  if (this._connectionQueue.length) {
    var cb = this._connectionQueue[0];
    this._connectionQueue.shift();
    process.nextTick(cb.bind(null, null, connection));
  } else {
    this._freeConnections.push(connection);
  }
};

Pool.prototype.end = function(cb) {
  this._closed = true;
  var self              = this;
  var closedConnections = 0;
  var calledBack        = false;
  var endCB = function(err) {
    if (calledBack) {
      return;
    } else if (err) {
      calledBack = true;
      delete endCB;
      cb(err);
    } else if (++closedConnections == self._allConnections.length) {
      calledBack = true;
      delete endCB;
      cb();
    }
  };
  for (var i = 0; i < this._allConnections.length; ++i) {
    var connection = this._allConnections[i];
    connection.destroy = connection._realDestroy;
    connection.end     = connection._realEnd;
    connection.end(endCB);
  }
};

Pool.prototype._createConnection = function() {
  var self = this;
  var connection = (this.config.createConnection)
    ? this.config.createConnection(this.config.connectionConfig)
    : Mysql.createConnection(this.config.connectionConfig);

  connection._realEnd = connection.end;
  connection.end      = function(cb) {
    self.releaseConnection(connection);
    if (cb) cb();
  };
  connection._realDestroy = connection.destroy;
  connection.destroy      = function() {
    for (var i = 0; i < self._allConnections.length; ++i) {
      if (self._allConnections[i] === this) {
        self._allConnections.splice(i, 1);
        break;
      }
    }
    this.end     = this._realEnd;
    this.destroy = this._realDestroy;
    this.destroy();
  };
  return connection;
};
<commit_msg>Added missing self reference<commit_after>
var Mysql        = require('../');
var Connection   = require('./Connection');

module.exports = Pool;
function Pool(options) {
  this.config = options.config;
  this.config.connectionConfig.pool = this;

  this._allConnections   = [];
  this._freeConnections  = [];
  this._connectionQueue  = [];
  this._closed           = false;
}

Pool.prototype.getConnection = function(cb) {
  if (this._closed) {
    cb(new Error('Pool is closed.'));
    return;
  }

  if (this._freeConnections.length > 0) {
    var connection = this._freeConnections[0];
    this._freeConnections.shift();
    cb(null, connection);
  } else if (this.config.connectionLimit == 0 || this._allConnections.length < this.config.connectionLimit) {
    var self       = this;
    var connection = this._createConnection();
    this._allConnections.push(connection);
    connection.connect(function(err) {
      if (self._closed) {
        cb(new Error('Pool is closed.'));
      }
      else if (err) {
        cb(err);
      } else {
        cb(null, connection);
      }
    });
  } else if (this.config.waitForConnections) {
    this._connectionQueue.push(cb);
  } else {
    cb(new Error('No connections available.'));
  }
};

Pool.prototype.releaseConnection = function(connection) {
  if (this._connectionQueue.length) {
    var cb = this._connectionQueue[0];
    this._connectionQueue.shift();
    process.nextTick(cb.bind(null, null, connection));
  } else {
    this._freeConnections.push(connection);
  }
};

Pool.prototype.end = function(cb) {
  this._closed = true;
  var self              = this;
  var closedConnections = 0;
  var calledBack        = false;
  var endCB = function(err) {
    if (calledBack) {
      return;
    } else if (err) {
      calledBack = true;
      delete endCB;
      cb(err);
    } else if (++closedConnections == self._allConnections.length) {
      calledBack = true;
      delete endCB;
      cb();
    }
  };
  for (var i = 0; i < this._allConnections.length; ++i) {
    var connection = this._allConnections[i];
    connection.destroy = connection._realDestroy;
    connection.end     = connection._realEnd;
    connection.end(endCB);
  }
};

Pool.prototype._createConnection = function() {
  var self = this;
  var connection = (this.config.createConnection)
    ? this.config.createConnection(this.config.connectionConfig)
    : Mysql.createConnection(this.config.connectionConfig);

  connection._realEnd = connection.end;
  connection.end      = function(cb) {
    self.releaseConnection(connection);
    if (cb) cb();
  };
  connection._realDestroy = connection.destroy;
  connection.destroy      = function() {
    for (var i = 0; i < self._allConnections.length; ++i) {
      if (self._allConnections[i] === this) {
        self._allConnections.splice(i, 1);
        break;
      }
    }
    this.end     = this._realEnd;
    this.destroy = this._realDestroy;
    this.destroy();
  };
  return connection;
};
<|endoftext|>


<commit_before>'use strict';
var Q = require('q');
var debug = require('debug')('dynamoose:scan');

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

function Scan (Model, filter, options) {

  this.Model = Model;
  this.options = options || {'all': {'delay': 0, 'max': 1}};

  // [{
  //     name: 'name',
  //     values: ['value', ...],
  //     comparison: 'string'
  //   },
  //    ...
  // ]
  this.filters = {};
  this.buildState = false;
  this.validationError = null;

  if (typeof filter === 'string') {
    this.buildState = filter;
    this.filters[filter] = {name: filter};
  } else if (typeof filter === 'object'){
    this.parseFilterObject(filter);
  }
}

Scan.prototype.exec = function (next) {
  debug('exec scan for ', this.scan);
  if (this.validationError) {
    if (next) {
      next(this.validationError);
    }
    return Q.reject(this.validationError);
  }

  var Model = this.Model;
  var Model$ = Model.$__;
  var schema = Model$.schema;
  var options = this.options;

  var scanReq = {
    TableName: Model.$__.name
  };

  function scanUseRawAwsFilter () {
    var deferred = Q.defer();
    var AWS = Model$.AWS;
    var docClient = new AWS.DynamoDB.DocumentClient();
    docClient.scan(filter, function scanRawCallback(err, data) {
      if (err) {
        return deferred.reject(err);
      }
      return deferred.resolve(data);
    })
  }

  if (options.useRawAwsFilter) {
    return scanUseRawAwsFilter();
  }

  if(Object.keys(this.filters).length > 0) {
    scanReq.ScanFilter = {};
    for(var name in this.filters) {
      var filter = this.filters[name];
      var filterAttr = schema.attributes[name];
      scanReq.ScanFilter[name] = {
        AttributeValueList: [],
        ComparisonOperator: filter.comparison
      };

      if(filter.values) {
        for (var i = 0; i < filter.values.length; i++) {
          var val = filter.values[i];
          scanReq.ScanFilter[name].AttributeValueList.push(
            filterAttr.toDynamo(val, true)
          );
        }
      }
    }
  }

  if(options.attributes) {
    scanReq.AttributesToGet = options.attributes;
  }

  if(options.count) {
    scanReq.Select = 'COUNT';
  }

  if(options.counts) {
    scanReq.Select = 'COUNT';
  }

  if(options.limit) {
    scanReq.Limit = options.limit;
  }

  if(options.ExclusiveStartKey) {
    scanReq.ExclusiveStartKey = options.ExclusiveStartKey;
  }

  if(options.conditionalOperator) {
    scanReq.ConditionalOperator = options.conditionalOperator;
  }

  if(options.consistent) {
    scanReq.ConsistentRead = true;
  }

  function scan () {
    var deferred = Q.defer();

    var models = {}, totalCount = 0, scannedCount = 0, timesScanned = 0, lastKey;
    if (!options.all) {
      options.all = {'delay': 0, 'max': 1};
    }
    scanOne();
    function scanOne() {
      debug('scan request', scanReq);
      Model.$__.base.ddb().scan(scanReq, function(err, data) {
        if(err) {
          debug('Error returned by scan', err);
          return deferred.reject(err);
        }
        debug('scan response', data);

        if(!Object.keys(data).length) {
          return deferred.resolve();
        }

        function toModel (item) {
          var model = new Model();
          model.$__.isNew = false;
          schema.parseDynamo(model, item);

          debug('scan parsed model', model);

          return model;
        }

        try {
          if (options.count) {
            return deferred.resolve(data.Count);
          }
          if (options.counts) {
            var counts = { count: data.Count, scannedCount: data.ScannedCount };
            return deferred.resolve(counts);
          }
          if (data.Items !== undefined) {
            if (!models.length || models.length === 0) {
              models = data.Items.map(toModel);
            } else {
              models = models.concat(data.Items.map(toModel));
            }

            if(options.one) {
              if (!models || models.length === 0) {
                return deferred.resolve();
              }
              return deferred.resolve(models[0]);
            }
            lastKey = data.LastEvaluatedKey;
          }
          totalCount += data.Count;
          scannedCount += data.ScannedCount;
          timesScanned++;

          if ((options.all.max === 0 || timesScanned < options.all.max) && lastKey) {
            // scan.all need to scan again
            scanReq.ExclusiveStartKey = lastKey;
            setTimeout(scanOne, options.all.delay * 1000);
          }
          else {
            // completed scan returning models
            models.lastKey = lastKey;
            models.count = totalCount;
            models.scannedCount = scannedCount;
            models.timesScanned = timesScanned;
            deferred.resolve(models);
          }
        } catch (err) {
          deferred.reject(err);
        }
      });
    }

    return deferred.promise.nodeify(next);
  }

  if(Model$.options.waitForActive) {
    return Model$.table.waitForActive().then(scan);
  }

  return scan();
};

Scan.prototype.parseFilterObject = function (filter) {

  if (Object.keys(filter).length > 0) {

    for(var filterName in filter) {
      if (filter.hasOwnProperty(filterName)) {

        // Parse AND OR
        if (filterName === 'and' || filterName === 'or') {

          this[filterName]();
          for(var condition in filter[filterName]) {
            if (filter[filterName].hasOwnProperty(condition)) {
              this.parseFilterObject(filter[filterName][condition]);
            }
          }
        } else {

          this.where(filterName);
          var val, comp;

          if (typeof filter[filterName] === 'object' &&
            Object.keys(filter[filterName]).length  === 1) {

            comp = Object.keys(filter[filterName])[0];

            if (comp === 'null') {
              if (!filter[filterName][comp]) {
                comp = 'not_null';
              }
              val = [null];
            } else if (comp === 'in' || comp === 'between') {
              val = filter[filterName][comp];
            } else {
              val = [filter[filterName][comp]];
            }

          } else {
            comp = 'eq';
            val = [filter[filterName]];
          }
          this.compVal(val, comp.toUpperCase());
        }

      }
    }
  }
};

Scan.prototype.and = function() {
  this.options.conditionalOperator = 'AND';
  return this;
};

Scan.prototype.or = function() {
  this.options.conditionalOperator = 'OR';
  return this;
};

Scan.prototype.consistent = function() {
  this.options.consistent = true;
  return this;
};

Scan.prototype.where = function (filter) {
  if (this.validationError) {
    return this;
  }

  if(this.buildState) {
    this.validationError = new errors.ScanError('Invalid scan state; where() must follow comparison');
    return this;
  }
  if(typeof filter === 'string') {
    this.buildState = filter;
    if(this.filters[filter]) {
      this.validationError = new errors.ScanError('Invalid scan state; %s can only be used once', filter);
      return this;
    }
    this.filters[filter] = {name: filter};
  }

  return this;
};
Scan.prototype.filter = Scan.prototype.where;

Scan.prototype.compVal = function (vals, comp) {
  if (this.validationError) {
    return this;
  }

  var permittedComparison =
    [
      'NOT_NULL','NULL','EQ','NE','GE','LT','GT','LE','GE',
      'NOT_CONTAINS','CONTAINS','BEGINS_WITH','IN','BETWEEN'
    ];

  if(!this.buildState) {
    this.validationError = new errors.ScanError('Invalid scan state; %s must follow scan(), where(), or filter()', comp);
    return this;
  }

  if (permittedComparison.indexOf(comp) === -1) {
    this.validationError = new errors.ScanError('Invalid comparison %s', comp);
    return this;
  }

  this.filters[this.buildState].values = vals;
  this.filters[this.buildState].comparison = comp;

  this.buildState = false;
  this.notState = false;

  return this;
};

Scan.prototype.not = function() {
  this.notState = true;
  return this;
};

Scan.prototype.null = function() {
  if(this.notState) {
    return this.compVal(null, 'NOT_NULL');
  } else {
    return this.compVal(null, 'NULL');
  }
};

Scan.prototype.eq = function (val) {
  if(this.notState) {
    return this.compVal([val], 'NE');
  } else {
    return this.compVal([val], 'EQ');
  }
};

Scan.prototype.lt = function (val) {
  if(this.notState) {
    return this.compVal([val], 'GE');
  } else {
    return this.compVal([val], 'LT');
  }
};

Scan.prototype.le = function (val) {
  if(this.notState) {
    return this.compVal([val], 'GT');
  } else {
    return this.compVal([val], 'LE');
  }
};

Scan.prototype.ge = function (val) {
  if(this.notState) {
    return this.compVal([val], 'LT');
  } else {
    return this.compVal([val], 'GE');
  }
};

Scan.prototype.gt = function (val) {
  if(this.notState) {
    return this.compVal([val], 'LE');
  } else {
    return this.compVal([val], 'GT');
  }
};

Scan.prototype.contains = function (val) {
  if(this.notState) {
    return this.compVal([val], 'NOT_CONTAINS');
  } else {
    return this.compVal([val], 'CONTAINS');
  }
};

Scan.prototype.beginsWith = function (val) {
  if (this.validationError) {
    return this;
  }
  if(this.notState) {
    this.validationError = new errors.ScanError('Invalid scan state: beginsWith() cannot follow not()');
    return this;
  }
  return this.compVal([val], 'BEGINS_WITH');
};

Scan.prototype.in = function (vals) {
  if (this.validationError) {
    return this;
  }
  if(this.notState) {
    this.validationError = new errors.ScanError('Invalid scan state: in() cannot follow not()');
    return this;
  }

  return this.compVal(vals, 'IN');
};

Scan.prototype.between = function (a, b) {
  if (this.validationError) {
    return this;
  }
  if(this.notState) {
    this.validationError = new errors.ScanError('Invalid scan state: between() cannot follow not()');
    return this;
  }
  return this.compVal([a, b], 'BETWEEN');
};

Scan.prototype.limit = function (limit) {
  this.options.limit = limit;
  return this;
};

Scan.prototype.startAt = function (key) {
  this.options.ExclusiveStartKey = key;
  return this;
};

Scan.prototype.attributes = function (attributes) {
  this.options.attributes = attributes;
  return this;
};

Scan.prototype.count = function () {
  this.options.count = true;
  this.options.select = 'COUNT';
  return this;
};

Scan.prototype.counts = function () {
  this.options.counts = true;
  this.options.select = 'COUNT';
  return this;
};

Scan.prototype.all = function (delay, max) {
  delay = delay || 1;
  max = max || 0;
  this.options.all = {'delay': delay, 'max': max};
  return this;
};

module.exports = Scan;
<commit_msg>Add support to useRawAwsFilter option<commit_after>'use strict';
var Q = require('q');
var debug = require('debug')('dynamoose:scan');

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

function Scan (Model, filter, options) {

  this.Model = Model;
  this.options = options || {'all': {'delay': 0, 'max': 1}};

  // [{
  //     name: 'name',
  //     values: ['value', ...],
  //     comparison: 'string'
  //   },
  //    ...
  // ]
  this.filters = {};
  this.buildState = false;
  this.validationError = null;

  if (typeof filter === 'string') {
    this.buildState = filter;
    this.filters[filter] = {name: filter};
  } else if (typeof filter === 'object'){
    if (!options.useRawAwsFilter) {
      this.parseFilterObject(filter);
    } else {
      // if use raw, just assign the filter
      this.filters = filter;
    }
  }
}

Scan.prototype.exec = function (next) {
  debug('exec scan for ', this.scan);
  if (this.validationError) {
    if (next) {
      next(this.validationError);
    }
    return Q.reject(this.validationError);
  }

  var Model = this.Model;
  var Model$ = Model.$__;
  var schema = Model$.schema;
  var options = this.options;

  var scanReq = { };
  var dbClient = null;

  if (options.useRawAwsFilter) {
    // use the raw aws filter, which needs to be composed by the developer
    // TODO validation of the custom raw filter
    scanReq = this.filters;

    // use the document client in aws-sdk
    var AWS = require('dynamoose').AWS;
    dbClient = new AWS.DynamoDB.DocumentClient();
  } else {
    // default
    scanReq = {
      TableName: Model.$__.name
    };

    if(Object.keys(this.filters).length > 0) {
      scanReq.ScanFilter = {};
      for(var name in this.filters) {
        var filter = this.filters[name];
        var filterAttr = schema.attributes[name];
        scanReq.ScanFilter[name] = {
          AttributeValueList: [],
          ComparisonOperator: filter.comparison
        };

        if(filter.values) {
          for (var i = 0; i < filter.values.length; i++) {
            var val = filter.values[i];
            scanReq.ScanFilter[name].AttributeValueList.push(
              filterAttr.toDynamo(val, true)
            );
          }
        }
      }
    }

    if(options.attributes) {
      scanReq.AttributesToGet = options.attributes;
    }

    if(options.count) {
      scanReq.Select = 'COUNT';
    }

    if(options.counts) {
      scanReq.Select = 'COUNT';
    }

    if(options.limit) {
      scanReq.Limit = options.limit;
    }

    if(options.ExclusiveStartKey) {
      scanReq.ExclusiveStartKey = options.ExclusiveStartKey;
    }

    if(options.conditionalOperator) {
      scanReq.ConditionalOperator = options.conditionalOperator;
    }

    if(options.consistent) {
      scanReq.ConsistentRead = true;
    }

    // use the default dynamodb client
    dbClient = Model.$__.base.ddb();
  }

  function scan () {
    var deferred = Q.defer();

    var models = {}, totalCount = 0, scannedCount = 0, timesScanned = 0, lastKey;
    if (!options.all) {
      options.all = {'delay': 0, 'max': 1};
    }
    scanOne();
    function scanOne() {
      debug('scan request', scanReq);

      // Model.$__.base.ddb()
      // use previous defined db client
      dbClient
      .scan(scanReq, function(err, data) {
        if(err) {
          debug('Error returned by scan', err);
          return deferred.reject(err);
        }
        debug('scan response', data);

        if(!Object.keys(data).length) {
          return deferred.resolve();
        }

        function toModel (item) {
          var model = new Model();
          model.$__.isNew = false;
          schema.parseDynamo(model, item);

          debug('scan parsed model', model);

          return model;
        }

        try {
          if (options.count) {
            return deferred.resolve(data.Count);
          }
          if (options.counts) {
            var counts = { count: data.Count, scannedCount: data.ScannedCount };
            return deferred.resolve(counts);
          }
          if (data.Items !== undefined) {
            if (!models.length || models.length === 0) {
              models = data.Items.map(toModel);
            } else {
              models = models.concat(data.Items.map(toModel));
            }

            if(options.one) {
              if (!models || models.length === 0) {
                return deferred.resolve();
              }
              return deferred.resolve(models[0]);
            }
            lastKey = data.LastEvaluatedKey;
          }
          totalCount += data.Count;
          scannedCount += data.ScannedCount;
          timesScanned++;

          if ((options.all.max === 0 || timesScanned < options.all.max) && lastKey) {
            // scan.all need to scan again
            scanReq.ExclusiveStartKey = lastKey;
            setTimeout(scanOne, options.all.delay * 1000);
          }
          else {
            // completed scan returning models
            models.lastKey = lastKey;
            models.count = totalCount;
            models.scannedCount = scannedCount;
            models.timesScanned = timesScanned;
            deferred.resolve(models);
          }
        } catch (err) {
          deferred.reject(err);
        }
      });
    }

    return deferred.promise.nodeify(next);
  }

  if(Model$.options.waitForActive) {
    return Model$.table.waitForActive().then(scan);
  }

  return scan();
};

Scan.prototype.parseFilterObject = function (filter) {

  if (Object.keys(filter).length > 0) {

    for(var filterName in filter) {
      if (filter.hasOwnProperty(filterName)) {

        // Parse AND OR
        if (filterName === 'and' || filterName === 'or') {

          this[filterName]();
          for(var condition in filter[filterName]) {
            if (filter[filterName].hasOwnProperty(condition)) {
              this.parseFilterObject(filter[filterName][condition]);
            }
          }
        } else {

          this.where(filterName);
          var val, comp;

          if (typeof filter[filterName] === 'object' &&
            Object.keys(filter[filterName]).length  === 1) {

            comp = Object.keys(filter[filterName])[0];

            if (comp === 'null') {
              if (!filter[filterName][comp]) {
                comp = 'not_null';
              }
              val = [null];
            } else if (comp === 'in' || comp === 'between') {
              val = filter[filterName][comp];
            } else {
              val = [filter[filterName][comp]];
            }

          } else {
            comp = 'eq';
            val = [filter[filterName]];
          }
          this.compVal(val, comp.toUpperCase());
        }

      }
    }
  }
};

Scan.prototype.and = function() {
  this.options.conditionalOperator = 'AND';
  return this;
};

Scan.prototype.or = function() {
  this.options.conditionalOperator = 'OR';
  return this;
};

Scan.prototype.consistent = function() {
  this.options.consistent = true;
  return this;
};

Scan.prototype.where = function (filter) {
  if (this.validationError) {
    return this;
  }

  if(this.buildState) {
    this.validationError = new errors.ScanError('Invalid scan state; where() must follow comparison');
    return this;
  }
  if(typeof filter === 'string') {
    this.buildState = filter;
    if(this.filters[filter]) {
      this.validationError = new errors.ScanError('Invalid scan state; %s can only be used once', filter);
      return this;
    }
    this.filters[filter] = {name: filter};
  }

  return this;
};
Scan.prototype.filter = Scan.prototype.where;

Scan.prototype.compVal = function (vals, comp) {
  if (this.validationError) {
    return this;
  }

  var permittedComparison =
    [
      'NOT_NULL','NULL','EQ','NE','GE','LT','GT','LE','GE',
      'NOT_CONTAINS','CONTAINS','BEGINS_WITH','IN','BETWEEN'
    ];

  if(!this.buildState) {
    this.validationError = new errors.ScanError('Invalid scan state; %s must follow scan(), where(), or filter()', comp);
    return this;
  }

  if (permittedComparison.indexOf(comp) === -1) {
    this.validationError = new errors.ScanError('Invalid comparison %s', comp);
    return this;
  }

  this.filters[this.buildState].values = vals;
  this.filters[this.buildState].comparison = comp;

  this.buildState = false;
  this.notState = false;

  return this;
};

Scan.prototype.not = function() {
  this.notState = true;
  return this;
};

Scan.prototype.null = function() {
  if(this.notState) {
    return this.compVal(null, 'NOT_NULL');
  } else {
    return this.compVal(null, 'NULL');
  }
};

Scan.prototype.eq = function (val) {
  if(this.notState) {
    return this.compVal([val], 'NE');
  } else {
    return this.compVal([val], 'EQ');
  }
};

Scan.prototype.lt = function (val) {
  if(this.notState) {
    return this.compVal([val], 'GE');
  } else {
    return this.compVal([val], 'LT');
  }
};

Scan.prototype.le = function (val) {
  if(this.notState) {
    return this.compVal([val], 'GT');
  } else {
    return this.compVal([val], 'LE');
  }
};

Scan.prototype.ge = function (val) {
  if(this.notState) {
    return this.compVal([val], 'LT');
  } else {
    return this.compVal([val], 'GE');
  }
};

Scan.prototype.gt = function (val) {
  if(this.notState) {
    return this.compVal([val], 'LE');
  } else {
    return this.compVal([val], 'GT');
  }
};

Scan.prototype.contains = function (val) {
  if(this.notState) {
    return this.compVal([val], 'NOT_CONTAINS');
  } else {
    return this.compVal([val], 'CONTAINS');
  }
};

Scan.prototype.beginsWith = function (val) {
  if (this.validationError) {
    return this;
  }
  if(this.notState) {
    this.validationError = new errors.ScanError('Invalid scan state: beginsWith() cannot follow not()');
    return this;
  }
  return this.compVal([val], 'BEGINS_WITH');
};

Scan.prototype.in = function (vals) {
  if (this.validationError) {
    return this;
  }
  if(this.notState) {
    this.validationError = new errors.ScanError('Invalid scan state: in() cannot follow not()');
    return this;
  }

  return this.compVal(vals, 'IN');
};

Scan.prototype.between = function (a, b) {
  if (this.validationError) {
    return this;
  }
  if(this.notState) {
    this.validationError = new errors.ScanError('Invalid scan state: between() cannot follow not()');
    return this;
  }
  return this.compVal([a, b], 'BETWEEN');
};

Scan.prototype.limit = function (limit) {
  this.options.limit = limit;
  return this;
};

Scan.prototype.startAt = function (key) {
  this.options.ExclusiveStartKey = key;
  return this;
};

Scan.prototype.attributes = function (attributes) {
  this.options.attributes = attributes;
  return this;
};

Scan.prototype.count = function () {
  this.options.count = true;
  this.options.select = 'COUNT';
  return this;
};

Scan.prototype.counts = function () {
  this.options.counts = true;
  this.options.select = 'COUNT';
  return this;
};

Scan.prototype.all = function (delay, max) {
  delay = delay || 1;
  max = max || 0;
  this.options.all = {'delay': delay, 'max': max};
  return this;
};

module.exports = Scan;
<|endoftext|>


<commit_before>
"use strict";

var EventEmitter = require('events').EventEmitter;
var util = require( './util' );

/**
 * Single user on the server.
 */
var User = function(data, client) {
    this.client = client;
    this._applyProperties(data);
};

User.prototype = Object.create(EventEmitter.prototype);

/**
 * @summary Moves the user to a channel
 *
 * @param {Channel|String} channel - Channel name or a channel object
 */
User.prototype.moveToChannel = function(channel) {
    var id;
    if(typeof channel === "string") {
        id = this.client.channelByName(channel).id;
    }
    else if(typeof channel === "object") {
        id = channel.id;
    }
    else {
        return;
    }
    this.client.connection.sendMessage( 'UserState', { session: this.session, actor: this.client.user.session, channel_id: id });
};

/**
 * @summary Attempts to kick the user.
 *
 * @param {String} [reason] - The reason to kick the user for.
 */
User.prototype.kick = function(reason) {
    this._sendRemoveUser( reason || "You have been kicked", false );
};

/**
 * @summary Attempts to ban the user.
 *
 * @param {String} [reason] - The reason to ban the user for.
 */
User.prototype.ban = function(reason) {
    this._sendRemoveUser( reason || "You have been banned", true );
};

/**
 * @summary Sends a message to the user.
 *
 * @param {String} message - The message to send.
 */
User.prototype.sendMessage = function(message) {
    this.client.sendMessage( message, { session: [ this.session ] } );
};

/**
 * @summary Returns an output stream for listening to the user audio.
 *
 * @param {Boolean} [noEmptyFrames]
 *      True to cut the output stream during silence. If the output stream
 *      isn't cut it will keep emitting zero-values when the user isn't
 *      talking.
 *
 * @returns {MumbleOutputStream} Output stream.
 */
User.prototype.outputStream = function(noEmptyFrames) {
    return this.client.connection.outputStream(this.session, noEmptyFrames);
};

/**
 * @summary Returns an input stream for sending audio to the user.
 *
 * @returns {MumbleInputStream} Input stream.
 */
User.prototype.inputStream = function() {
    return this.client.inputStreamForUser( this.session );
};

/**
 * @summary Checks whether the user can talk or not.
 *
 * @returns {Boolean} True if the user can talk.
 */
User.prototype.canTalk = function() {
    return !this.mute && !this.selfMute && !this.suppress;
};

/**
 * @summary Checks whether the user can hear other people.
 *
 * @returns {Boolean} True if the user can hear.
 */
User.prototype.canHear = function() {
    return !this.selfDeaf;
};

User.prototype._applyProperties = function(data) {

    /**
     * @summary Session ID
     *
     * @description
     * Session ID is present for all users. The ID specifies the current user
     * session and will change when the user reconnects.
     *
     * @see User#id
     *
     * @name User#session
     * @type Number
     */
    this.session = data.session;

    /**
     * @summary User name
     *
     * @name User#name
     * @type String
     */
    this.name = data.name;

    /**
     * @summary User ID
     *
     * @description
     * User ID is specified only for users who are registered on the server.
     * The user ID won't change when the user reconnects.
     *
     * @see User#session
     *
     * @name User#id
     * @type Number
     */
    this.id = data.user_id;

    /**
     * @summary _true_ when the user is muted by an admin.
     *
     * @name User#mute
     * @type Boolean
     */
    this.mute = data.mute;

    /**
     * @summary _true_ when the user is deafened by an admin.
     *
     * @name User#deaf
     * @type Boolean
     */
    this.deaf = data.deaf;

    /**
     * @summary _true_ when the user is suppressed due to lack of
     * permissions.
     *
     * @description
     * The user will be suppressed by the server if they don't have permissions
     * to speak on the current channel.
     *
     * @name User#suppress
     * @type Boolean
     */
    this.suppress = data.suppress;

    /**
     * @summary _true_ when the user has muted themselves.
     *
     * @name User#selfMute
     * @type Boolean
     */
    this.selfMute = data.self_mute;

    /**
     * @summary _true_ when the user has deafened themselves.
     *
     * @name User#selfDeaf
     * @type Boolean
     */
    this.selfDeaf = data.self_deaf;

    /**
     * @summary The hash of the user certificate
     *
     * @name User#hash
     * @type String
     */
    this.hash = data.hash;

    /**
     * @summary _true_ when the user is recording the conversation.
     *
     * @name User#recording
     * @type Boolean
     */
    this.recording = data.recording;

    /**
     * @summary _true_ when the user is a priority speaker.
     *
     * @name User#prioritySpeaker
     * @type Boolean
     */
    this.prioritySpeaker = data.priority_speaker;

    /**
     * @summary User's current channel.
     *
     * @name User#channel
     * @type Channel
     */
    if(data.channel_id !== null) {
        this.channel = this.client.channelById(data.channel_id);
    }
    else { // New users always enter root
        this.channel = this.client.rootChannel;
    }
    this.channel._addUser(this);
    //TODO: Comments, textures
};

/**
 * @summary Emitted when the user disconnects
 *
 * @description
 * Also available through the client `user-disconnect` event.
 *
 * @event User#disconnect
 */

User.prototype._detach = function() {
    this.emit('disconnect');
    this.channel._removeUser(this);
};

/**
 * @summary Emitted when the user moves between channels.
 *
 * @event User#move
 * @param {Channel} oldChannel - The channel where the user was moved from.
 * @param {Channel} newChannel - The channel where the user was moved to.
 * @param {User} actor - The user who moved the channel or undefined for server.
 */
User.prototype._checkChangeChannel = function( data ) {

    // Get the two channel instances.
    var newChannel = this.client.channelById( data.channel_id );
    var oldChannel = this.channel;

    // Make sure there is a change in the channel.
    if( newChannel === oldChannel )
        return;

    // Make the channel change and notify listeners.
    this.channel = newChannel;
    oldChannel._removeUser( this );
    newChannel._addUser( this );

    var actor = this.client.userBySession( data.actor );
    this.emit( 'move', oldChannel, newChannel, actor );
};

/**
 * @summary Emitted when the user is muted or unmuted by the server.
 *
 * @description
 * Also available through the client `user-mute` event.
 *
 * @event User#mute
 * @param {Boolean} status
 *      True when the user is muted, false when unmuted.
 */

/**
 * @summary Emitted when the user mutes or unmutes themselves.
 *
 * @description
 * Also available through the client `user-self-mute` event.
 *
 * @event User#self-mute
 * @param {Boolean} status
 *      True when the user mutes themselves. False when unmuting.
 */

/**
 * @summary Emitted when the user deafens or undeafens themselves.
 *
 * @description
 * Also available through the client `user-self-deaf` event.
 *
 * @event User#self-deaf
 * @param {Boolean} status
 *      True when the user deafens themselves. False when undeafening.
 */

/**
 * @summary Emitted when the user is suppressed or unsuppressed.
 *
 * @description
 * Also available through the client `user-suppress` event.
 *
 * @event User#suppress
 * @param {Boolean} status
 *      True when the user is suppressed. False when unsuppressed.
 */

/**
 * @summary Emitted when the user starts or stops recording.
 *
 * @description
 * Also available through the client `user-recording` event.
 *
 * @event User#recording
 * @param {Boolean} status
 *      True when the user starts recording. False when they stop.
 */

/**
 * @summary Emitted when the user gains or loses priority speaker status.
 *
 * @description
 * Also available through the client `user-priority-speaker` event.
 *
 * @event User#priority-speaker
 * @param {Boolean} status
 *      True when the user gains priority speaker status. False when they lose
 *      it.
 */

User.prototype.update = function(data) {
    var self = this;

    // Check the simple fields.
    [
        'mute', 'selfMute', 'suppress',
        'selfDeaf',
        'recording', 'prioritySpeaker',
    ].forEach( function(f) {
        self._checkField( data, f );
    });

    // Channel check
    if( data.channel_id !== null ) {
        this._checkChangeChannel( data );
    }

};

User.prototype._sendRemoveUser = function( reason, ban ) {
    this.client.connection.sendMessage( 'UserRemove', {
        session: this.session,
        actor: this.client.user.session,
        reason: reason,
        ban: ban
    } );
};

User.prototype._checkField = function( data, field ) {

    // Make sure the field has a value.
    var newValue = data[ util.toFieldName( field ) ];
    if( newValue === undefined )
        return;

    // Make sure the new value differs.
    var oldValue = this[ field ];
    if( newValue === oldValue )
        return;

    // All checks succeeded. Store the new value and emit change event.
    this[ field ] = newValue;
    var actor = this.client.userBySession( data.actor );
    this.emit( util.toEventName( field ), newValue, actor );
};

module.exports = User;
<commit_msg>Add self mute/deaf to User. closes #73.<commit_after>
"use strict";

var EventEmitter = require('events').EventEmitter;
var util = require( './util' );

/**
 * Single user on the server.
 */
var User = function(data, client) {
    this.client = client;
    this._applyProperties(data);
};

User.prototype = Object.create(EventEmitter.prototype);

/**
 * @summary Moves the user to a channel
 *
 * @param {Channel|String} channel - Channel name or a channel object
 */
User.prototype.moveToChannel = function(channel) {
    var id;
    if(typeof channel === "string") {
        id = this.client.channelByName(channel).id;
    }
    else if(typeof channel === "object") {
        id = channel.id;
    }
    else {
        return;
    }
    this.client.connection.sendMessage( 'UserState', { session: this.session, actor: this.client.user.session, channel_id: id });
};

/**
 * @summary Change a user's self deafened state. (Obviously) only works on yourself.
 *
 * @param {Boolean} isSelfDeaf - The new self deafened state
 */
User.prototype.setSelfDeaf = function(isSelfDeaf){
    this.client.connection.sendMessage( 'UserState', { session: this.session, actor: this.client.user.session, self_deaf: isSelfDeaf });
};

/**
 * @summary Change a user's self muted state. (Obviously) only works on yourself.
 *
 * @param {Boolean} isSelfMute - The new self muted state
 */
User.prototype.setSelfMute = function(isSelfMute){
    this.client.connection.sendMessage( 'UserState', { session: this.session, actor: this.client.user.session, self_mute: isSelfMute });
};

/**
 * @summary Attempts to kick the user.
 *
 * @param {String} [reason] - The reason to kick the user for.
 */
User.prototype.kick = function(reason) {
    this._sendRemoveUser( reason || "You have been kicked", false );
};

/**
 * @summary Attempts to ban the user.
 *
 * @param {String} [reason] - The reason to ban the user for.
 */
User.prototype.ban = function(reason) {
    this._sendRemoveUser( reason || "You have been banned", true );
};

/**
 * @summary Sends a message to the user.
 *
 * @param {String} message - The message to send.
 */
User.prototype.sendMessage = function(message) {
    this.client.sendMessage( message, { session: [ this.session ] } );
};

/**
 * @summary Returns an output stream for listening to the user audio.
 *
 * @param {Boolean} [noEmptyFrames]
 *      True to cut the output stream during silence. If the output stream
 *      isn't cut it will keep emitting zero-values when the user isn't
 *      talking.
 *
 * @returns {MumbleOutputStream} Output stream.
 */
User.prototype.outputStream = function(noEmptyFrames) {
    return this.client.connection.outputStream(this.session, noEmptyFrames);
};

/**
 * @summary Returns an input stream for sending audio to the user.
 *
 * @returns {MumbleInputStream} Input stream.
 */
User.prototype.inputStream = function() {
    return this.client.inputStreamForUser( this.session );
};

/**
 * @summary Checks whether the user can talk or not.
 *
 * @returns {Boolean} True if the user can talk.
 */
User.prototype.canTalk = function() {
    return !this.mute && !this.selfMute && !this.suppress;
};

/**
 * @summary Checks whether the user can hear other people.
 *
 * @returns {Boolean} True if the user can hear.
 */
User.prototype.canHear = function() {
    return !this.selfDeaf;
};

User.prototype._applyProperties = function(data) {

    /**
     * @summary Session ID
     *
     * @description
     * Session ID is present for all users. The ID specifies the current user
     * session and will change when the user reconnects.
     *
     * @see User#id
     *
     * @name User#session
     * @type Number
     */
    this.session = data.session;

    /**
     * @summary User name
     *
     * @name User#name
     * @type String
     */
    this.name = data.name;

    /**
     * @summary User ID
     *
     * @description
     * User ID is specified only for users who are registered on the server.
     * The user ID won't change when the user reconnects.
     *
     * @see User#session
     *
     * @name User#id
     * @type Number
     */
    this.id = data.user_id;

    /**
     * @summary _true_ when the user is muted by an admin.
     *
     * @name User#mute
     * @type Boolean
     */
    this.mute = data.mute;

    /**
     * @summary _true_ when the user is deafened by an admin.
     *
     * @name User#deaf
     * @type Boolean
     */
    this.deaf = data.deaf;

    /**
     * @summary _true_ when the user is suppressed due to lack of
     * permissions.
     *
     * @description
     * The user will be suppressed by the server if they don't have permissions
     * to speak on the current channel.
     *
     * @name User#suppress
     * @type Boolean
     */
    this.suppress = data.suppress;

    /**
     * @summary _true_ when the user has muted themselves.
     *
     * @name User#selfMute
     * @type Boolean
     */
    this.selfMute = data.self_mute;

    /**
     * @summary _true_ when the user has deafened themselves.
     *
     * @name User#selfDeaf
     * @type Boolean
     */
    this.selfDeaf = data.self_deaf;

    /**
     * @summary The hash of the user certificate
     *
     * @name User#hash
     * @type String
     */
    this.hash = data.hash;

    /**
     * @summary _true_ when the user is recording the conversation.
     *
     * @name User#recording
     * @type Boolean
     */
    this.recording = data.recording;

    /**
     * @summary _true_ when the user is a priority speaker.
     *
     * @name User#prioritySpeaker
     * @type Boolean
     */
    this.prioritySpeaker = data.priority_speaker;

    /**
     * @summary User's current channel.
     *
     * @name User#channel
     * @type Channel
     */
    if(data.channel_id !== null) {
        this.channel = this.client.channelById(data.channel_id);
    }
    else { // New users always enter root
        this.channel = this.client.rootChannel;
    }
    this.channel._addUser(this);
    //TODO: Comments, textures
};

/**
 * @summary Emitted when the user disconnects
 *
 * @description
 * Also available through the client `user-disconnect` event.
 *
 * @event User#disconnect
 */

User.prototype._detach = function() {
    this.emit('disconnect');
    this.channel._removeUser(this);
};

/**
 * @summary Emitted when the user moves between channels.
 *
 * @event User#move
 * @param {Channel} oldChannel - The channel where the user was moved from.
 * @param {Channel} newChannel - The channel where the user was moved to.
 * @param {User} actor - The user who moved the channel or undefined for server.
 */
User.prototype._checkChangeChannel = function( data ) {

    // Get the two channel instances.
    var newChannel = this.client.channelById( data.channel_id );
    var oldChannel = this.channel;

    // Make sure there is a change in the channel.
    if( newChannel === oldChannel )
        return;

    // Make the channel change and notify listeners.
    this.channel = newChannel;
    oldChannel._removeUser( this );
    newChannel._addUser( this );

    var actor = this.client.userBySession( data.actor );
    this.emit( 'move', oldChannel, newChannel, actor );
};

/**
 * @summary Emitted when the user is muted or unmuted by the server.
 *
 * @description
 * Also available through the client `user-mute` event.
 *
 * @event User#mute
 * @param {Boolean} status
 *      True when the user is muted, false when unmuted.
 */

/**
 * @summary Emitted when the user mutes or unmutes themselves.
 *
 * @description
 * Also available through the client `user-self-mute` event.
 *
 * @event User#self-mute
 * @param {Boolean} status
 *      True when the user mutes themselves. False when unmuting.
 */

/**
 * @summary Emitted when the user deafens or undeafens themselves.
 *
 * @description
 * Also available through the client `user-self-deaf` event.
 *
 * @event User#self-deaf
 * @param {Boolean} status
 *      True when the user deafens themselves. False when undeafening.
 */

/**
 * @summary Emitted when the user is suppressed or unsuppressed.
 *
 * @description
 * Also available through the client `user-suppress` event.
 *
 * @event User#suppress
 * @param {Boolean} status
 *      True when the user is suppressed. False when unsuppressed.
 */

/**
 * @summary Emitted when the user starts or stops recording.
 *
 * @description
 * Also available through the client `user-recording` event.
 *
 * @event User#recording
 * @param {Boolean} status
 *      True when the user starts recording. False when they stop.
 */

/**
 * @summary Emitted when the user gains or loses priority speaker status.
 *
 * @description
 * Also available through the client `user-priority-speaker` event.
 *
 * @event User#priority-speaker
 * @param {Boolean} status
 *      True when the user gains priority speaker status. False when they lose
 *      it.
 */

User.prototype.update = function(data) {
    var self = this;

    // Check the simple fields.
    [
        'mute', 'selfMute', 'suppress',
        'selfDeaf',
        'recording', 'prioritySpeaker',
    ].forEach( function(f) {
        self._checkField( data, f );
    });

    // Channel check
    if( data.channel_id !== null ) {
        this._checkChangeChannel( data );
    }

};

User.prototype._sendRemoveUser = function( reason, ban ) {
    this.client.connection.sendMessage( 'UserRemove', {
        session: this.session,
        actor: this.client.user.session,
        reason: reason,
        ban: ban
    } );
};

User.prototype._checkField = function( data, field ) {

    // Make sure the field has a value.
    var newValue = data[ util.toFieldName( field ) ];
    if( newValue === undefined )
        return;

    // Make sure the new value differs.
    var oldValue = this[ field ];
    if( newValue === oldValue )
        return;

    // All checks succeeded. Store the new value and emit change event.
    this[ field ] = newValue;
    var actor = this.client.userBySession( data.actor );
    this.emit( util.toEventName( field ), newValue, actor );
};

module.exports = User;
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { replace, push } from 'react-router-redux';
import { fetchTopicTimespansList, filterByTimespan, toggleTimespanControls, setTimespanVisiblePeriod }
  from '../../../../actions/topicActions';
import composeAsyncContainer from '../../../common/AsyncContainer';
import { filteredLocation } from '../../../util/location';
import TimespanSelector from './TimespanSelector';

class TimespanSelectorContainer extends React.Component {
  componentWillReceiveProps(nextProps) {
    const { filters, fetchData, selectedTimespan } = this.props;
    console.log('receiving new props!');
    console.log(nextProps.filters);
    if (((nextProps.filters.snapshotId !== filters.snapshotId) ||
         (nextProps.filters.focusId !== filters.focusId) ||
         (nextProps.filters.timespanId !== filters.timespanId)) &&
        (nextProps.topicId !== null) && (nextProps.filters.snapshotId !== null)) {
      fetchData(nextProps.topicId, nextProps.filters.snapshotId, nextProps.filters.focusId, nextProps.timespanId, selectedTimespan);
    }
  }
  refetchData = () => {
    const { topicId, filters, timespanId, fetchData, selectedTimespan } = this.props;
    fetchData(topicId, filters.snapshotId, filters.focusId, timespanId, selectedTimespan);
  }
  render() {
    const { timespans, selectedTimespan, setExpanded, handleTimespanSelected, handlePeriodSelected, isVisible, selectedPeriod } = this.props;
    let content = null;
    if ((timespans.length > 0) && (selectedTimespan !== null) && (selectedTimespan !== undefined)) {
      content = (
        <TimespanSelector
          timespans={timespans}
          isExpanded={isVisible}
          selectedPeriod={selectedPeriod}
          selectedTimespan={selectedTimespan}
          onTimespanSelected={handleTimespanSelected}
          onPeriodSelected={handlePeriodSelected}
          setExpanded={setExpanded}
        />
      );
    }
    return (

        {content}

    );
  }
}

TimespanSelectorContainer.propTypes = {
  // from parent
  topicId: React.PropTypes.number.isRequired,
  location: React.PropTypes.object.isRequired,
  filters: React.PropTypes.object.isRequired,
  // from dispatch
  fetchData: React.PropTypes.func.isRequired,
  handleTimespanSelected: React.PropTypes.func.isRequired,
  setExpanded: React.PropTypes.func.isRequired,
  handlePeriodSelected: React.PropTypes.func.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
  timespans: React.PropTypes.array.isRequired,
  isVisible: React.PropTypes.bool.isRequired,
  selectedPeriod: React.PropTypes.string.isRequired,
  timespanId: React.PropTypes.number,
  selectedTimespan: React.PropTypes.object,
};

// helper to update the url and fire off event
function updateTimespan(dispatch, location, timespan, shouldPush) {
  const newLocation = filteredLocation(location,
    { snapshotId: timespan.snapshots_id, focusId: timespan.foci_id, timespanId: timespan.timespans_id });
  // console.log('updateTimestamp');
  // console.log(newLocation);
  dispatch(filterByTimespan(timespan.timespans_id));
  if (shouldPush) {
    // add to history
    dispatch(push(newLocation));
  } else {
    // do a replace, not a push here so the non-snapshot url isn't in the history
    dispatch(replace(newLocation));
  }
}

const mapStateToProps = state => ({
  fetchStatus: state.topics.selected.timespans.fetchStatus,
  timespans: state.topics.selected.timespans.list,
  timespanId: state.topics.selected.filters.timespanId,
  isVisible: state.topics.selected.timespans.isVisible,
  selectedPeriod: state.topics.selected.timespans.selectedPeriod,
  selectedTimespan: state.topics.selected.timespans.selected,
});

// when you switch snapshots we need to find the matching timespan in the new snapshot
function findMatchingTimespan(timespan, timespanList) {
  return timespanList.list.find(ts => (
    ((ts.period === timespan.period) && (ts.start_date === timespan.start_date) && (ts.end_date === timespan.end_date))
  ));
}

const mapDispatchToProps = (dispatch, ownProps) => ({
  handlePeriodSelected: (period) => {
    dispatch(setTimespanVisiblePeriod(period));
  },
  setExpanded: (isExpanded) => {
    dispatch(toggleTimespanControls(isExpanded));
  },
  fetchData: (topicId, snapshotId, focusId, timespanId, selectedTimespan) => {
    const cleanedFocus = isNaN(focusId) ? null : focusId;
    dispatch(fetchTopicTimespansList(topicId, snapshotId, { focusId: cleanedFocus }))
      .then((response) => {
        let pickDefault = false;
        if (timespanId === null || isNaN(timespanId)) {
          // no timespan selected so we'll default to one
          pickDefault = true;
        } else if ((selectedTimespan !== null) && (selectedTimespan.foci_id !== cleanedFocus)) {
          // if the snapshot has switched, we need to figure out what the corresponding timespan is
          const matchingNewTimespan = findMatchingTimespan(selectedTimespan, response.list);
          if (matchingNewTimespan !== undefined) {
            updateTimespan(dispatch, ownProps.location, matchingNewTimespan, false);
          } else {
            pickDefault = true;
          }
        } else {
          // first load - match sure selected timespan period matches the selected timespan
          const matchingTimespan = response.list.find(ts => ts.timespans_id === timespanId);
          dispatch(setTimespanVisiblePeriod(matchingTimespan.period));
        }
        if (pickDefault) {
          const defaultTimespan = response.list[0]; // pick the first timespan as the default (this is the overall one)
          // console.log('pick default');
          // console.log(defaultTimespan);
          updateTimespan(dispatch, ownProps.location, defaultTimespan, false);
        }
      });
  },
  handleTimespanSelected: (timespan) => {
    updateTimespan(dispatch, ownProps.location, timespan, true);
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      if (ownProps.filters.snapshotId !== null) {
        dispatchProps.fetchData(ownProps.topicId, ownProps.filters.snapshotId,
          ownProps.filters.focusId, stateProps.timespanId, stateProps.selectedTimespan);
      }
    },
  });
}

export default
  connect(mapStateToProps, mapDispatchToProps, mergeProps)(
    composeAsyncContainer(
      TimespanSelectorContainer
    )
  );
<commit_msg>remove logs<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { replace, push } from 'react-router-redux';
import { fetchTopicTimespansList, filterByTimespan, toggleTimespanControls, setTimespanVisiblePeriod }
  from '../../../../actions/topicActions';
import composeAsyncContainer from '../../../common/AsyncContainer';
import { filteredLocation } from '../../../util/location';
import TimespanSelector from './TimespanSelector';

class TimespanSelectorContainer extends React.Component {
  componentWillReceiveProps(nextProps) {
    const { filters, fetchData, selectedTimespan } = this.props;
    if (((nextProps.filters.snapshotId !== filters.snapshotId) ||
         (nextProps.filters.focusId !== filters.focusId) ||
         (nextProps.filters.timespanId !== filters.timespanId)) &&
        (nextProps.topicId !== null) && (nextProps.filters.snapshotId !== null)) {
      fetchData(nextProps.topicId, nextProps.filters.snapshotId, nextProps.filters.focusId, nextProps.timespanId, selectedTimespan);
    }
  }
  refetchData = () => {
    const { topicId, filters, timespanId, fetchData, selectedTimespan } = this.props;
    fetchData(topicId, filters.snapshotId, filters.focusId, timespanId, selectedTimespan);
  }
  render() {
    const { timespans, selectedTimespan, setExpanded, handleTimespanSelected, handlePeriodSelected, isVisible, selectedPeriod } = this.props;
    let content = null;
    if ((timespans.length > 0) && (selectedTimespan !== null) && (selectedTimespan !== undefined)) {
      content = (
        <TimespanSelector
          timespans={timespans}
          isExpanded={isVisible}
          selectedPeriod={selectedPeriod}
          selectedTimespan={selectedTimespan}
          onTimespanSelected={handleTimespanSelected}
          onPeriodSelected={handlePeriodSelected}
          setExpanded={setExpanded}
        />
      );
    }
    return (

        {content}

    );
  }
}

TimespanSelectorContainer.propTypes = {
  // from parent
  topicId: React.PropTypes.number.isRequired,
  location: React.PropTypes.object.isRequired,
  filters: React.PropTypes.object.isRequired,
  // from dispatch
  fetchData: React.PropTypes.func.isRequired,
  handleTimespanSelected: React.PropTypes.func.isRequired,
  setExpanded: React.PropTypes.func.isRequired,
  handlePeriodSelected: React.PropTypes.func.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
  timespans: React.PropTypes.array.isRequired,
  isVisible: React.PropTypes.bool.isRequired,
  selectedPeriod: React.PropTypes.string.isRequired,
  timespanId: React.PropTypes.number,
  selectedTimespan: React.PropTypes.object,
};

// helper to update the url and fire off event
function updateTimespan(dispatch, location, timespan, shouldPush) {
  const newLocation = filteredLocation(location,
    { snapshotId: timespan.snapshots_id, focusId: timespan.foci_id, timespanId: timespan.timespans_id });
  // console.log('updateTimestamp');
  // console.log(newLocation);
  dispatch(filterByTimespan(timespan.timespans_id));
  if (shouldPush) {
    // add to history
    dispatch(push(newLocation));
  } else {
    // do a replace, not a push here so the non-snapshot url isn't in the history
    dispatch(replace(newLocation));
  }
}

const mapStateToProps = state => ({
  fetchStatus: state.topics.selected.timespans.fetchStatus,
  timespans: state.topics.selected.timespans.list,
  timespanId: state.topics.selected.filters.timespanId,
  isVisible: state.topics.selected.timespans.isVisible,
  selectedPeriod: state.topics.selected.timespans.selectedPeriod,
  selectedTimespan: state.topics.selected.timespans.selected,
});

// when you switch snapshots we need to find the matching timespan in the new snapshot
function findMatchingTimespan(timespan, timespanList) {
  return timespanList.list.find(ts => (
    ((ts.period === timespan.period) && (ts.start_date === timespan.start_date) && (ts.end_date === timespan.end_date))
  ));
}

const mapDispatchToProps = (dispatch, ownProps) => ({
  handlePeriodSelected: (period) => {
    dispatch(setTimespanVisiblePeriod(period));
  },
  setExpanded: (isExpanded) => {
    dispatch(toggleTimespanControls(isExpanded));
  },
  fetchData: (topicId, snapshotId, focusId, timespanId, selectedTimespan) => {
    const cleanedFocus = isNaN(focusId) ? null : focusId;
    dispatch(fetchTopicTimespansList(topicId, snapshotId, { focusId: cleanedFocus }))
      .then((response) => {
        let pickDefault = false;
        if (timespanId === null || isNaN(timespanId)) {
          // no timespan selected so we'll default to one
          pickDefault = true;
        } else if ((selectedTimespan !== null) && (selectedTimespan.foci_id !== cleanedFocus)) {
          // if the snapshot has switched, we need to figure out what the corresponding timespan is
          const matchingNewTimespan = findMatchingTimespan(selectedTimespan, response.list);
          if (matchingNewTimespan !== undefined) {
            updateTimespan(dispatch, ownProps.location, matchingNewTimespan, false);
          } else {
            pickDefault = true;
          }
        } else {
          // first load - match sure selected timespan period matches the selected timespan
          const matchingTimespan = response.list.find(ts => ts.timespans_id === timespanId);
          dispatch(setTimespanVisiblePeriod(matchingTimespan.period));
        }
        if (pickDefault) {
          const defaultTimespan = response.list[0]; // pick the first timespan as the default (this is the overall one)
          // console.log('pick default');
          // console.log(defaultTimespan);
          updateTimespan(dispatch, ownProps.location, defaultTimespan, false);
        }
      });
  },
  handleTimespanSelected: (timespan) => {
    updateTimespan(dispatch, ownProps.location, timespan, true);
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      if (ownProps.filters.snapshotId !== null) {
        dispatchProps.fetchData(ownProps.topicId, ownProps.filters.snapshotId,
          ownProps.filters.focusId, stateProps.timespanId, stateProps.selectedTimespan);
      }
    },
  });
}

export default
  connect(mapStateToProps, mapDispatchToProps, mergeProps)(
    composeAsyncContainer(
      TimespanSelectorContainer
    )
  );
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//
// PersonalOptions class
// Encapsulated handling of personal options page.
//
function PersonalOptions() {
  OptionsPage.call(this, 'personal', templateData.personalPage, 'personalPage');
}

cr.addSingletonGetter(PersonalOptions);

PersonalOptions.prototype = {
  // Inherit PersonalOptions from OptionsPage.
  __proto__: OptionsPage.prototype,

  // Initialize PersonalOptions page.
  initializePage: function() {
    // Call base class implementation to starts preference initialization.
    OptionsPage.prototype.initializePage.call(this);

    // Listen to pref changes.
    Preferences.getInstance().addEventListener('sync.has_setup_completed',
        function(event) {
          if(event.value) {
            $('text-when-synced').style.display = 'block';
            $('button-when-synced').style.display = 'block';
          }
          else {
            $('text-when-not-synced').style.display = 'block';
            $('button-when-not-synced').style.display = 'block';
          }
        });

    $('sync-customize').onclick = function(event) {
      OptionsPage.showPageByName('sync');
    };

    $('showpasswords').onclick = function(event) {
      //TODO(sargrass): Show passwords dialog here.
    };

    $('autofill_options').onclick = function(event) {
      //TODO(sargrass): Show autofill dialog here.
    };

    $('import_data').onclick = function(event) {
      //TODO(sargrass): Show import_data dialog here.
    };

    if(navigator.platform.match(/linux|BSD/i)) {
      $('themes_GTK_button').onclick = function(event) {
        //TODO(sargrass): Show themes GTK dialog here.
      };

      $('themes_set_classic').onclick = function(event) {
        //TODO(sargrass): Show themes set classic dialog here.
      };
    }

    if(navigator.platform.match(/Mac|Win|CrOS/i)) {
      $('themes_reset').onclick = function(event) {
        //TODO(sargrass): Show themes reset dialog here.
      };
    }

  },
};
<commit_msg>Fix DOMUI options for Chromium OS.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//
// PersonalOptions class
// Encapsulated handling of personal options page.
//
function PersonalOptions() {
  OptionsPage.call(this, 'personal', templateData.personalPage, 'personalPage');
}

cr.addSingletonGetter(PersonalOptions);

PersonalOptions.prototype = {
  // Inherit PersonalOptions from OptionsPage.
  __proto__: OptionsPage.prototype,

  // Initialize PersonalOptions page.
  initializePage: function() {
    // Call base class implementation to starts preference initialization.
    OptionsPage.prototype.initializePage.call(this);

    // Listen to pref changes.
    Preferences.getInstance().addEventListener('sync.has_setup_completed',
        function(event) {
          if(event.value) {
            $('text-when-synced').style.display = 'block';
            $('button-when-synced').style.display = 'block';
          }
          else {
            $('text-when-not-synced').style.display = 'block';
            $('button-when-not-synced').style.display = 'block';
          }
        });

    $('sync-customize').onclick = function(event) {
      OptionsPage.showPageByName('sync');
    };

    $('showpasswords').onclick = function(event) {
      //TODO(sargrass): Show passwords dialog here.
    };

    $('autofill_options').onclick = function(event) {
      //TODO(sargrass): Show autofill dialog here.
    };

    $('import_data').onclick = function(event) {
      //TODO(sargrass): Show import_data dialog here.
    };

    if(!cr.isChromeOS && navigator.platform.match(/linux|BSD/i)) {
      $('themes_GTK_button').onclick = function(event) {
        //TODO(sargrass): Show themes GTK dialog here.
      };

      $('themes_set_classic').onclick = function(event) {
        //TODO(sargrass): Show themes set classic dialog here.
      };
    }

    if(cr.isMac || cr.isWindows || cr.isChromeOS) {
      $('themes_reset').onclick = function(event) {
        //TODO(sargrass): Show themes reset dialog here.
      };
    }

  },
};
<|endoftext|>