<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';

var globalTimers = {
    setTimeout: require('timers').setTimeout,
    clearTimeout: require('timers').clearTimeout,
    now: Date.now
};
var globalRandom = Math.random;
var net = require('net');
var format = require('util').format;
var extend = require('xtend');

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

var nullLogger = require('./null-logger.js');
var EndpointHandler = require('./endpoint-handler.js');
var TChannelRequest = require('./request');
var TChannelServiceNameHandler = require('./service-name-handler');
var errors = require('./errors');

var TChannelConnection = require('./connection');
var TChannelPeers = require('./peers');

var REQ_TIMEOUT_DEFAULT = 5000;

// TODO restore spying
// var Spy = require('./v2/spy');
// var dumpEnabled = /\btchannel_dump\b/.test(process.env.NODE_DEBUG || '');

function TChannel(options) {
    if (!(this instanceof TChannel)) {
        return new TChannel(options);
    }

    var self = this;
    EventEmitter.call(self);

    self.options = extend({
        timeoutCheckInterval: 1000,
        timeoutFuzz: 100,
        // TODO: maybe we should always add pid to user-supplied?
        processName: format('%s[%s]', process.title, process.pid)
    }, options);

    self.options.requestDefaults = extend({
        timeout: REQ_TIMEOUT_DEFAULT
    }, self.options.requestDefaults);

    self.logger = self.options.logger || nullLogger;
    self.random = self.options.random || globalRandom;
    self.timers = self.options.timers || globalTimers;

    // Filled in by the listen call:
    self.host = null;
    self.requestedPort = null;

    // Filled in by listening event:
    self.hostPort = null;

    // name of the service running over this channel
    self.serviceName = '';
    if (self.options.serviceName) {
        self.serviceName = self.options.serviceName;
        self.options.requestDefaults.serviceName = self.serviceName;
        delete self.options.serviceName;
    }

    // populated by makeSubChannel
    self.topChannel = null;
    self.subChannels = self.serviceName ? null : {};

    // how to handle incoming requests
    if (!self.options.handler) {
        if (!self.serviceName) {
            self.handler = TChannelServiceNameHandler(self);
        } else {
            self.handler = EndpointHandler(self.serviceName);
        }
    } else {
        self.handler = self.options.handler;
        delete self.options.handler;
    }

    // populated by:
    // - manually api (.peers.add etc)
    // - incoming connections on any listening socket
    self.peers = TChannelPeers(self, self.options);

    // TChannel advances through the following states.
    self.listened = false;
    self.listening = false;
    self.destroyed = false;

    if (self.options.trace) {
        self.tracer = require('./trace/agent').ref({
            autoTracePropagate: self.options.autoTracePropagate !== false
        });
        if (self.options.requestDefaults !== false) {
            self.options.requestDefaults.trace = true;
        }
    }

    // lazily created by .getServer (usually from .listen)
    self.serverSocket = null;
    self.serverConnections = null;
}
inherits(TChannel, EventEmitter);

TChannel.prototype.getServer = function getServer() {
    var self = this;
    if (self.serverSocket) {
        return self.serverSocket;
    }

    self.serverConnections = Object.create(null);
    self.serverSocket = net.createServer(onServerSocketConnection);
    self.serverSocket.on('listening', onServerSocketListening);
    self.serverSocket.on('error', onServerSocketError);

    return self.serverSocket;

    function onServerSocketConnection(sock) {
        self.onServerSocketConnection(sock);
    }

    function onServerSocketListening() {
        self.onServerSocketListening();
    }

    function onServerSocketError(err) {
        self.onServerSocketError(err);
    }
};

TChannel.prototype.onServerSocketConnection = function onServerSocketConnection(sock) {
    var self = this;

    if (self.destroyed) {
        self.logger.error('got incoming socket whilst destroyed', {
            remoteAddr: sock.remoteAddr,
            remotePort: sock.remotePort,
            hostPort: self.hostPort
        });
        return;
    }

    var remoteAddr = sock.remoteAddress + ':' + sock.remotePort;
    var conn = new TChannelConnection(self, sock, 'in', remoteAddr);

    conn.on('span', function handleSpanFromConn(span) {
        self.tracer.report(span);
    });

    if (self.serverConnections[remoteAddr]) {
        var oldConn = self.serverConnections[remoteAddr];
        oldConn.resetAll(errors.SocketClosedError({
            reason: 'duplicate remoteAddr incoming conn'
        }));
        delete self.serverConnections[remoteAddr];
    }

    sock.on('close', onSocketClose);

    self.serverConnections[remoteAddr] = conn;
    self.emit('connection', conn);

    function onSocketClose() {
        delete self.serverConnections[remoteAddr];
    }
};

TChannel.prototype.onServerSocketListening = function onServerSocketListening() {
    var self = this;

    if (self.destroyed) {
        self.logger.error('got serverSocket listen whilst destroyed', {
            requestHostPort: self.host + ':' + self.requestedPort,
            hostPort: self.host + ':' + self.serverSocket.address().port
        });
        return;
    }

    var address = self.serverSocket.address();
    self.hostPort = self.host + ':' + address.port;
    self.listening = true;

    if (self.subChannels) {
        Object.keys(self.subChannels).forEach(function each(serviceName) {
            var chan = self.subChannels[serviceName];
            if (!chan.hostPort) {
                chan.hostPort = self.hostPort;
            }
        });
    }

    self.emit('listening');
};

TChannel.prototype.onServerSocketError = function onServerSocketError(err) {
    var self = this;

    if (err.code === 'EADDRINUSE') {
        err = errors.TChannelListenError(err, {
            requestedPort: self.requestedPort,
            host: self.host
        });
    }
    self.logger.error('server socket error', {
        err: err,
        requestedPort: self.requestedPort,
        host: self.host,
        hostPort: self.hostPort || null
    });
    self.emit('error', err);
};

TChannel.prototype.makeSubChannel = function makeSubChannel(options) {
    var self = this;
    if (!options) options = {};
    if (self.serviceName) {
        throw new Error('arbitrary-depth sub channels are unsupported'); // TODO typed error
    }
    if (!options.serviceName) {
        throw new Error('must specify serviceName'); // TODO typed error
    }
    if (self.subChannels[options.serviceName]) {
        throw new Error('sub channel already exists'); // TODO typed error
    }
    var opts = extend(self.options);
    var keys = Object.keys(options);
    for (var i = 0; i < keys.length; i++) {
        switch (keys[i]) {
            case 'peers':
                break;
            default:
                opts[keys[i]] = options[keys[i]];
        }
    }
    var chan = TChannel(opts);
    chan.topChannel = self;
    if (options.peers) {
        for (i = 0; i < options.peers.length; i++) {
            if (typeof options.peers[i] === 'string') {
                chan.peers.addPeer(self.peers.add(options.peers[i]));
            } else {
                chan.peers.addPeer(options.peers[i]);
            }
        }
    }
    self.subChannels[chan.serviceName] = chan;

    if (self.hostPort) {
        chan.hostPort = self.hostPort;
    }

    return chan;
};

// Decoulping config and creation from the constructor.
TChannel.prototype.listen = function listen(port, host, callback) {
    var self = this;
    if (self.listened) {
        throw new Error('TChannel can only listen once'); // TODO typed error
    }
    if (typeof host !== 'string') {
        throw new Error('TChannel requires host argument'); // TODO typed error
    }
    if (typeof port !== 'number') {
        // Note that 0 is a valid port number, indicating that the system must
        // assign an available ephemeral port.
        throw new Error('TChannel must listen with numeric port'); // TODO typed error
    }
    // Does not expressly forbid 127.0.0.1 or localhost since these are valid
    // hosts for testing.
    if (host === '0.0.0.0') {
        throw new Error('TChannel must listen with externally visible host'); // TODO typed error
    }
    self.listened = true;
    self.requestedPort = port;
    self.host = host;
    self.getServer().listen(port, host, callback);
};

TChannel.prototype.register = function register(name, handler) {
    var self = this;

    var handlerType = self.handler && self.handler.type;

    switch (handlerType) {
        case 'tchannel.endpoint-handler':
            self.handler.register(name, handler);
            break;

        case 'tchannel.service-name-handler':
            throw errors.TopLevelRegisterError();

        default:
            throw errors.InvalidHandlerForRegister({
                handlerType: handlerType,
                handler: self.handler
            });
    }
};

TChannel.prototype.address = function address() {
    var self = this;
    if (self.serverSocket) {
        return self.serverSocket.address() || null;
    } else if (self.topChannel) {
        return self.topChannel.address();
    } else {
        return null;
    }
};

TChannel.prototype.request = function channelRequest(options) {
    var self = this;

    if (self.destroyed) {
        throw new Error('cannot request() to destroyed tchannel'); // TODO typed error
    }

    options = extend(self.options.requestDefaults, options);

    if (!options.service && options.serviceName) {
        options.service = options.serviceName;
    }

    if (!self.serviceName && !options.host) {
        if (options.service &&
            self.subChannels &&
            self.subChannels[options.service]) {
            return self.subChannels[options.service].request(options);
        } else {
            throw errors.TopLevelRequestError();
        }
    }

    if (options.streamed) {
        return self.peers.request(null, options);
    } else {
        return new TChannelRequest(self, options);
    }
};

TChannel.prototype.quit = // to provide backward compatibility.
TChannel.prototype.close = function close(callback) {
    var self = this;

    if (self.destroyed) {
        throw new Error('double close'); // TODO typed error
    }

    if (self.tracer) {
        self.tracer.unref();
    }

    self.destroyed = true;

    var counter = 1;

    if (self.serverSocket) {
        ++counter;
        if (self.serverSocket.address()) {
            closeServerSocket();
        } else {
            self.serverSocket.once('listening', closeServerSocket);
        }
    }

    if (self.serverConnections) {
        var incomingConns = Object.keys(self.serverConnections);
        for (var i = 0; i < incomingConns.length; i++) {
            ++counter;
            var incomingConn = self.serverConnections[incomingConns[i]];
            incomingConn.close(onClose);
        }
    }

    if (self.subChannels) {
        var serviceNames = Object.keys(self.subChannels);
        serviceNames.forEach(function each(serviceName) {
            var svcchan = self.subChannels[serviceName];
            if (!svcchan.destroyed) {
                counter++;
                svcchan.close(onClose);
            }
        });
    }

    self.peers.close(onClose);

    function closeServerSocket() {
        self.serverSocket.once('close', onClose);
        self.serverSocket.close();
    }

    function onClose() {
        if (--counter <= 0) {
            if (counter < 0) {
                self.logger.error('closed more channel sockets than expected', {
                    counter: counter
                });
            }
            if (typeof callback === 'function') {
                callback();
            }
        }
    }
};

module.exports = TChannel;
<commit_msg>TChannel: add request event<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';

var globalTimers = {
    setTimeout: require('timers').setTimeout,
    clearTimeout: require('timers').clearTimeout,
    now: Date.now
};
var globalRandom = Math.random;
var net = require('net');
var format = require('util').format;
var extend = require('xtend');

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

var nullLogger = require('./null-logger.js');
var EndpointHandler = require('./endpoint-handler.js');
var TChannelRequest = require('./request');
var TChannelServiceNameHandler = require('./service-name-handler');
var errors = require('./errors');

var TChannelConnection = require('./connection');
var TChannelPeers = require('./peers');

var REQ_TIMEOUT_DEFAULT = 5000;

// TODO restore spying
// var Spy = require('./v2/spy');
// var dumpEnabled = /\btchannel_dump\b/.test(process.env.NODE_DEBUG || '');

function TChannel(options) {
    if (!(this instanceof TChannel)) {
        return new TChannel(options);
    }

    var self = this;
    EventEmitter.call(self);

    self.options = extend({
        timeoutCheckInterval: 1000,
        timeoutFuzz: 100,
        // TODO: maybe we should always add pid to user-supplied?
        processName: format('%s[%s]', process.title, process.pid)
    }, options);

    self.options.requestDefaults = extend({
        timeout: REQ_TIMEOUT_DEFAULT
    }, self.options.requestDefaults);

    self.logger = self.options.logger || nullLogger;
    self.random = self.options.random || globalRandom;
    self.timers = self.options.timers || globalTimers;

    // Filled in by the listen call:
    self.host = null;
    self.requestedPort = null;

    // Filled in by listening event:
    self.hostPort = null;

    // name of the service running over this channel
    self.serviceName = '';
    if (self.options.serviceName) {
        self.serviceName = self.options.serviceName;
        self.options.requestDefaults.serviceName = self.serviceName;
        delete self.options.serviceName;
    }

    // populated by makeSubChannel
    self.topChannel = null;
    self.subChannels = self.serviceName ? null : {};

    // how to handle incoming requests
    if (!self.options.handler) {
        if (!self.serviceName) {
            self.handler = TChannelServiceNameHandler(self);
        } else {
            self.handler = EndpointHandler(self.serviceName);
        }
    } else {
        self.handler = self.options.handler;
        delete self.options.handler;
    }

    // populated by:
    // - manually api (.peers.add etc)
    // - incoming connections on any listening socket
    self.peers = TChannelPeers(self, self.options);

    // TChannel advances through the following states.
    self.listened = false;
    self.listening = false;
    self.destroyed = false;

    if (self.options.trace) {
        self.tracer = require('./trace/agent').ref({
            autoTracePropagate: self.options.autoTracePropagate !== false
        });
        if (self.options.requestDefaults !== false) {
            self.options.requestDefaults.trace = true;
        }
    }

    // lazily created by .getServer (usually from .listen)
    self.serverSocket = null;
    self.serverConnections = null;
}
inherits(TChannel, EventEmitter);

TChannel.prototype.getServer = function getServer() {
    var self = this;
    if (self.serverSocket) {
        return self.serverSocket;
    }

    self.serverConnections = Object.create(null);
    self.serverSocket = net.createServer(onServerSocketConnection);
    self.serverSocket.on('listening', onServerSocketListening);
    self.serverSocket.on('error', onServerSocketError);

    return self.serverSocket;

    function onServerSocketConnection(sock) {
        self.onServerSocketConnection(sock);
    }

    function onServerSocketListening() {
        self.onServerSocketListening();
    }

    function onServerSocketError(err) {
        self.onServerSocketError(err);
    }
};

TChannel.prototype.onServerSocketConnection = function onServerSocketConnection(sock) {
    var self = this;

    if (self.destroyed) {
        self.logger.error('got incoming socket whilst destroyed', {
            remoteAddr: sock.remoteAddr,
            remotePort: sock.remotePort,
            hostPort: self.hostPort
        });
        return;
    }

    var remoteAddr = sock.remoteAddress + ':' + sock.remotePort;
    var conn = new TChannelConnection(self, sock, 'in', remoteAddr);

    conn.on('span', function handleSpanFromConn(span) {
        self.tracer.report(span);
    });

    if (self.serverConnections[remoteAddr]) {
        var oldConn = self.serverConnections[remoteAddr];
        oldConn.resetAll(errors.SocketClosedError({
            reason: 'duplicate remoteAddr incoming conn'
        }));
        delete self.serverConnections[remoteAddr];
    }

    sock.on('close', onSocketClose);

    self.serverConnections[remoteAddr] = conn;
    self.emit('connection', conn);

    function onSocketClose() {
        delete self.serverConnections[remoteAddr];
    }
};

TChannel.prototype.onServerSocketListening = function onServerSocketListening() {
    var self = this;

    if (self.destroyed) {
        self.logger.error('got serverSocket listen whilst destroyed', {
            requestHostPort: self.host + ':' + self.requestedPort,
            hostPort: self.host + ':' + self.serverSocket.address().port
        });
        return;
    }

    var address = self.serverSocket.address();
    self.hostPort = self.host + ':' + address.port;
    self.listening = true;

    if (self.subChannels) {
        Object.keys(self.subChannels).forEach(function each(serviceName) {
            var chan = self.subChannels[serviceName];
            if (!chan.hostPort) {
                chan.hostPort = self.hostPort;
            }
        });
    }

    self.emit('listening');
};

TChannel.prototype.onServerSocketError = function onServerSocketError(err) {
    var self = this;

    if (err.code === 'EADDRINUSE') {
        err = errors.TChannelListenError(err, {
            requestedPort: self.requestedPort,
            host: self.host
        });
    }
    self.logger.error('server socket error', {
        err: err,
        requestedPort: self.requestedPort,
        host: self.host,
        hostPort: self.hostPort || null
    });
    self.emit('error', err);
};

TChannel.prototype.makeSubChannel = function makeSubChannel(options) {
    var self = this;
    if (!options) options = {};
    if (self.serviceName) {
        throw new Error('arbitrary-depth sub channels are unsupported'); // TODO typed error
    }
    if (!options.serviceName) {
        throw new Error('must specify serviceName'); // TODO typed error
    }
    if (self.subChannels[options.serviceName]) {
        throw new Error('sub channel already exists'); // TODO typed error
    }
    var opts = extend(self.options);
    var keys = Object.keys(options);
    for (var i = 0; i < keys.length; i++) {
        switch (keys[i]) {
            case 'peers':
                break;
            default:
                opts[keys[i]] = options[keys[i]];
        }
    }
    var chan = TChannel(opts);
    chan.topChannel = self;
    if (options.peers) {
        for (i = 0; i < options.peers.length; i++) {
            if (typeof options.peers[i] === 'string') {
                chan.peers.addPeer(self.peers.add(options.peers[i]));
            } else {
                chan.peers.addPeer(options.peers[i]);
            }
        }
    }
    self.subChannels[chan.serviceName] = chan;

    if (self.hostPort) {
        chan.hostPort = self.hostPort;
    }

    return chan;
};

// Decoulping config and creation from the constructor.
TChannel.prototype.listen = function listen(port, host, callback) {
    var self = this;
    if (self.listened) {
        throw new Error('TChannel can only listen once'); // TODO typed error
    }
    if (typeof host !== 'string') {
        throw new Error('TChannel requires host argument'); // TODO typed error
    }
    if (typeof port !== 'number') {
        // Note that 0 is a valid port number, indicating that the system must
        // assign an available ephemeral port.
        throw new Error('TChannel must listen with numeric port'); // TODO typed error
    }
    // Does not expressly forbid 127.0.0.1 or localhost since these are valid
    // hosts for testing.
    if (host === '0.0.0.0') {
        throw new Error('TChannel must listen with externally visible host'); // TODO typed error
    }
    self.listened = true;
    self.requestedPort = port;
    self.host = host;
    self.getServer().listen(port, host, callback);
};

TChannel.prototype.register = function register(name, handler) {
    var self = this;

    var handlerType = self.handler && self.handler.type;

    switch (handlerType) {
        case 'tchannel.endpoint-handler':
            self.handler.register(name, handler);
            break;

        case 'tchannel.service-name-handler':
            throw errors.TopLevelRegisterError();

        default:
            throw errors.InvalidHandlerForRegister({
                handlerType: handlerType,
                handler: self.handler
            });
    }
};

TChannel.prototype.address = function address() {
    var self = this;
    if (self.serverSocket) {
        return self.serverSocket.address() || null;
    } else if (self.topChannel) {
        return self.topChannel.address();
    } else {
        return null;
    }
};

TChannel.prototype.request = function channelRequest(options) {
    var self = this;

    if (self.destroyed) {
        throw new Error('cannot request() to destroyed tchannel'); // TODO typed error
    }

    options = extend(self.options.requestDefaults, options);

    if (!options.service && options.serviceName) {
        options.service = options.serviceName;
    }

    if (!self.serviceName && !options.host) {
        if (options.service &&
            self.subChannels &&
            self.subChannels[options.service]) {
            return self.subChannels[options.service].request(options);
        } else {
            throw errors.TopLevelRequestError();
        }
    }

    var req = null;
    if (options.streamed) {
        req = self.peers.request(null, options);
    } else {
        req = new TChannelRequest(self, options);
    }
    self.emit('request', req);
    return req;
};

TChannel.prototype.quit = // to provide backward compatibility.
TChannel.prototype.close = function close(callback) {
    var self = this;

    if (self.destroyed) {
        throw new Error('double close'); // TODO typed error
    }

    if (self.tracer) {
        self.tracer.unref();
    }

    self.destroyed = true;

    var counter = 1;

    if (self.serverSocket) {
        ++counter;
        if (self.serverSocket.address()) {
            closeServerSocket();
        } else {
            self.serverSocket.once('listening', closeServerSocket);
        }
    }

    if (self.serverConnections) {
        var incomingConns = Object.keys(self.serverConnections);
        for (var i = 0; i < incomingConns.length; i++) {
            ++counter;
            var incomingConn = self.serverConnections[incomingConns[i]];
            incomingConn.close(onClose);
        }
    }

    if (self.subChannels) {
        var serviceNames = Object.keys(self.subChannels);
        serviceNames.forEach(function each(serviceName) {
            var svcchan = self.subChannels[serviceName];
            if (!svcchan.destroyed) {
                counter++;
                svcchan.close(onClose);
            }
        });
    }

    self.peers.close(onClose);

    function closeServerSocket() {
        self.serverSocket.once('close', onClose);
        self.serverSocket.close();
    }

    function onClose() {
        if (--counter <= 0) {
            if (counter < 0) {
                self.logger.error('closed more channel sockets than expected', {
                    counter: counter
                });
            }
            if (typeof callback === 'function') {
                callback();
            }
        }
    }
};

module.exports = TChannel;
<|endoftext|>


<commit_before>const GB = 1000000000;
const MS_TO_HOUR = 1000 * 60 * 60;
const STORAGE_DEBIT_TYPE = "storage";
const BANDWIDTH_DEBIT_TYPE = "bandwidth";
const billingClient = require('../utils/billing-client');

// Need indexes on bucketentries.frame and pointers.hash
// Otherwise these will run slow as hell.

const calculateUserAmountOwedForStorage = function(storage) {
  return function(billingPeriodStart, billingPeriodEnd, gbHourPrice) {
    const billingPeriodStartIsoDate = new Date(billingPeriodStart);
    const billingPeriodEndIsoDate = new Date(billingPeriodEnd);

    return storage.models.Frame
        .aggregate([
          {
            $project: {
              _id: 1,
              user: 1,
              size: 1,
              created: 1
            }
          },
          {
            $lookup: {
              from: "bucketentries",
              localField: "_id",
              foreignField: "frame",
              as: "bucketEntry"
            }
          },
          {
            $unwind: {
              path: "$bucketEntry"
            }
          },
          {
            $project: {
              _id: 1,
              user: 1,
              gigabytes: {$divide: ["$size", GB]},
              fileCreatedTime: {
                $ifNull: ["$bucketEntry.created", "$created"]
              },
              fileRenewalTime: {
                $ifNull: ["$bucketEntry.renewal", billingPeriodEndIsoDate]
              }
            }
          },
          {
            $match: {
              $and: [
                {
                  "fileCreatedTime": {$lte: billingPeriodEndIsoDate}
                },
                {
                  "fileRenewalTime": {$gte: billingPeriodStartIsoDate}
                }
              ]
            }
          },
          {
            $project: {
              _id: 1,
              user: 1,
              fileBillableAmount: {
                $multiply: [
                  gbHourPrice,
                  "$gigabytes",
                  {
                    $divide: [
                      {
                        $add: [
                          1,
                          {
                            $subtract: [
                              {$min: ["$fileRenewalTime", billingPeriodEndIsoDate]},
                              {$max: ["$fileCreatedTime", billingPeriodStartIsoDate]}
                            ]
                          },
                        ]
                      },
                      MS_TO_HOUR
                    ]
                  }
                ]
              }
            }
          },
          {
            $group: {
              _id: "$user",
              totalBillableAmount: {$sum: "$fileBillableAmount"}
            }
          },
          {
            $project: {
              _id: 0,
              amount: "$totalBillableAmount",
              user: "$_id",
              type: {$literal: STORAGE_DEBIT_TYPE}
            }
          }
        ])
        .cursor({batchSize: 100, async: true})
        .exec()
        .then(cursor => {
          console.log('batching storage debits...');
          const insertPromises = [];
          return new Promise((resolve, reject) => {
            cursor.on('data', (debit) => {
              console.log('storage debits received: ', debit);
              // insertPromises.push(storage.models.Debit.create(data));
              insertPromises.push(billingClient.postDebit(debit));
            });

            cursor.on('end', () => {
              console.log('storage cursor ended...');
              Promise.all(insertPromises).then(() => {
                console.log('storage debits done!');
                resolve(true)
              })
            })
          });
        });
  }
};

const calculateUserAmountOwedForBandwidth = function(storage) {
  return function(billingPeriodStart, billingPeriodEnd, perGbPrice) {
    const billingPeriodStartIsoDate = new Date(billingPeriodStart);
    const billingPeriodEndIsoDate = new Date(billingPeriodEnd);

    return storage.models.ExchangeReport
        .aggregate([
          {
            $match: {
              $and: [
                {
                  exchangeResultMessage: "SHARD_DOWNLOADED"
                },
                {
                  exchangeStart: {$gte: billingPeriodStartIsoDate, $lte: billingPeriodEndIsoDate}
                }
              ]
            }
          },
          {
            $project: {
              _id: 0,
              reporterId: 1,
              dataHash: 1
            }
          },
          {
            $lookup: {
              from: "pointers",
              localField: "dataHash",
              foreignField: "hash",
              as: "pointerData"
            }
          },
          {
            $unwind: {
              path: "$pointerData"
            }
          },
          {
            $lookup: {
              from: "publickeys",
              localField: "reporterId",
              foreignField: "_id",
              as: "publicKeyId"
            }
          },
          {
            $unwind: {
              path: "$publicKeyId"
            }
          },
          {
            $group: {
              _id: "$publicKeyId.user",
              size: {$sum: "$pointerData.size"}
            }
          },
          {
            $project: {
              _id: 0,
              amount: {
                $multiply: [perGbPrice, {$divide: ["$size", GB]}]
              },
              user: "$_id",
              type: {$literal: BANDWIDTH_DEBIT_TYPE}
            }
          }
        ])
        .cursor({batchSize: 500, async: true})
        .exec()
        .then(cursor => {
          console.log('batching bandwidth debits...');
          const insertPromises = [];
          return new Promise((resolve, reject) => {
            cursor.on('data', (debit) => {
              console.log('bandwidth debits received: ', debit);
              // insertPromises.push(storage.models.Debit.create(data));
              insertPromises.push(billingClient.postDebit(debit));
            });

            cursor.on('end', () => {
              console.log('bandwidth cursor ended...');
              Promise.all(insertPromises).then(() => {
                console.log('bandwidth debits done!');
                resolve(true)
              })
            })
          });
        });
  }
};

module.exports = function(storage) {
  return {
    forBandwidth: calculateUserAmountOwedForBandwidth(storage),
    forStorage: calculateUserAmountOwedForStorage(storage)
  }
};
<commit_msg>don't generate debits for free tier users<commit_after>const GB = 1000000000;
const MS_TO_HOUR = 1000 * 60 * 60;
const STORAGE_DEBIT_TYPE = "storage";
const BANDWIDTH_DEBIT_TYPE = "bandwidth";
const billingClient = require('../utils/billing-client');

// Need indexes on bucketentries.frame and pointers.hash
// Otherwise these will run slow as hell.

const calculateUserAmountOwedForStorage = function(storage) {
  return function(billingPeriodStart, billingPeriodEnd, gbHourPrice) {
    const billingPeriodStartIsoDate = new Date(billingPeriodStart);
    const billingPeriodEndIsoDate = new Date(billingPeriodEnd);

    return storage.models.Frame
        .aggregate([
          {
            $project: {
              _id: 1,
              user: 1,
              size: 1,
              created: 1
            }
          },
          {
            $lookup: {
              from: "bucketentries",
              localField: "_id",
              foreignField: "frame",
              as: "bucketEntry"
            }
          },
          {
            $unwind: {
              path: "$bucketEntry"
            }
          },
          {
            $project: {
              _id: 1,
              user: 1,
              gigabytes: {$divide: ["$size", GB]},
              fileCreatedTime: {
                $ifNull: ["$bucketEntry.created", "$created"]
              },
              fileRenewalTime: {
                $ifNull: ["$bucketEntry.renewal", billingPeriodEndIsoDate]
              }
            }
          },
          {
            $match: {
              $and: [
                {
                  "fileCreatedTime": {$lte: billingPeriodEndIsoDate}
                },
                {
                  "fileRenewalTime": {$gte: billingPeriodStartIsoDate}
                },
                {
                  "user.isFreeTier": {$eq: false}
                }
              ]
            }
          },
          {
            $project: {
              _id: 1,
              user: 1,
              fileBillableAmount: {
                $multiply: [
                  gbHourPrice,
                  "$gigabytes",
                  {
                    $divide: [
                      {
                        $add: [
                          1,
                          {
                            $subtract: [
                              {$min: ["$fileRenewalTime", billingPeriodEndIsoDate]},
                              {$max: ["$fileCreatedTime", billingPeriodStartIsoDate]}
                            ]
                          },
                        ]
                      },
                      MS_TO_HOUR
                    ]
                  }
                ]
              }
            }
          },
          {
            $group: {
              _id: "$user",
              totalBillableAmount: {$sum: "$fileBillableAmount"}
            }
          },
          {
            $project: {
              _id: 0,
              amount: "$totalBillableAmount",
              user: "$_id",
              type: {$literal: STORAGE_DEBIT_TYPE}
            }
          }
        ])
        .cursor({batchSize: 100, async: true})
        .exec()
        .then(cursor => {
          console.log('batching storage debits...');
          const insertPromises = [];
          return new Promise((resolve, reject) => {
            cursor.on('data', (debit) => {
              console.log('storage debits received: ', debit);
              // insertPromises.push(storage.models.Debit.create(data));
              insertPromises.push(billingClient.postDebit(debit));
            });

            cursor.on('end', () => {
              console.log('storage cursor ended...');
              Promise.all(insertPromises).then(() => {
                console.log('storage debits done!');
                resolve(true)
              })
            })
          });
        });
  }
};

const calculateUserAmountOwedForBandwidth = function(storage) {
  return function(billingPeriodStart, billingPeriodEnd, perGbPrice) {
    const billingPeriodStartIsoDate = new Date(billingPeriodStart);
    const billingPeriodEndIsoDate = new Date(billingPeriodEnd);

    return storage.models.ExchangeReport
        .aggregate([
          {
            $match: {
              $and: [
                {
                  exchangeResultMessage: "SHARD_DOWNLOADED"
                },
                {
                  exchangeStart: {$gte: billingPeriodStartIsoDate, $lte: billingPeriodEndIsoDate}
                }
              ]
            }
          },
          {
            $project: {
              _id: 0,
              reporterId: 1,
              dataHash: 1
            }
          },
          {
            $lookup: {
              from: "pointers",
              localField: "dataHash",
              foreignField: "hash",
              as: "pointerData"
            }
          },
          {
            $unwind: {
              path: "$pointerData"
            }
          },
          {
            $lookup: {
              from: "publickeys",
              localField: "reporterId",
              foreignField: "_id",
              as: "publicKeyId"
            }
          },
          {
            $unwind: {
              path: "$publicKeyId"
            }
          },
          {
            $group: {
              _id: "$publicKeyId.user",
              size: {$sum: "$pointerData.size"}
            }
          },
          {
            $project: {
              _id: 0,
              amount: {
                $multiply: [perGbPrice, {$divide: ["$size", GB]}]
              },
              user: "$_id",
              type: {$literal: BANDWIDTH_DEBIT_TYPE}
            }
          }
        ])
        .cursor({batchSize: 500, async: true})
        .exec()
        .then(cursor => {
          console.log('batching bandwidth debits...');
          const insertPromises = [];
          return new Promise((resolve, reject) => {
            cursor.on('data', (debit) => {
              console.log('bandwidth debits received: ', debit);
              // insertPromises.push(storage.models.Debit.create(data));
              insertPromises.push(billingClient.postDebit(debit));
            });

            cursor.on('end', () => {
              console.log('bandwidth cursor ended...');
              Promise.all(insertPromises).then(() => {
                console.log('bandwidth debits done!');
                resolve(true)
              })
            })
          });
        });
  }
};

module.exports = function(storage) {
  return {
    forBandwidth: calculateUserAmountOwedForBandwidth(storage),
    forStorage: calculateUserAmountOwedForStorage(storage)
  }
};
<|endoftext|>


<commit_before>"use strict";

class TimerBar extends HTMLElement {
  static get observedAttributes() { return [ "duration", "value", "elapsed", "hideafter", "lefttext", "centertext", "righttext", "width", "height", "bg", "fg", "style", "toward" ]; }

  // Background color.
  set bg(c) { this.setAttribute("bg", c); }
  get bg() { return this.getAttribute("bg"); }

  // Foreground color.
  set fg(c) { this.setAttribute("fg", c); }
  get fg() { return this.getAttribute("fg"); }

  // The width of the bar.
  set width(w) { this.setAttribute("width", w); }
  get width() { return this.getAttribute("width"); }

  // The height of the bar.
  set height(w) { this.setAttribute("height", w); }
  get height() { return this.getAttribute("height"); }

  // The total length of time to count down.
  set duration(s) { this.attributeChangedCallback("duration" , this.duration, s); }
  get duration() { return this._duration.toString(); }

  // The length remaining in the count down.
  set value(s) { this.attributeChangedCallback("value" , this.value, s); }
  get value() {
    if (!this._start) return this._duration.toString();
    var elapsedMs = new Date() - this._start;
    return Math.max(0, this._duration - (elapsedMs / 1000)).toString();
  }

  // The elapsed time.
  set elapsed(s) { this.attributeChangedCallback("elapsed" , this.elapsed, s); }
  get elapsed() {
    if (!this._start) return '0';
    return (new Date() - this._start).toString();
  }

  // If "right" then animates left-to-right (the default). If "left"
  // then animates right-to-left.
  set toward(t) { this.setAttribute("toward", t); }
  get toward() { return this.getAttribute("toward"); }

  // If "fill" then the progress goes empty-to-full, if "empty" then the
  // progress bar starts full and goes to empty.
  set style(s) { this.setAttribute("style", s); }
  get style() { return this.getAttribute("style"); }

  // When the bar reaches 0, it is hidden after this many seconds. If ""
  // then it is not hidden.
  set hideafter(h) { this.setAttribute("hideafter", h); }
  get hideafter() { return this.getAttribute("hideafter"); }

  // Chooses what should be shown in the text field in each area of
  // the bar. Can be one of:
  // empty - nothing is shown.
  // "remain" - shows the remaining time.
  // "duration" - shows the remaining and total duration time
  //              of the bar.
  // "percent" - shows the percentage of remaining time to
  //             the duration.
  // "elapsed"   - shows the elapsed time
  // anything else - the given text is shown literally.
  set lefttext(p) { this.setAttribute("lefttext", p); }
  get lefttext() { return this.getAttribute("lefttext"); }
  set righttext(p) { this.setAttribute("righttext", p); }
  get righttext() { return this.getAttribute("righttext"); }
  set centertext(p) { this.setAttribute("centertext", p); }
  get centertext() { return this.getAttribute("centertext"); }

  // This would be used with window.customElements.
  constructor() {
    super();
    var root = this.attachShadow({mode: 'open'});
    this.init(root);
  }

  // These would be used by document.registerElement, which is deprecated but
  // ACT uses an old CEF which has this instead of the newer APIs.
  createdCallback() {
    var root = this.createShadowRoot();
    this.init(root);
  }
  // Convert from the deprecated API names to the modern API names.
  attachedCallback() { this.connectedCallback(); }
  detachedCallback() { this.disconnectedCallback(); }

  init(root) {
    // Default values.
    this._duration = 0;
    this._width = '100%';
    this._height = '100%';
    this._bg = "black";
    this._fg = "yellow";
    this._toward_right = false;
    this._style_fill = false;
    this._left_text = "";
    this._center_text = "";
    this._right_text = "";
    this._hideafter = -1;

    root.innerHTML = `

    `
  }

  connectedCallback() {
    this.rootElement = this.shadowRoot.getElementById("root");
    this.foregroundElement = this.shadowRoot.getElementById("fg");
    this.backgroundElement = this.shadowRoot.getElementById("bg");
    this.leftTextElement = this.shadowRoot.getElementById("lefttext");
    this.centerTextElement = this.shadowRoot.getElementById("centertext");
    this.rightTextElement = this.shadowRoot.getElementById("righttext");

    this._connected = true;
    this.layout();
    this.updateText();
    if (this._start == null)
      this.setvalue(this._duration);
    else
      this.advance();
  }

  disconnectedCallback() {
    this._connected = false;
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name == "duration") {
      this._duration = Math.max(parseFloat(newValue), 0);
      this.setvalue(this._duration);
    } else if (name == "value") {
      this.setvalue(Math.max(parseFloat(newValue), 0));
    } else if (name == "elapsed") {
      this.setvalue(this._duration - Math.max(parseFloat(newValue), 0));
    } else if (name == "width") {
      this._width = newValue;
      this.layout();
    } else if (name == "height") {
      this._height = newValue;
      this.layout();
    } else if (name == "bg") {
      this._bg = newValue;
      this.layout();
    } else if (name == "fg") {
      this._fg = newValue;
      this.layout();
    } else if (name == "style") {
      this._style_fill = newValue == "fill";
      this.layout();
    } else if (name == "toward") {
      this._toward_right = newValue == "right";
      this.layout();
    } else if (name == "lefttext") {
      var update = newValue != this._left_text && this._connected;
      this._left_text = newValue;
      if (update)
        this.updateText();
    } else if (name == "centertext") {
      var update = newValue != this._center_text && this._connected;
      this._center_text = newValue;
      if (update)
        this.updateText();
    } else if (name == "righttext") {
      var update = newValue != this._right_text && this._connected;
      this._right_text = newValue;
      if (update)
        this.updateText();
    } else if (name == "hideafter") {
      this._hideafter = Math.max(parseFloat(this.hideafter), 0);
      if (this.value == '0') {
        if (this._hideafter >= 0)
          this.hide();
        else
          this.show();
      }
    }

    if (this._connected)
      this.draw();
  }

  layout() {
    if (!this._connected)
      return;

    this.backgroundElement.style.backgroundColor = this._bg;
    this.foregroundElement.style.backgroundColor = this._fg;
    this.rootElement.style.width = this._width;
    this.rootElement.style.height = this._height;

    // To start full and animate to empty, we animate backwards and flip
    // the direction.
    if (this._toward_right ^ this._style_fill)
      this.foregroundElement.style.transformOrigin = "100% 0%";
    else
      this.foregroundElement.style.transformOrigin = "0% 0%";
  }

  updateText() {
    var varying_texts = ["elapsed", "duration", "percent", "remain"];
    // These values are filled in during draw() when the values change.
    if (varying_texts.indexOf(this._left_text) == -1) {
      // Otherwise the value is fixed so it can be set here.
      this.leftTextElement.innerText = this._left_text;
    }
    if (varying_texts.indexOf(this._center_text) == -1) {
      this.centerTextElement.innerText = this._center_text;
    }
    if (varying_texts.indexOf(this._right_text) == -1) {
      this.rightTextElement.innerText = this._right_text;
    }
  }

  draw() {
    var elapsedSec = (new Date() - this._start) / 1000;
    var remainSec = Math.max(0, this._duration - elapsedSec);
    var percent = this._duration <= 0 ? 0 : remainSec / this._duration;
    // Keep it between 0 and 1.
    percent = Math.min(1, Math.max(0, percent));
    var display_remain = remainSec ? remainSec.toFixed(1) : ''; //parseInt(this._value + 0.99999999999);
    var display_elapsed = elapsedSec.toFixed(1);
    if (this._style_fill)
      percent = 1.0 - percent;
    this.foregroundElement.style.transform = "scale(" + percent + ",1)";
    if (this._left_text != "") {
      if (this._left_text == "remain")
        this.leftTextElement.innerHTML = display_remain;
      else if (this._left_text == "duration")
        this.leftTextElement.innerHTML = display_remain + " / " + this._duration;
      else if (this._left_text == "percent")
        this.leftTextElement.innerHTML = (percent * 100).toFixed(1) + " %";
      else if (this._left_text == "elapsed")
        this.leftTextElement.innerHTML = display_elapsed;
    }
    if (this._center_text != "") {
      if (this._center_text == "remain")
        this.centerTextElement.innerHTML = display_remain;
      else if (this._center_text == "duration")
        this.centerTextElement.innerHTML = display_remain + " / " + this._duration;
      else if (this._center_text == "percent")
        this.centerTextElement.innerHTML = (percent * 100).toFixed(1) + " %";
      else if (this._center_text == "elapsed")
        this.centerTextElement.innerHTML = display_elapsed;
    }
    if (this._right_text != "") {
      if (this._right_text == "remain")
        this.rightTextElement.innerHTML = display_remain;
      else if (this._right_text == "duration")
        this.rightTextElement.innerHTML = display_remain + " / " + this._duration;
      else if (this._right_text == "percent")
        this.rightTextElement.innerHTML = (percent * 100).toFixed(1) + " %";
      else if (this._right_text == "elapsed")
        this.rightTextElement.innerHTML = display_elapsed;
    }
  }

  setvalue(remainSec) {
    var elapsedSec = Math.max(0, this._duration - remainSec);
    this._start = new Date() - (elapsedSec * 1000);

    if (!this._connected) return;

    this.show();
    clearTimeout(this._hide_timer);
    this._hide_timer = null;

    this.advance();
  }

  advance() {
    var elapsedSec = (new Date() - this._start) / 1000;
    if (elapsedSec >= this._duration) {
      // Sets the attribute to 0 so users can see the counter is done, and
      // if they set the same duration again it will count.
      this._duration = 0;
      if (this._hideafter > 0)
        this._hide_timer = setTimeout(this.hide.bind(this), this._hideafter * 1000);
      else if (this._hideafter == 0)
        this.hide();

      window.cancelAnimationFrame(this._animationFrame);
      this._animationFrame = null;
    } else {
      this._animationFrame = window.requestAnimationFrame(this.advance.bind(this));
    }
    this.draw();
  }

  show() {
    if (this._connected)
      this.rootElement.style.display = "block";
  }

  hide() {
    if (this._connected)
      this.rootElement.style.display = "none";
  }
}

if (window.customElements) {
  // Preferred method but old CEF doesn't have this.
  window.customElements.define('timer-bar', TimerBar);
} else {
  document.registerElement('timer-bar', {
    prototype: Object.create(TimerBar.prototype)
  });
}
<commit_msg>Timerbar elapsed property should be seconds not milliseconds<commit_after>"use strict";

class TimerBar extends HTMLElement {
  static get observedAttributes() { return [ "duration", "value", "elapsed", "hideafter", "lefttext", "centertext", "righttext", "width", "height", "bg", "fg", "style", "toward" ]; }

  // Background color.
  set bg(c) { this.setAttribute("bg", c); }
  get bg() { return this.getAttribute("bg"); }

  // Foreground color.
  set fg(c) { this.setAttribute("fg", c); }
  get fg() { return this.getAttribute("fg"); }

  // The width of the bar.
  set width(w) { this.setAttribute("width", w); }
  get width() { return this.getAttribute("width"); }

  // The height of the bar.
  set height(w) { this.setAttribute("height", w); }
  get height() { return this.getAttribute("height"); }

  // The total length of time to count down.
  set duration(s) { this.attributeChangedCallback("duration" , this.duration, s); }
  get duration() { return this._duration.toString(); }

  // The length remaining in the count down.
  set value(s) { this.attributeChangedCallback("value" , this.value, s); }
  get value() {
    if (!this._start) return this._duration.toString();
    var elapsedMs = new Date() - this._start;
    return Math.max(0, this._duration - (elapsedMs / 1000)).toString();
  }

  // The elapsed time.
  set elapsed(s) { this.attributeChangedCallback("elapsed" , this.elapsed, s); }
  get elapsed() {
    if (!this._start) return '0';
    return ((new Date() - this._start / 1000)).toString();
  }

  // If "right" then animates left-to-right (the default). If "left"
  // then animates right-to-left.
  set toward(t) { this.setAttribute("toward", t); }
  get toward() { return this.getAttribute("toward"); }

  // If "fill" then the progress goes empty-to-full, if "empty" then the
  // progress bar starts full and goes to empty.
  set style(s) { this.setAttribute("style", s); }
  get style() { return this.getAttribute("style"); }

  // When the bar reaches 0, it is hidden after this many seconds. If ""
  // then it is not hidden.
  set hideafter(h) { this.setAttribute("hideafter", h); }
  get hideafter() { return this.getAttribute("hideafter"); }

  // Chooses what should be shown in the text field in each area of
  // the bar. Can be one of:
  // empty - nothing is shown.
  // "remain" - shows the remaining time.
  // "duration" - shows the remaining and total duration time
  //              of the bar.
  // "percent" - shows the percentage of remaining time to
  //             the duration.
  // "elapsed"   - shows the elapsed time
  // anything else - the given text is shown literally.
  set lefttext(p) { this.setAttribute("lefttext", p); }
  get lefttext() { return this.getAttribute("lefttext"); }
  set righttext(p) { this.setAttribute("righttext", p); }
  get righttext() { return this.getAttribute("righttext"); }
  set centertext(p) { this.setAttribute("centertext", p); }
  get centertext() { return this.getAttribute("centertext"); }

  // This would be used with window.customElements.
  constructor() {
    super();
    var root = this.attachShadow({mode: 'open'});
    this.init(root);
  }

  // These would be used by document.registerElement, which is deprecated but
  // ACT uses an old CEF which has this instead of the newer APIs.
  createdCallback() {
    var root = this.createShadowRoot();
    this.init(root);
  }
  // Convert from the deprecated API names to the modern API names.
  attachedCallback() { this.connectedCallback(); }
  detachedCallback() { this.disconnectedCallback(); }

  init(root) {
    // Default values.
    this._duration = 0;
    this._width = '100%';
    this._height = '100%';
    this._bg = "black";
    this._fg = "yellow";
    this._toward_right = false;
    this._style_fill = false;
    this._left_text = "";
    this._center_text = "";
    this._right_text = "";
    this._hideafter = -1;

    root.innerHTML = `

    `
  }

  connectedCallback() {
    this.rootElement = this.shadowRoot.getElementById("root");
    this.foregroundElement = this.shadowRoot.getElementById("fg");
    this.backgroundElement = this.shadowRoot.getElementById("bg");
    this.leftTextElement = this.shadowRoot.getElementById("lefttext");
    this.centerTextElement = this.shadowRoot.getElementById("centertext");
    this.rightTextElement = this.shadowRoot.getElementById("righttext");

    this._connected = true;
    this.layout();
    this.updateText();
    if (this._start == null)
      this.setvalue(this._duration);
    else
      this.advance();
  }

  disconnectedCallback() {
    this._connected = false;
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name == "duration") {
      this._duration = Math.max(parseFloat(newValue), 0);
      this.setvalue(this._duration);
    } else if (name == "value") {
      this.setvalue(Math.max(parseFloat(newValue), 0));
    } else if (name == "elapsed") {
      this.setvalue(this._duration - Math.max(parseFloat(newValue), 0));
    } else if (name == "width") {
      this._width = newValue;
      this.layout();
    } else if (name == "height") {
      this._height = newValue;
      this.layout();
    } else if (name == "bg") {
      this._bg = newValue;
      this.layout();
    } else if (name == "fg") {
      this._fg = newValue;
      this.layout();
    } else if (name == "style") {
      this._style_fill = newValue == "fill";
      this.layout();
    } else if (name == "toward") {
      this._toward_right = newValue == "right";
      this.layout();
    } else if (name == "lefttext") {
      var update = newValue != this._left_text && this._connected;
      this._left_text = newValue;
      if (update)
        this.updateText();
    } else if (name == "centertext") {
      var update = newValue != this._center_text && this._connected;
      this._center_text = newValue;
      if (update)
        this.updateText();
    } else if (name == "righttext") {
      var update = newValue != this._right_text && this._connected;
      this._right_text = newValue;
      if (update)
        this.updateText();
    } else if (name == "hideafter") {
      this._hideafter = Math.max(parseFloat(this.hideafter), 0);
      if (this.value == '0') {
        if (this._hideafter >= 0)
          this.hide();
        else
          this.show();
      }
    }

    if (this._connected)
      this.draw();
  }

  layout() {
    if (!this._connected)
      return;

    this.backgroundElement.style.backgroundColor = this._bg;
    this.foregroundElement.style.backgroundColor = this._fg;
    this.rootElement.style.width = this._width;
    this.rootElement.style.height = this._height;

    // To start full and animate to empty, we animate backwards and flip
    // the direction.
    if (this._toward_right ^ this._style_fill)
      this.foregroundElement.style.transformOrigin = "100% 0%";
    else
      this.foregroundElement.style.transformOrigin = "0% 0%";
  }

  updateText() {
    var varying_texts = ["elapsed", "duration", "percent", "remain"];
    // These values are filled in during draw() when the values change.
    if (varying_texts.indexOf(this._left_text) == -1) {
      // Otherwise the value is fixed so it can be set here.
      this.leftTextElement.innerText = this._left_text;
    }
    if (varying_texts.indexOf(this._center_text) == -1) {
      this.centerTextElement.innerText = this._center_text;
    }
    if (varying_texts.indexOf(this._right_text) == -1) {
      this.rightTextElement.innerText = this._right_text;
    }
  }

  draw() {
    var elapsedSec = (new Date() - this._start) / 1000;
    var remainSec = Math.max(0, this._duration - elapsedSec);
    var percent = this._duration <= 0 ? 0 : remainSec / this._duration;
    // Keep it between 0 and 1.
    percent = Math.min(1, Math.max(0, percent));
    var display_remain = remainSec ? remainSec.toFixed(1) : ''; //parseInt(this._value + 0.99999999999);
    var display_elapsed = elapsedSec.toFixed(1);
    if (this._style_fill)
      percent = 1.0 - percent;
    this.foregroundElement.style.transform = "scale(" + percent + ",1)";
    if (this._left_text != "") {
      if (this._left_text == "remain")
        this.leftTextElement.innerHTML = display_remain;
      else if (this._left_text == "duration")
        this.leftTextElement.innerHTML = display_remain + " / " + this._duration;
      else if (this._left_text == "percent")
        this.leftTextElement.innerHTML = (percent * 100).toFixed(1) + " %";
      else if (this._left_text == "elapsed")
        this.leftTextElement.innerHTML = display_elapsed;
    }
    if (this._center_text != "") {
      if (this._center_text == "remain")
        this.centerTextElement.innerHTML = display_remain;
      else if (this._center_text == "duration")
        this.centerTextElement.innerHTML = display_remain + " / " + this._duration;
      else if (this._center_text == "percent")
        this.centerTextElement.innerHTML = (percent * 100).toFixed(1) + " %";
      else if (this._center_text == "elapsed")
        this.centerTextElement.innerHTML = display_elapsed;
    }
    if (this._right_text != "") {
      if (this._right_text == "remain")
        this.rightTextElement.innerHTML = display_remain;
      else if (this._right_text == "duration")
        this.rightTextElement.innerHTML = display_remain + " / " + this._duration;
      else if (this._right_text == "percent")
        this.rightTextElement.innerHTML = (percent * 100).toFixed(1) + " %";
      else if (this._right_text == "elapsed")
        this.rightTextElement.innerHTML = display_elapsed;
    }
  }

  setvalue(remainSec) {
    var elapsedSec = Math.max(0, this._duration - remainSec);
    this._start = new Date() - (elapsedSec * 1000);

    if (!this._connected) return;

    this.show();
    clearTimeout(this._hide_timer);
    this._hide_timer = null;

    this.advance();
  }

  advance() {
    var elapsedSec = (new Date() - this._start) / 1000;
    if (elapsedSec >= this._duration) {
      // Sets the attribute to 0 so users can see the counter is done, and
      // if they set the same duration again it will count.
      this._duration = 0;
      if (this._hideafter > 0)
        this._hide_timer = setTimeout(this.hide.bind(this), this._hideafter * 1000);
      else if (this._hideafter == 0)
        this.hide();

      window.cancelAnimationFrame(this._animationFrame);
      this._animationFrame = null;
    } else {
      this._animationFrame = window.requestAnimationFrame(this.advance.bind(this));
    }
    this.draw();
  }

  show() {
    if (this._connected)
      this.rootElement.style.display = "block";
  }

  hide() {
    if (this._connected)
      this.rootElement.style.display = "none";
  }
}

if (window.customElements) {
  // Preferred method but old CEF doesn't have this.
  window.customElements.define('timer-bar', TimerBar);
} else {
  document.registerElement('timer-bar', {
    prototype: Object.create(TimerBar.prototype)
  });
}
<|endoftext|>


<commit_before>var validator = (function () {
    var validator = {};

    Object.defineProperty(validator, 'validateBoolean', {
        value: function (value, valueName) {
            if (typeof value !== 'boolean') {
                throw new TypeError(valueName + ' must be a boolean');
            }
        }
    });

    Object.defineProperty(validator, 'validateIfNumber', {
        value: function (value, valueName) {
            if (typeof value !== 'number') {
                throw new Error(valueName + ' must be a number');
            }
        }
    });

    //I am not sure for this :(
    Object.defineProperty(validator, 'validateIfHexColor', {
        value: function (value, valueName) {
            var checkHexFormat  = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/;
            if (checkHexFormat.(value) == false) {
                throw new Error(valueName + ' must be a HEX color');
            }
        }
    });

    return validator;
}());<commit_msg>Refactor validator.js<commit_after>var validator = (function () {
    var validator = {},
        defaultValueName = 'Value';

    function isRealNumber(number) {
        return !isNaN(parseFloat(number)) && isFinite(number);
    }

    Object.defineProperty(validator, 'validateNotNullAndUndefined', {
        value: function (value, valueName) {
            valueName = valueName || defaultValueName;

            if (value == undefined) {
                throw new TypeError(valueName + ' cannot be null and undefined.');
            }
        }
    });

    Object.defineProperty(validator, 'validateIfBoolean', {
        value: function (value, valueName) {
            valueName = valueName || defaultValueName;

            if (typeof value !== 'boolean') {
                throw new TypeError(valueName + ' must be a boolean');
            }
        }
    });

    Object.defineProperty(validator, 'validateIfRealNumber', {
        value: function (value, valueName) {
            valueName = valueName || defaultValueName;

            if (isRealNumber(value) === false) {
                throw new TypeError(valueName + ' must be a real number');
            }
        }
    });

    Object.defineProperty(validator, 'validateIfString', {
        value: function (value, valueName) {
            valueName = valueName || defaultValueName;

            if (typeof value !== 'string') {
                throw new Error(valueName + ' must be a string');
            }
        }
    });

    return validator;
}());<|endoftext|>


<commit_before>define(['knockout'] function(ko) {
	var ViewModel = function(model) {
		console.log(model);
	}
	return ViewModel;
});<commit_msg>missing comma<commit_after>define(['knockout'], function(ko) {
	var ViewModel = function(model) {
		console.log(model);
	}
	return ViewModel;
});<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const keys = require("../core/key");
const CustomXUL = require("../core/custom");

const { Windows } = require("../util/windows");
const { Overlays } = require("../core/overlay");

var _ = require("sdk/l10n").get;
const extensionPrefs = require("sdk/simple-prefs");

const GROUPS = {
		"Application": ["key_quitApplication", "key_openHelpMac", "key_openHelp", "key_preferencesCmdMac", "key_hideThisAppCmdMac", "key_hideOtherAppsCmdMac"],
        "Navigation": ["goBackKb", "goBackKb2", "goForwardKb", "goForwardKb2", "goHome", "openFileKb", "key_reload", "key_reload2", "key_forceReload", /*"Ctrl+F5", Ctrl+Shift+R,*/ "key_stop", "key_stop_mac", "focusURLBar", "focusURLBar2"],

        "CurrentPage": [ /*"End", "F6", "Shift+F6",*/ "goHome", "key_viewInfo", "key_viewSource", "printKb", "key_savePage", "key_fullZoomEnlarge", "key_fullZoomReduce", "key_fullZoomReset", "key_fullScreen", "key_fullScreen_old", "key_minimizeWindow", "key_toggleMute","key_switchTextDirection"],

        "Editing": ["key_copy", "key_cut", "key_delete", "key_paste", "key_redo", "key_selectAll", "key_undo"],

        "Search": ["key_find", "key_findAgain", "key_findPrevious", "key_search", "key_search2", "key_findSelection"],

        "WindowsTabs": ["key_close", "key_closeWindow", "key_closeOther", "key_newNavigatorTab", "key_newNavigator", "key_undoCloseTab", "key_undoCloseWindow", "key_selectTab1", "key_selectTab2", "key_selectTab3", "key_selectTab4", "key_selectTab5", "key_selectTab6", "key_selectTab7", "key_selectTab8", "key_selectLastTab", "key_tabview", "key_nextTabGroup", "key_previousTabGroup", "key_prevTab", "key_nextTab","key_showAllTabs"],

        "BookmarksHistory": ["addBookmarkAsKb", "viewBookmarksSidebarKb", "viewBookmarksSidebarWinKb", "manBookmarkKb", "bookmarkAllTabsKb", "key_gotoHistory", "showAllHistoryKb"],

        "Tools": ["key_openDownloads", "key_openAddons", "key_errorConsole", "key_webConsole", "key_privatebrowsing", "key_sanitize", "key_sanitize_mac", "key_webide"],

        "DeveloperTools": [
    "key_webconsole", "key_errorConsole", "key_jsdebugger", "key_inspector",
    "key_styleeditor", "key_jsprofiler", "key_devToolbar", "key_responsiveUI",
    "key_scratchpad"],
    	"CustomXUL": []
       };

const LABELS = {
        "focusURLBar": _("focusURLBar_label"),
        "focusURLBar2": _("focusURLBar2_label"),
        "key_search": _("key_search_label"),
        "key_search2": _("key_search2_label"),
        "key_stop": _("key_stop_label"),
        "key_stop_mac": _("key_stop_mac_label"),
        "key_reload": _("key_reload_label"),
        "key_reload2": _("key_reload2_label"),
        "key_forceReload": _("key_forceReload_label"),
        "key_forceReload2": _("key_forceReload2_label"),
        "goHome": _("goHome_label"),
        "goBackKb": _("goBackKb_label"),
        "goBackKb2": _("goBackKb2_label"),
        "goForwardKb": _("goForwardKb_label"),
        "goForwardKb2": _("goForwardKb2_label"),
        "key_close": _("key_close_label"),
        "key_undoCloseTab": _("key_undoCloseTab_label"),
        "key_undoCloseWindow": _("key_undoCloseWindow_label"),
        "key_toggleAddonBar": _("key_toggleAddonBar_label"),
        "key_findPrevious": _("key_findPrevious_label"),
        "key_selectLastTab": _("key_selectLastTab_label"),
        "key_closeOther": _("key_closeOther_label"),
        "key_prevTab": _("key_prevTab_label"),
        "key_nextTab": _("key_nextTab_label"),
        "key_selectTab1": _("key_selectTab1_label"),
        "key_selectTab2": _("key_selectTab2_label"),
        "key_selectTab3": _("key_selectTab3_label"),
        "key_selectTab4": _("key_selectTab4_label"),
        "key_selectTab5": _("key_selectTab5_label"),
        "key_selectTab6": _("key_selectTab6_label"),
        "key_selectTab7": _("key_selectTab7_label"),
        "key_selectTab8": _("key_selectTab8_label"),
        "key_tabview": _("key_tabview_label"),
        "key_nextTabGroup": _("key_nextTabGroup_label"),
        "key_previousTabGroup": _("key_previousTabGroup_label"),
        "key_toggleMute": _("key_toggleMute_label"),
        "key_sanitize_mac": _("key_sanitize_mac_label"),
        "key_devToolboxMenuItemF12": _("key_devToolboxMenuItemF12_label"),
        "key_devToolboxMenuItem": _("key_devToolboxMenuItem_label"),
        "key_fullScreen_old": _("key_fullScreen_old_label"),
        "key_minimizeWindow": _("key_minimizeWindow_label"),
		"key_switchTextDirection": _("key_switchTextDirection_label"),
		"key_toggleMute": _("key_toggleMute_label"),
		"key_findSelection": _("key_findSelection_label"),
		"key_webide": _("key_webide_label"),
		"focusChatBar": _("focusChatBar_label"),
        "key_firebug_toggleBreakOn": _("key_firebug_toggleBreakOn_label"),
        "key_firebug_detachFirebug": _("key_firebug_detachFirebug_label"),
        "key_firebug_closeFirebug": _("key_firebug_closeFirebug_label"),
        "key_firebug_focusCommandLine": _("key_firebug_focusCommandLine_label"),
        "key_firebug_toggleInspecting": _("key_firebug_toggleInspecting_label"),
        "key_foxyproxyquickadd": _("key_foxyproxyquickadd_label"),
        "key_changeproxy": _("key_changeproxy_label"),
        "secureLoginShortcut": _("secureLoginShortcut_label")
    };

function findKeyLabel(key) {
	const { Windows } = require("../util/windows");
	let label = key.getLabel();

	if (label) {
		return label;
	}

    let id = key.id;
    if (id in LABELS) {
    return LABELS[id];
    }

        // try to find a menuitem
        let menuitem = Windows.querySelector(`menuitem[key="${id}"][label]`);
return menuitem ? menuitem.getAttribute("label") : id;
    }

let TreeView = exports.TreeView = function (term) {
	function filter(key) {

		return findKeyLabel(key).toLowerCase().includes(term.toLowerCase()) ||
		key.shortcut.toString().toLowerCase().includes(term.toLowerCase());
	}
	this._buildGroups(term ? keys.filter(filter) : keys.all());
	this._buildRows();
	};

TreeView.prototype = {
    _buildGroups: function(map) {
        this.groups = [];

        for (let [gname, gkeys] of keys.group(map, GROUPS, "Other")) {
           	let lname = String.concat(gname,"_group");
			let group = {type: "group", name: _(lname), parentIdx: -1, open: true, keys: gkeys};
            this.groups.push(group);
        }
    },

    _buildRows: function() {
        this.rows = [];

        for (let group of this.groups) {
            let parentIdx = this.rows.push(group) - 1;

            if (group.open) {
				for (let key of group.keys) {
                    this.rows.push({type: "key", key: key, parentIdx: parentIdx});
                }
            }
        }
    },

	get rowCount() { return this.rows.length },

    isContainer: function(idx) {
        return ("group" == this.rows[idx].type);
    },

    isEditable: function(idx, column) {
        return column.index && !this.isContainer(idx);
    },

    isContainerOpen: function(idx) {
        return this.rows[idx].open;
    },

    getLevel: function(idx) {
        return +!this.isContainer(idx);
    },

    getParentIndex: function(idx) {
        return this.rows[idx].parentIdx;
    },

    toggleOpenState: function(idx) {
        let row = this.rows[idx];

        let numRows = -row.keys.length;
        if (row.open = !row.open) { numRows *= -1; }

        this._buildRows();
        this.treebox.rowCountChanged(idx + 1, numRows);
        this.treebox.invalidateRow(idx);
    },

    hasNextSibling: function(idx, after) {
        let level = this.getLevel(idx);
        for (let t = after + 1; t < this.rowCount; t++) {
            let nextLevel = this.getLevel(t);
            if (nextLevel == level) { return true; }
            if (nextLevel < level) { return false; }
        }
    },

    getCellText: function(idx, column) {
        let row = this.rows[idx];
        if (this.isContainer(idx)) { return (column.index ? "" : row.name); }

        let key = row.key;
        if (!column.index) { return findKeyLabel(key); }

        let overlay = Overlays.findByKey(key);
        return (overlay ? overlay.shortcut : key.shortcut).toString();
    },

    getCellValue: function(idx, column) {
        if (!this.isContainer(idx)) { return this.rows[idx].key.toString(); }
    },

    getCellProperties: function(idx, column) {
        if (this.isContainer(idx)) { return; }

        if (!column.index) { return; }

        let props = [];
        let key = this.rows[idx].key;
        if (Overlays.findByKey(key)) {
            props.push("custom");
            let toStringCache = Overlays.findByKey(key).shortcut.toString();

            if (Overlays.findByConflictingShortcut(toStringCache)) {
                let groupColor = Overlays.getConflictingShortcutColor(toStringCache);
                props.push("maketext" + groupColor);
            }
        }
        if ((Overlays.findByCustomShortcut(key.shortcut)) && (!Overlays.findByOverriddenShortcut(key.shortcut)) && (!Overlays.findByDisabledKey(key))) // Display as overridden only if using default shortcut and not disabled.
        props.push("overridden");

        if (Overlays.findByDisabledKey(key)) { props.push("disabled"); }

        return props.join(" ");
    },

    setTree: function(treebox) {
        this.treebox = treebox;
    },

    isContainerEmpty: function() { return false },
    setCellText: function() { },
    isSeparator: function() { return false },
    isSorted: function() { return false },
    getImageSrc: function() {},
    getRowProperties: function() {},
    getColumnProperties: function() {}
};

let customXULTreeView = exports.customXULTreeView = function (term, find = false, tree = null, filtered = false, newXul = false) {

	function findNewRow(row) {
		return row.key == this;
	}

	function filter(key) {
		return key.getKey().toLowerCase().includes(term.toLowerCase()) || key.getLabel().toLowerCase().includes(term.toLowerCase()) ||
		key.getCommand().toLowerCase().includes(term.toLowerCase())
	}

	if (!find) { this._buildRows(term ? CustomXUL.filter(filter) : CustomXUL.allCustomKeys()); }

	else {
	this._buildRows(!!filtered ? CustomXUL.filter(filter) : CustomXUL.allCustomKeys());
	let toSelect = this.rows.find(findNewRow,term);
	toSelect = this.rows.indexOf(toSelect);
	Windows.getMostRecentWindow().setTimeout(function () {
	tree.view.selection.adjustSelection((toSelect -1),1);
	tree.view.selection.select(toSelect);
	if (!!newXul) { tree.startEditing(tree.view.selection.currentIndex,tree.columns.getNamedColumn("KeybinderCustomXUL_NameCol")); }
	},0);
	}
};

customXULTreeView.prototype = {
    _buildRows: function(map) {
        this.rows = [];
		let sortedKeys = new Map([...map.entries()].sort((a,b) => a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0));
	    for (let [mapKey, mapValue] of sortedKeys.entries()) {
	    this.rows.push({type:"customXULKey", key:mapValue.key, label:mapValue.label, command:mapValue.command});
	    }
    },

	get rowCount() { return this.rows.length },

    isContainer: function(idx) {
        return ("group" == this.rows[idx].type);
    },

    performActionOnRow: function(action, idx) {
    if ("select" == action) { this.treebox.view.selection.select(idx) }
    },

    isEditable: function(idx, column) {
        return column.index && !this.isContainer(idx);
    },

    isSelectable: function(idx, column) {
        return column.index && !this.isContainer(idx);
    },

    isContainerOpen: function(idx) {
        return this.rows[idx].open;
    },

    getLevel: function(idx) {
        return +!this.isContainer(idx);
    },

    getParentIndex: function(idx) {
        return this.rows[idx].parentIdx;
    },

    toggleOpenState: function(idx) {
		let row = this.rows[idx];

		let numRows = -row.keys.length;
		if (row.open = !row.open) { numRows *= -1; }

		this._buildRows();
		this.treebox.rowCountChanged(idx + 1, numRows);
		this.treebox.invalidateRow(idx);
    },

    hasNextSibling: function(idx, after) {},

    getCellText: function(idx, column) {
        let row = this.rows[idx];
        if (!row.key || !CustomXUL.find(row.key)) { return }
		if ("KeybinderCustomXUL_NameCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).key }
		if ("KeybinderCustomXUL_LabelCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).label }
		if ("KeybinderCustomXUL_ActionCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).command }
    },

    getCellValue: function(idx, column) {
        if (!this.isContainer(idx)) { return this.rows[idx].key.toString(); }
    },

    getCellProperties: function(idx, column) {},

    setTree: function(treebox) {
        this.treebox = treebox;
    },

    isContainerEmpty: function() { return false },
    setCellText: function() { },
    isSeparator: function() { return false },
    isSorted: function() { return false },
    getImageSrc: function() {},
    getRowProperties: function() {},
    getColumnProperties: function() {}
};
<commit_msg>Couple changes to key group definitions.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const keys = require("../core/key");
const CustomXUL = require("../core/custom");

const { Windows } = require("../util/windows");
const { Overlays } = require("../core/overlay");

var _ = require("sdk/l10n").get;
const extensionPrefs = require("sdk/simple-prefs");

const GROUPS = {
		"Application": ["key_quitApplication", "key_openHelpMac", "key_openHelp", "key_preferencesCmdMac", "key_hideThisAppCmdMac", "key_hideOtherAppsCmdMac"],
        "Navigation": ["goBackKb", "goBackKb2", "goForwardKb", "goForwardKb2", "goHome", "openFileKb", "key_reload", "key_reload2", "key_forceReload", /*"Ctrl+F5", Ctrl+Shift+R,*/ "key_stop", "key_stop_mac", "focusURLBar", "focusURLBar2"],

        "CurrentPage": [ /*"End", "F6", "Shift+F6",*/ "goHome", "key_viewInfo", "key_viewSource", "printKb", "key_savePage", "key_fullZoomEnlarge", "key_fullZoomReduce", "key_fullZoomReset", "key_fullScreen", "key_fullScreen_old", "key_minimizeWindow", "key_toggleMute","key_switchTextDirection"],

        "Editing": ["key_copy", "key_cut", "key_delete", "key_paste", "key_redo", "key_selectAll", "key_undo"],

        "Search": ["key_find", "key_findAgain", "key_findPrevious", "key_search", "key_search2", "key_findSelection"],

        "WindowsTabs": ["key_close", "key_closeWindow", "key_closeOther", "key_newNavigatorTab", "key_newNavigator", "key_undoCloseTab", "key_undoCloseWindow", "key_selectTab1", "key_selectTab2", "key_selectTab3", "key_selectTab4", "key_selectTab5", "key_selectTab6", "key_selectTab7", "key_selectTab8", "key_selectLastTab", "key_tabview", "key_nextTabGroup", "key_previousTabGroup", "key_prevTab", "key_nextTab","key_showAllTabs"],

        "BookmarksHistory": ["addBookmarkAsKb", "viewBookmarksSidebarKb", "viewBookmarksSidebarWinKb", "manBookmarkKb", "bookmarkAllTabsKb", "key_gotoHistory", "showAllHistoryKb"],

        "Tools": ["key_openDownloads", "key_openAddons", "key_errorConsole", "key_webConsole", "key_privatebrowsing", "key_sanitize", "key_sanitize_mac"],

        "DeveloperTools": [
    "key_webconsole", "key_errorConsole", "key_jsdebugger", "key_inspector",
    "key_styleeditor", "key_jsprofiler", "key_devToolbar", "key_responsiveUI",
    "key_scratchpad", "key_webide"],
    	"CustomXUL": []
       };

const LABELS = {
        "focusURLBar": _("focusURLBar_label"),
        "focusURLBar2": _("focusURLBar2_label"),
        "key_search": _("key_search_label"),
        "key_search2": _("key_search2_label"),
        "key_stop": _("key_stop_label"),
        "key_stop_mac": _("key_stop_mac_label"),
        "key_reload": _("key_reload_label"),
        "key_reload2": _("key_reload2_label"),
        "key_forceReload": _("key_forceReload_label"),
        "key_forceReload2": _("key_forceReload2_label"),
        "goHome": _("goHome_label"),
        "goBackKb": _("goBackKb_label"),
        "goBackKb2": _("goBackKb2_label"),
        "goForwardKb": _("goForwardKb_label"),
        "goForwardKb2": _("goForwardKb2_label"),
        "key_close": _("key_close_label"),
        "key_undoCloseTab": _("key_undoCloseTab_label"),
        "key_undoCloseWindow": _("key_undoCloseWindow_label"),
        "key_toggleAddonBar": _("key_toggleAddonBar_label"),
        "key_findPrevious": _("key_findPrevious_label"),
        "key_selectLastTab": _("key_selectLastTab_label"),
        "key_closeOther": _("key_closeOther_label"),
        "key_prevTab": _("key_prevTab_label"),
        "key_nextTab": _("key_nextTab_label"),
        "key_selectTab1": _("key_selectTab1_label"),
        "key_selectTab2": _("key_selectTab2_label"),
        "key_selectTab3": _("key_selectTab3_label"),
        "key_selectTab4": _("key_selectTab4_label"),
        "key_selectTab5": _("key_selectTab5_label"),
        "key_selectTab6": _("key_selectTab6_label"),
        "key_selectTab7": _("key_selectTab7_label"),
        "key_selectTab8": _("key_selectTab8_label"),
        "key_tabview": _("key_tabview_label"),
        "key_nextTabGroup": _("key_nextTabGroup_label"),
        "key_previousTabGroup": _("key_previousTabGroup_label"),
        "key_toggleMute": _("key_toggleMute_label"),
        "key_sanitize_mac": _("key_sanitize_mac_label"),
        "key_devToolboxMenuItemF12": _("key_devToolboxMenuItemF12_label"),
        "key_devToolboxMenuItem": _("key_devToolboxMenuItem_label"),
        "key_fullScreen_old": _("key_fullScreen_old_label"),
        "key_minimizeWindow": _("key_minimizeWindow_label"),
		"key_switchTextDirection": _("key_switchTextDirection_label"),
		"key_toggleMute": _("key_toggleMute_label"),
		"key_findSelection": _("key_findSelection_label"),
		"key_webide": _("key_webide_label"),
		"focusChatBar": _("focusChatBar_label"),
        "key_firebug_toggleBreakOn": _("key_firebug_toggleBreakOn_label"),
        "key_firebug_detachFirebug": _("key_firebug_detachFirebug_label"),
        "key_firebug_closeFirebug": _("key_firebug_closeFirebug_label"),
        "key_firebug_focusCommandLine": _("key_firebug_focusCommandLine_label"),
        "key_firebug_toggleInspecting": _("key_firebug_toggleInspecting_label"),
        "key_foxyproxyquickadd": _("key_foxyproxyquickadd_label"),
        "key_changeproxy": _("key_changeproxy_label"),
        "secureLoginShortcut": _("secureLoginShortcut_label")
    };

function findKeyLabel(key) {
	const { Windows } = require("../util/windows");
	let label = key.getLabel();

	if (label) {
		return label;
	}

    let id = key.id;
    if (id in LABELS) {
    return LABELS[id];
    }

        // try to find a menuitem
        let menuitem = Windows.querySelector(`menuitem[key="${id}"][label]`);
return menuitem ? menuitem.getAttribute("label") : id;
    }

let TreeView = exports.TreeView = function (term) {
	function filter(key) {
		return findKeyLabel(key).toLowerCase().includes(term.toLowerCase()) ||
		key.shortcut.toString().toLowerCase().includes(term.toLowerCase());
	}
	this._buildGroups(term ? keys.filter(filter) : keys.all());
	this._buildRows();
	};

TreeView.prototype = {
    _buildGroups: function(map) {
        this.groups = [];

        for (let [gname, gkeys] of keys.group(map, GROUPS, "Other")) {
           	let lname = String.concat(gname,"_group");
			let group = {type: "group", name: _(lname), parentIdx: -1, open: true, keys: gkeys};
            this.groups.push(group);
        }
    },

    _buildRows: function() {
        this.rows = [];

        for (let group of this.groups) {
            let parentIdx = this.rows.push(group) - 1;

            if (group.open) {
				for (let key of group.keys) {
                    this.rows.push({type: "key", key: key, parentIdx: parentIdx});
                }
            }
        }
    },

	get rowCount() { return this.rows.length },

    isContainer: function(idx) {
        return ("group" == this.rows[idx].type);
    },

    isEditable: function(idx, column) {
        return column.index && !this.isContainer(idx);
    },

    isContainerOpen: function(idx) {
        return this.rows[idx].open;
    },

    getLevel: function(idx) {
        return +!this.isContainer(idx);
    },

    getParentIndex: function(idx) {
        return this.rows[idx].parentIdx;
    },

    toggleOpenState: function(idx) {
        let row = this.rows[idx];

        let numRows = -row.keys.length;
        if (row.open = !row.open) { numRows *= -1; }

        this._buildRows();
        this.treebox.rowCountChanged(idx + 1, numRows);
        this.treebox.invalidateRow(idx);
    },

    hasNextSibling: function(idx, after) {
        let level = this.getLevel(idx);
        for (let t = after + 1; t < this.rowCount; t++) {
            let nextLevel = this.getLevel(t);
            if (nextLevel == level) { return true; }
            if (nextLevel < level) { return false; }
        }
    },

    getCellText: function(idx, column) {
        let row = this.rows[idx];
        if (this.isContainer(idx)) { return (column.index ? "" : row.name); }

        let key = row.key;
        if (!column.index) { return findKeyLabel(key); }

        let overlay = Overlays.findByKey(key);
        return (overlay ? overlay.shortcut : key.shortcut).toString();
    },

    getCellValue: function(idx, column) {
        if (!this.isContainer(idx)) { return this.rows[idx].key.toString(); }
    },

    getCellProperties: function(idx, column) {
        if (this.isContainer(idx)) { return; }

        if (!column.index) { return; }

        let props = [];
        let key = this.rows[idx].key;
        if (Overlays.findByKey(key)) {
            props.push("custom");
            let toStringCache = Overlays.findByKey(key).shortcut.toString();

            if (Overlays.findByConflictingShortcut(toStringCache)) {
                let groupColor = Overlays.getConflictingShortcutColor(toStringCache);
                props.push("maketext" + groupColor);
            }
        }
        if ((Overlays.findByCustomShortcut(key.shortcut)) && (!Overlays.findByOverriddenShortcut(key.shortcut)) && (!Overlays.findByDisabledKey(key))) // Display as overridden only if using default shortcut and not disabled.
        props.push("overridden");

        if (Overlays.findByDisabledKey(key)) { props.push("disabled"); }

        return props.join(" ");
    },

    setTree: function(treebox) {
        this.treebox = treebox;
    },

    isContainerEmpty: function() { return false },
    setCellText: function() { },
    isSeparator: function() { return false },
    isSorted: function() { return false },
    getImageSrc: function() {},
    getRowProperties: function() {},
    getColumnProperties: function() {}
};

let customXULTreeView = exports.customXULTreeView = function (term, find = false, tree = null, filtered = false, newXul = false) {

	function findNewRow(row) {
		return row.key == this;
	}

	function filter(key) {
		return key.getKey().toLowerCase().includes(term.toLowerCase()) || key.getLabel().toLowerCase().includes(term.toLowerCase()) ||
		key.getCommand().toLowerCase().includes(term.toLowerCase())
	}

	if (!find) { this._buildRows(term ? CustomXUL.filter(filter) : CustomXUL.allCustomKeys()); }

	else {
	this._buildRows(!!filtered ? CustomXUL.filter(filter) : CustomXUL.allCustomKeys());
	let toSelect = this.rows.find(findNewRow,term);
	toSelect = this.rows.indexOf(toSelect);
	Windows.getMostRecentWindow().setTimeout(function () {
	tree.view.selection.adjustSelection((toSelect -1),1);
	tree.view.selection.select(toSelect);
	if (!!newXul) { tree.startEditing(tree.view.selection.currentIndex,tree.columns.getNamedColumn("KeybinderCustomXUL_NameCol")); }
	},0);
	}
};

customXULTreeView.prototype = {
    _buildRows: function(map) {
        this.rows = [];
		let sortedKeys = new Map([...map.entries()].sort((a,b) => a[0] > b[0] ? 1 : a[0] < b[0] ? -1 : 0));
	    for (let [mapKey, mapValue] of sortedKeys.entries()) {
	    this.rows.push({type:"customXULKey", key:mapValue.key, label:mapValue.label, command:mapValue.command});
	    }
    },

	get rowCount() { return this.rows.length },

    isContainer: function(idx) {
        return ("group" == this.rows[idx].type);
    },

    performActionOnRow: function(action, idx) {
    if ("select" == action) { this.treebox.view.selection.select(idx) }
    },

    isEditable: function(idx, column) {
        return column.index && !this.isContainer(idx);
    },

    isSelectable: function(idx, column) {
        return column.index && !this.isContainer(idx);
    },

    isContainerOpen: function(idx) {
        return this.rows[idx].open;
    },

    getLevel: function(idx) {
        return +!this.isContainer(idx);
    },

    getParentIndex: function(idx) {
        return this.rows[idx].parentIdx;
    },

    toggleOpenState: function(idx) {
		let row = this.rows[idx];

		let numRows = -row.keys.length;
		if (row.open = !row.open) { numRows *= -1; }

		this._buildRows();
		this.treebox.rowCountChanged(idx + 1, numRows);
		this.treebox.invalidateRow(idx);
    },

    hasNextSibling: function(idx, after) {},

    getCellText: function(idx, column) {
        let row = this.rows[idx];
        if (!row.key || !CustomXUL.find(row.key)) { return }
		if ("KeybinderCustomXUL_NameCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).key }
		if ("KeybinderCustomXUL_LabelCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).label }
		if ("KeybinderCustomXUL_ActionCol" == column.id) { return CustomXUL.allCustomKeys().get(row.key).command }
    },

    getCellValue: function(idx, column) {
        if (!this.isContainer(idx)) { return this.rows[idx].key.toString(); }
    },

    getCellProperties: function(idx, column) {},

    setTree: function(treebox) {
        this.treebox = treebox;
    },

    isContainerEmpty: function() { return false },
    setCellText: function() { },
    isSeparator: function() { return false },
    isSorted: function() { return false },
    getImageSrc: function() {},
    getRowProperties: function() {},
    getColumnProperties: function() {}
};
<|endoftext|>


<commit_before>'use strict'
const props = require('../render/static').property
const getParent = require('../render/dom/parent')
const vstamp = require('vigour-stamp')

exports.types = {
  group: {
    type: 'property',
    isGroup: true,
    subscriptionType: 1,
    properties: {
      render (val) {
        this.define({ groupRender: { val } })
      }
    },
    define: {
      render: {
        val: {
          static (target, pnode) {
            const store = {}
            const parsed = '_' + target.key + 'StaticParsed'
            if (pnode) {
              props(target, pnode, store)
              pnode[parsed] = true
            }
            target.groupRender.static(target, pnode, store)
          },
          state (target, s, type, stamp, subs, tree, id, pid, order, store) {
            let storeId = pid + target.key
            if (!store) { store = tree._[storeId] || (tree._[storeId] = {}) }
            const pnode = getParent(type, stamp, subs, tree, pid)
            if (pnode) {
              const parsed = '_' + target.key + 'StaticParsed'
              if (!pnode[parsed]) {
                // only need that key for this
                props(target, pnode, store)
                pnode[parsed] = true
              }
              if (!('stamp' in store)) {
                store.stamp = stamp
                vstamp.on(stamp, () => {
                  delete store.stamp
                  target.groupRender.state(target, s, type, stamp, subs, tree, id, pid, store)
                })
              }
            }
          }
        }
      }
    },
    child: {
      define: {
        getStore (tree, id) {
          if (this.$ !== true) {
            let length = this.$.length
            if (this.$switch || this.$any || this.$test) {
              length = length - 1
            }
            while (length) {
              if (!/^\$pass|\$current/.test(tree._key)) {
                length--
              }
              tree = tree._p
            }
          }
          const _ = tree._ || (tree._ = {})
          const store = _[id] || (_[id] = {})
          return store
        }
      },
      render: {
        static (target, node, store) {
          store[target.key] = target.compute()
        },
        state (target, s, type, stamp, subs, tree, id, pid, order) {
          const parent = target._parent
          const store = target.getStore(tree, pid + parent.key)
          if (s.val === null) {
            if (target.key in store) {
              delete store[target.key]
            }
          } else {
            store[target.key] = target.compute(s)
          }
          parent.render.state(parent, s, type, stamp, subs, tree, id, pid, order, store)
        }
      }
    }
  }
}
<commit_msg>added deep context fix for group (bit slow)<commit_after>'use strict'
const props = require('../render/static').property
const getParent = require('../render/dom/parent')
const vstamp = require('vigour-stamp')

exports.types = {
  group: {
    type: 'property',
    isGroup: true,
    subscriptionType: 1,
    properties: {
      render (val) {
        this.define({ groupRender: { val } })
      }
    },
    define: {
      render: {
        val: {
          static (target, pnode) {
            const store = {}
            const parsed = '_' + target.key + 'StaticParsed'
            if (pnode) {
              props(target, pnode, store)
              pnode[parsed] = true
            }
            target.groupRender.static(target, pnode, store)
          },
          state (target, s, type, stamp, subs, tree, id, pid, order, store) {
            let storeId = pid + target.key
            if (!store) { store = tree._[storeId] || (tree._[storeId] = {}) }
            const pnode = getParent(type, stamp, subs, tree, pid)
            if (pnode) {
              const parsed = '_' + target.key + 'StaticParsed'
              if (!pnode[parsed]) {
                // only need that key for this
                props(target, pnode, store)
                pnode[parsed] = true
              }
              if (!('stamp' in store)) {
                store.stamp = stamp
                vstamp.on(stamp, () => {
                  delete store.stamp
                  target.groupRender.state(target, s, type, stamp, subs, tree, id, pid, store)
                })
              }
            }
          }
        }
      }
    },
    child: {
      define: {
        getStore (tree, id) {
          if (this.$ !== true) {
            let length = this.$.length
            if (this.$switch || this.$any || this.$test) {
              length = length - 1
            }
            while (length) {
              if (!/^\$pass|\$current/.test(tree._key)) {
                length--
              }
              tree = tree._p
            }
          }
          const _ = tree._ || (tree._ = {})
          const store = _[id] || (_[id] = {})
          return store
        }
      },
      render: {
        static (target, node, store) {
          store[target.key] = target.compute()
        },
        state (target, s, type, stamp, subs, tree, id, pid, order) {
          var w = subs
          var p
          const key = target._parent.key
          const store = target.getStore(tree, pid + key)
          while (w && !p) {
            let _ = w._
            if (_) {
              p = w._.t && _.t[pid] || _.s && _.s[pid]
              w = _.p
            }
          }
          p = p[key]
          if (s.val === null) {
            if (target.key in store) {
              delete store[target.key]
            }
          } else {
            store[target.key] = target.compute(s)
          }
          p.render.state(p, s, type, stamp, subs, tree, id, pid, order, store)
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>/*jslint node : true, nomen: true, plusplus: true, vars: true, eqeq: true,*/
"use strict";
var url = require('url'),
    httpProxy = require('http-proxy');

/**
 * local fixture middleware
 */
module.exports = function (opts) {

    var proxy = httpProxy.createProxyServer({target : opts.url});

    return function handler(req, res, next) {
        /*proxy.on('error', function (e) {
            next(e);
        });*/
        proxy.web(req, res, function (err) {
            if (err) {
                console.log(err);
            }
        });
    };
};
<commit_msg>do not try to write upstream error<commit_after>/*jslint node : true, nomen: true, plusplus: true, vars: true, eqeq: true,*/
"use strict";
var url = require('url'),
    httpProxy = require('http-proxy');

/**
 * local fixture middleware
 */
module.exports = function (opts) {

    var proxy = httpProxy.createProxyServer({target : opts.url});

    return function handler(req, res, next) {
        /*proxy.on('error', function (e) {
            next(e);
        });*/
        proxy.web(req, res, function (err) {
            if (err) {
                console.log(err);
            }
            next();
        });
    };
};
<|endoftext|>