<commit_before>var _ = require('underscore'),
    sys = require('sys'),
    Step = require('step'),
    EventEmitter = require('events').EventEmitter,
    Server = require('./server'),
    SphericalMercator = require('./sphericalmercator');

// Batch tile generation class.
//
// Required `options` keys:
// - `filepath` {String} filepath of generated batch.
// - `datasource` {String|Object} datasource for the tile, one of the
//   acceptable datasource formats accepted by the `Tile` class.
// - `storage` {Object} storage backend.
// - `renderer` {Object} render backend.
//
// Optional:
// - `bbox` {Array} bounding box coordinates in the order `[w, s, e, n]`.
//    Defaults to [-180,-90,180,90].
// - `srs` {String} SRS code of `bbox`. Either `900913` or `WGS84`.
// - `minzoom` {Number} minimum zoom level to render (inclusive). Default: 0.
// - `maxzoom` {Number} maximum zoom level to render (inclusive). Defualt: 4.
// - `interactivity` {Object} interactivity options to be passed through
//   to `Format.grid.render()` as `tile.format_options` (see `format.js`).
// - `batchsize` {Number} number of images/grids to render at a time
// - `metadata` {Object} hash of metadata to be written to the mbtiles database
var Batch = function(options) {
    if (!options) throw new Error('`options` is required.');
    if (!options.datasource) throw new Error('`options.datasource` is required.');
    if (!options.storage) throw new Error('`options.storage` is required.');
    if (!options.renderer) throw new Error('`options.renderer` is required.');

    _(options).defaults({
        bbox: [-180,-90,180,90],
        srs: 'WGS84',
        batchsize: 100,
        minzoom: 0,
        maxzoom: 4,
        metadata: {},
        serve: {}
    });
    _(options.serve).defaults({ format: 'png' });

    this.options = options;
    this.storage = options.storage;
    this.server = new Server(options.renderer);

    // validate formatter so that wax does not fail down the line
    try {
        eval('this.f = ' + this.options.metadata.formatter);
    } catch (e) {
        throw new Error('The metadata.formatter you specified is not valid javascript: \n' +
            this.options.metadata.formatter);
    }

    SphericalMercator.call(this, this.options);

    // Vars needed for tile calculation/generation.
    this.grid = !!this.options.metadata.formatter;
    this.minzoom = this.options.minzoom;
    this.maxzoom = this.options.maxzoom;
    this.tilesCurrent = 0;
    this.tilesTotal = 0;

    // Precalculate the tile int bounds for each zoom level.
    this.zoomBounds = [];
    for (var z = this.minzoom; z <= this.maxzoom; z++) {
        this.zoomBounds[z] = this.xyz(this.options.bbox, z, true, this.options.srs);
        this.tilesTotal += (this.zoomBounds[z].maxX -
                             this.zoomBounds[z].minX + 1) *
                            (this.zoomBounds[z].maxY -
                             this.zoomBounds[z].minY + 1);
    }

    // Add bounds to metadata if not provided.
    if (!this.options.metadata.bounds) {
        this.options.metadata.bounds = this.options.srs === '900913'
            ? this.convert(this.options.bbox, 'WGS84')
            : this.options.bbox;
        this.options.metadata.bounds = this.options.metadata.bounds.join(',');
    }
};

sys.inherits(Batch, SphericalMercator, EventEmitter);

Batch.prototype.execute = function(callback) {
    var batch = this,
        storage = this.storage,
        resource;
    Step(
        function() {
            var next = this;
            storage.pool(batch.options.filepath).create(function(res) {
                resource = res;
                next();
            });
        },
        function(err) {
            if (err) throw err;
            storage.store('setup', resource, {}, this);
        },
        function(err) {
            if (err) throw err;
            batch.render(err, resource, this);
        },
        // this.features,
        // this.metadata,
        function(err) {
            if (err) throw err;
            storage.store('finish', resource, {}, this);
        },
        function(err) {
            if (err) throw err;
            storage.pool(batch.options.filepath).destroy(resource);
            this();
        },
        callback
    );
};

// Insert grid feature data for all features on the interactivity layer.
// Requires `this.options.interactivity` to be defined.
Batch.prototype.features = function(err, callback) {
    // This function is meaningless without interactivity settings
    if (!this.grid) {
        callback('Interactivity must be supported to add grid data');
        return;
    }

    // @TODO: batch.renderer.features() << get features from the server.
    var that = this;
    var map;
    Step(
        // Acquire a raw map object
        function() {
            Pool.acquire('map', that.options.datasource, that.options, this);
        },
        function(err, res) {
            if (err) throw err;

            map = res;

            var lyr = map.mapnik.get_layer(that.options.interactivity.layer);
            var featureset = lyr.datasource.featureset();
            var feat;
            var group = this.group();
            while (feat = featureset.next()) {
                that.mbtiles.insertGridData(feat,
                        that.options.interactivity.key_name,
                        group(err));
            }
        },
        function(err) {
            Pool.release('map', that.options.datasource, map);
            callback(err);
        }
    );
};

// Render an image/grid batch of size `this.options.batchsize` and insert into
// the MBTiles. `callback(err, tiles)` should call `renderChunk()` again to
// render the next batch of tiles. If `tiles` is false, there are no batches
// left to be rendered.
Batch.prototype.render = function(err, resource, callback) {
    var tiles = this.next(this.options.batchsize);
    var batch = this;

    if (err) return callback(err);
    if (!tiles) return callback(null);

    // @TODO emit events.
    process.nextTick(function() {
        Step(
            function() {
                var group = this.group();
                for (var i = 0; i < tiles.length; i++) {
                    var options = _.extend(batch.options.serve, {
                        datasource: batch.options.datasource,
                        xyz: [tiles[i][1], tiles[i][2], tiles[i][0]]
                    });
                    batch.server.serve(options, group());
                }
            },
            function(err, renders) {
                if (err) throw err;
                batch.storage.store(
                    'tiles',
                    resource,
                    _(renders).map(function(r, i) {
                        return { tile: tiles[i], data: r[0] };
                    }),
                    this
                );
            },
            function(err) {
                if (err) throw err;
                if (!batch.grid) return this();
                var group = this.group();
                for (var i = 0; i < tiles.length; i++) {
                    var options = _.extend(this.options.serve, {
                        datasource: batch.options.datasource,
                        xyz: [tiles[i][1], tiles[i][2], tiles[i][0]],
                        format: 'grid.json'
                    });
                    batch.server.serve(options, group());
                }
            },
            function(err, renders) {
                if (err) throw err;
                if (!batch.grid) return this();
                batch.storage.store(
                    'grids',
                    resource,
                    _(renders).map(function(r, i) {
                        return { tile: tiles[i], data: r[0] };
                    }),
                    this
                );
            },
            function(err) {
                batch.render(err, resource, callback);
            }
        );
    });
};

// Retrieve an array of tiles that should be rendered next. Returns `false` if
// there are no more tiles to be generated.
Batch.prototype.next = function(count) {
    var count = count || 1;
    var triplets = [];

    this.curZ = (typeof this.curZ === 'undefined') ? this.minzoom : this.curZ;
    for (var z = this.curZ; z <= this.maxzoom; z++) {
        this.curX = (typeof this.curX === 'undefined') ?
            this.zoomBounds[z].minX : this.curX;
        this.curY = (typeof this.curY === 'undefined') ?
            this.zoomBounds[z].minY : this.curY;

        for (var x = this.curX; x <= this.zoomBounds[z].maxX; x++) {
            for (var y = this.curY; y <= this.zoomBounds[z].maxY; y++) {
                this.curX = x;
                this.curY = y;
                this.curZ = z;
                if (triplets.length < count) {
                    triplets.push([z, x, y]);
                    this.tilesCurrent++;
                } else if (triplets.length === count) {
                    return triplets;
                }
            }
            // End of row, reset Y cursor.
            this.curY = this.zoomBounds[z].minY;
        }

        // End of the zoom layer, pass through and reset XY cursors.
        delete this.curX;
        delete this.curY;
    }
    // We're done, set our cursor outside usable bounds.
    this.curZ = this.maxzoom + 1;
    this.curX = this.zoomBounds[this.maxzoom].maxX + 1;
    this.curY = this.zoomBounds[this.maxzoom].maxY + 1;
    if (!triplets.length) {
        return false;
    }
    return triplets;
};

module.exports = Batch;
<commit_msg>Update batch for new serve options.<commit_after>var _ = require('underscore'),
    sys = require('sys'),
    Step = require('step'),
    EventEmitter = require('events').EventEmitter,
    Server = require('./server'),
    SphericalMercator = require('./sphericalmercator');

// Batch tile generation class.
//
// Required `options` keys:
// - `filepath` {String} filepath of generated batch.
// - `datasource` {String|Object} datasource for the tile, one of the
//   acceptable datasource formats accepted by the `Tile` class.
// - `storage` {Object} storage backend.
// - `renderer` {Object} render backend.
//
// Optional:
// - `batchsize` {Number} number of images/grids to render at a time
// - `bbox` {Array} bounding box coordinates in the order `[w, s, e, n]`.
//    Defaults to [-180,-90,180,90].
// - `srs` {String} SRS code of `bbox`. Either `900913` or `WGS84`.
// - `minzoom` {Number} minimum zoom level to render (inclusive). Default: 0.
// - `maxzoom` {Number} maximum zoom level to render (inclusive). Defualt: 4.
// - `metadata` {Object} hash of metadata to be written to the mbtiles database
// - `serve` {Object} hash of options passed to the renderer `serve` method.
var Batch = function(options) {
    if (!options) throw new Error('`options` is required.');
    if (!options.datasource) throw new Error('`options.datasource` is required.');
    if (!options.storage) throw new Error('`options.storage` is required.');
    if (!options.renderer) throw new Error('`options.renderer` is required.');

    _(options).defaults({
        batchsize: 100,
        bbox: [-180,-90,180,90],
        srs: 'WGS84',
        minzoom: 0,
        maxzoom: 4,
        metadata: {},
        serve: {}
    });
    _(options.serve).defaults({ format: 'png' });

    this.options = options;
    this.storage = options.storage;
    this.server = new Server(options.renderer);

    SphericalMercator.call(this, options);

    // Vars needed for tile calculation/generation.
    this.grid = !!this.options.metadata.formatter;
    this.minzoom = this.options.minzoom;
    this.maxzoom = this.options.maxzoom;
    this.tilesCurrent = 0;
    this.tilesTotal = 0;

    // Precalculate the tile int bounds for each zoom level.
    this.zoomBounds = [];
    for (var z = this.minzoom; z <= this.maxzoom; z++) {
        this.zoomBounds[z] = this.xyz(this.options.bbox, z, true, this.options.srs);
        this.tilesTotal += (this.zoomBounds[z].maxX -
                             this.zoomBounds[z].minX + 1) *
                            (this.zoomBounds[z].maxY -
                             this.zoomBounds[z].minY + 1);
    }

    // Add bounds to metadata if not provided.
    if (!this.options.metadata.bounds) {
        this.options.metadata.bounds = this.options.srs === '900913'
            ? this.convert(this.options.bbox, 'WGS84')
            : this.options.bbox;
        this.options.metadata.bounds = this.options.metadata.bounds.join(',');
    }

    // Validate formatter so that wax does not fail down the line.
    try {
        eval('this.f = ' + this.options.metadata.formatter);
    } catch (e) {
        throw new Error('`metadata.formatter` is not valid javascript: \n' +
            this.options.metadata.formatter);
    }
};

sys.inherits(Batch, SphericalMercator, EventEmitter);

// Execute the batch. Calls `callback(err)` upon completion or error.
Batch.prototype.execute = function(callback) {
    var batch = this,
        storage = this.storage,
        resource;
    Step(
        function() {
            var next = this;
            storage.pool(batch.options.filepath).create(function(res) {
                resource = res;
                next();
            });
        },
        function(err) {
            if (err) throw err;
            storage.store('setup', resource, {}, this);
        },
        function(err) {
            if (err) throw err;
            batch.render(err, resource, this);
        },
        // @TODO
        // this.features,
        // this.metadata,
        function(err) {
            if (err) throw err;
            storage.store('finish', resource, {}, this);
        },
        function(err) {
            if (err) throw err;
            storage.pool(batch.options.filepath).destroy(resource);
            this();
        },
        callback
    );
};

// Insert grid feature data for all features on the interactivity layer.
// Requires `this.grid` to be defined.
Batch.prototype.features = function(err, callback) {
    if (!this.grid) {
        callback(new Error('Interactivity must be supported to add grid data'));
        return;
    }

    // @TODO: batch.renderer.features() << get features from the server.
    var that = this;
    var map;
    Step(
        // Acquire a raw map object
        function() {
            Pool.acquire('map', that.options.datasource, that.options, this);
        },
        function(err, res) {
            if (err) throw err;

            map = res;

            var lyr = map.mapnik.get_layer(that.options.interactivity.layer);
            var featureset = lyr.datasource.featureset();
            var feat;
            var group = this.group();
            while (feat = featureset.next()) {
                that.mbtiles.insertGridData(feat,
                        that.options.interactivity.key_name,
                        group(err));
            }
        },
        function(err) {
            Pool.release('map', that.options.datasource, map);
            callback(err);
        }
    );
};

// Render an image/grid batch of size `this.options.batchsize` and insert into
// the MBTiles. `callback(err, tiles)` should call `renderChunk()` again to
// render the next batch of tiles. If `tiles` is false, there are no batches
// left to be rendered.
Batch.prototype.render = function(err, resource, callback) {
    var tiles = this.next(this.options.batchsize);
    var batch = this;

    if (err) return callback(err);
    if (!tiles) return callback(null);

    // @TODO emit events.
    process.nextTick(function() {
        Step(
            function() {
                var group = this.group();
                for (var i = 0; i < tiles.length; i++) {
                    var options = _({}).extend(batch.options.serve, tiles[i], {
                        datasource: batch.options.datasource
                    });
                    batch.server.serve(options, group());
                }
            },
            function(err, renders) {
                if (err) throw err;
                batch.storage.store(
                    'tiles',
                    resource,
                    _(renders).map(function(r, i) {
                        return { tile: tiles[i], data: r[0] };
                    }),
                    this
                );
            },
            function(err) {
                if (err) throw err;
                if (!batch.grid) return this();
                var group = this.group();
                for (var i = 0; i < tiles.length; i++) {
                    var options = _({}).extend(batch.options.serve, tiles[i], {
                        datasource: batch.options.datasource,
                        format: 'grid.json'
                    });
                    batch.server.serve(options, group());
                }
            },
            function(err, renders) {
                if (err) throw err;
                if (!batch.grid) return this();
                batch.storage.store(
                    'grids',
                    resource,
                    _(renders).map(function(r, i) {
                        return { tile: tiles[i], data: r[0] };
                    }),
                    this
                );
            },
            function(err) {
                batch.render(err, resource, callback);
            }
        );
    });
};

// Retrieve an array of tiles that should be rendered next. Returns `false` if
// there are no more tiles to be generated.
Batch.prototype.next = function(count) {
    var count = count || 1;
    var triplets = [];

    this.curZ = (typeof this.curZ === 'undefined') ? this.minzoom : this.curZ;
    for (var z = this.curZ; z <= this.maxzoom; z++) {
        this.curX = (typeof this.curX === 'undefined') ?
            this.zoomBounds[z].minX : this.curX;
        this.curY = (typeof this.curY === 'undefined') ?
            this.zoomBounds[z].minY : this.curY;

        for (var x = this.curX; x <= this.zoomBounds[z].maxX; x++) {
            for (var y = this.curY; y <= this.zoomBounds[z].maxY; y++) {
                this.curX = x;
                this.curY = y;
                this.curZ = z;
                if (triplets.length < count) {
                    triplets.push({x: x, y: y, z: z});
                    this.tilesCurrent++;
                } else if (triplets.length === count) {
                    return triplets;
                }
            }
            // End of row, reset Y cursor.
            this.curY = this.zoomBounds[z].minY;
        }

        // End of the zoom layer, pass through and reset XY cursors.
        delete this.curX;
        delete this.curY;
    }
    // We're done, set our cursor outside usable bounds.
    this.curZ = this.maxzoom + 1;
    this.curX = this.zoomBounds[this.maxzoom].maxX + 1;
    this.curY = this.zoomBounds[this.maxzoom].maxY + 1;
    return triplets.length ? triplets : false;
};

module.exports = Batch;
<|endoftext|>


<commit_before>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */

var express = require('express');
var app = express();
var q = require('q');
var url = require('url');
require('bufferjs');
var _ = require('underscore');
var ntread = require('./nt/read').read;

var Firebase = require('./firebase-node');
var FirebaseTokenGenerator = require("./firebase-token-generator-node.js");

if(!process.env.FIREBASE_SECRET) {
    console.error('FIREBASE_SECRET not available in the current environment');
    process.exit(1);
}

var tokenGenerator = new FirebaseTokenGenerator(process.env.FIREBASE_SECRET);
var token = tokenGenerator.createToken({}, {
    admin: true
});
var firebase = new Firebase('https://featuredcontent.firebaseio.com/');

var getTorrentReadStream = function(torrent) {
    var ret = new q.defer();
    var torrentStream = torrent.createReadStream();
    var buffers = [];
    torrentStream.addListener('data', function(buffer) {
        buffers.push(buffer);
    });
    torrentStream.addListener('end', function() {
        ret.resolve(Buffer.concat(buffers));
    });
    return ret.promise;
}

app.get('/:link.torrent', function(req, res) {
    var link = req.params.link;
    firebase.child('torrent').child(link).once('value', function(valueSnapshot) {
        var value = valueSnapshot.val();
        var torrent = value.torrent;
        var tracker = value.tracker;
        console.log('torrent', torrent);
        console.log('tracker', tracker);
        ntread(torrent, function(err, torrent) {
            // maybe not a valid torrent file?
            if(err) {
                console.log('unable to read torrent file', err);
                res.writeHead(400);
                res.end();
                return;
            }

            // set the main tracker to our tracker
            if(torrent.metadata.hasOwnProperty('announce-list')) {
                torrent.metadata['announce-list'].push([tracker]);
            } else {
                torrent.metadata['announce-list'] = [[tracker], [torrent.metadata.announce]];
            }

            // now create a read stream and dump it into our response stream
            var torrentReadStreamRequest = getTorrentReadStream(torrent);
            torrentReadStreamRequest.done(function(data) {
                res.writeHead(200, {
                    'Content-Type': 'application/x-bittorrent',
                    'Content-Disposition': 'inline; filename="' + torrent.metadata.info.name + '.torrent"'
                });
                res.end(data);
            });
            torrentReadStreamRequest.fail(function() {
                console.log('unable to get torrent file stream');
                res.writeHead(400);
                res.end();
                return;
            });
        });
    });
});

var port = process.env.PORT || 5000;
var onAuth = function(error, dummy) {
    if(!error) {
        console.log('firebase login success');
        app.listen(port, function() {
            console.log('Listening on ' + port);
        });
    } else {
        console.log('firebase login failure');
        process.exit(1);
    }
};
firebase.auth(token, onAuth);<commit_msg>adding support for custom file names and re-enabling on the fly editing via url parameters<commit_after>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */

var express = require('express');
var app = express();
var q = require('q');
var URL = require('url');
require('bufferjs');
var _ = require('underscore');
var ntread = require('./nt/read').readURL;

var Firebase = require('./firebase-node');
var FirebaseTokenGenerator = require("./firebase-token-generator-node.js");

if(!process.env.FIREBASE_SECRET) {
    console.error('FIREBASE_SECRET not available in the current environment');
    process.exit(1);
}

var tokenGenerator = new FirebaseTokenGenerator(process.env.FIREBASE_SECRET);
var token = tokenGenerator.createToken({}, {
    admin: true
});
var firebase = new Firebase('https://featuredcontent.firebaseio.com/');

var getTorrentReadStream = function(torrent) {
    var ret = new q.defer();
    var torrentStream = torrent.createReadStream();
    var buffers = [];
    torrentStream.addListener('data', function(buffer) {
        buffers.push(buffer);
    });
    torrentStream.addListener('end', function() {
        ret.resolve(Buffer.concat(buffers));
    });
    return ret.promise;
}

var serveEditedTorrentFile = function(res, torrent, tracker) {
    console.log('serving torrent with tracker');
    console.log('torrent', torrent);
    console.log('tracker', tracker);

    ntread(torrent, function(err, torrent) {
        // maybe not a valid torrent file?
        if(err) {
            console.log('unable to read torrent file', err);
            res.writeHead(400);
            res.end();
            return;
        }

        // set the main tracker to our tracker
        if(torrent.metadata.hasOwnProperty('announce-list')) {
            torrent.metadata['announce-list'].push([tracker]);
        } else {
            torrent.metadata['announce-list'] = [[tracker], [torrent.metadata.announce]];
        }

        // now create a read stream and dump it into our response stream
        var torrentReadStreamRequest = getTorrentReadStream(torrent);
        torrentReadStreamRequest.done(function(data) {
            res.writeHead(200, {
                'Content-Type': 'application/x-bittorrent',
                'Content-Disposition': 'inline; filename="' + torrent.metadata.info.name + '.torrent"'
            });
            res.end(data);
        });
        torrentReadStreamRequest.fail(function() {
            console.log('unable to get torrent file stream');
            res.writeHead(400);
            res.end();
            return;
        });
    });
};

var handleParameterRequest = function(req, res) {
    var query = URL.parse(req.url, true).query;
    if(!query.hasOwnProperty('torrent')) {
        res.writeHead(400);
        res.end('no torrent specified');
        return;
    }
    if(!query.hasOwnProperty('tracker')) {
        res.writeHead(400);
        res.end('no tracker specified');
        return;
    }
    serveEditedTorrentFile(res, query['torrent'], query['tracker']);
};

var handleShortenedTorrentLink = function(req, res) {
    var link = req.params.link;
    firebase.child('torrent').child(link).once('value', function(valueSnapshot) {
        var value = valueSnapshot.val();
        var torrent = value.torrent;
        var tracker = value.tracker;
        console.log('torrent', torrent);
        console.log('tracker', tracker);

        serveEditedTorrentFile(res, torrent, tracker);
    });
};

//support the case where someone wants to name their torrent
app.get('/:link/:name.torrent', handleShortenedTorrentLink);
//support the random link name
app.get('/:link.torrent', handleShortenedTorrentLink);
//support the case where someone wants to specify the torrent and tracker themselves
app.get('/', handleParameterRequest);

var port = process.env.PORT || 5000;
var onAuth = function(error, dummy) {
    if(!error) {
        console.log('firebase login success');
        app.listen(port, function() {
            console.log('Listening on ' + port);
        });
    } else {
        console.log('firebase login failure');
        process.exit(1);
    }
};
firebase.auth(token, onAuth);<|endoftext|>


<commit_before>
/**
 * socket.io
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

(function (exports, io) {

  /**
   * Expose constructor.
   *
   * @api public
   */

  exports.XHR = XHR;

  /**
   * XHR constructor
   *
   * @costructor
   * @api public
   */

  function XHR (socket) {
    if (!socket) return;

    io.Transport.apply(this, arguments);
    this.sendBuffer = [];
  };

  /**
   * Inherits from Transport.
   */

  io.util.inherit(XHR, io.Transport);

  /**
   * Establish a connection
   *
   * @returns {Transport}
   * @api public
   */

  XHR.prototype.open = function () {
    this.get();
    this.onOpen();

    // we need to make sure the request succeeds since we have no indication
    // whether the request opened or not until it succeeded.
    this.setCloseTimeout();

    return this;
  };

  /**
   * Check if we need to send data to the Socket.IO server, if we have data in our
   * buffer we encode it and forward it to the `post` method.
   *
   * @api private
   */

  XHR.prototype.checkSend = function () {
    if (!this.posting && this.sendBuffer.length) {
      var encoded = io.parser.encodePayload(this.sendBuffer);
      this.sendBuffer = [];
      this.post(encoded);
    }
  };

  /**
   * Send data to the Socket.IO server.
   *
   * @param data The message
   * @returns {Transport}
   * @api public
   */

  XHR.prototype.send = function (data) {
    if (io.util.isArray(data)) {
      this.sendBuffer.push.apply(this.sendBuffer, data);
    } else {
      this.sendBuffer.push(data);
    }

    this.checkSend();

    return this;
  };

  /**
   * Posts a encoded message to the Socket.IO server.
   *
   * @param {String} data A encoded message.
   * @api private
   */

  function empty () { };

  XHR.prototype.post = function (data) {
    var self = this;
    this.posting = true;

    function stateChange () {
      if (this.readyState == 4) {
        this.onreadystatechange = empty;
        self.posting = false;

        if (this.status == 200){
          self.checkSend();
        } else {
          self.onClose();
        }
      }
    }

    function onload () {
      this.onload = empty;
      self.posting = false;
      self.checkSend();
    };

    this.sendXHR = this.request('POST');

    if (window.XDomainRequest && this.sendXHR instanceof XDomainRequest) {
      this.sendXHR.onload = this.sendXHR.onerror = onload;
    } else {
      this.sendXHR.onreadystatechange = stateChange;
    }

    this.sendXHR.send(data);
  };

  /**
   * Handle the disconnect request.
   *
   * @api private
   */

  XHR.prototype.onClose = function(){
    if (this.xhr){
      this.xhr.onreadystatechange = this.xhr.onload = empty;
      try {
        this.xhr.abort();
      } catch(e){}
      this.xhr = null;
    }

    if (this.sendXHR){
      this.sendXHR.onreadystatechange = this.sendXHR.onload = empty;
      try {
        this.sendXHR.abort();
      } catch(e){}
      this.sendXHR = null;
    }

    this.sendBuffer = [];

    io.Transport.prototype.onClose.call(this);
  };

  /**
   * Generates a configured XHR request
   *
   * @param {String} url The url that needs to be requested.
   * @param {String} method The method the request should use.
   * @returns {XMLHttpRequest}
   * @api private
   */

  XHR.prototype.request = function (method) {
    var req = io.util.request(this.socket.isXDomain());
    req.open(method || 'GET', this.prepareUrl() + '?t' + (+ new Date));

    if (method == 'POST') {
      if (req.setRequestHeader) {
        req.setRequestHeader('Content-type', 'text/plain');
      } else {
        // XDomainRequest
        try {
          req.contentType = 'text/plain';
        } catch (e) {}
      }
    }

    return req;
  };

  /**
   * Returns the scheme to use for the transport URLs.
   *
   * @api private
   */

  XHR.prototype.scheme = function () {
    return this.socket.options.secure ? 'https' : 'http';
  };

  /**
   * Check if the XHR transports are supported
   *
   * @param {Boolean} xdomain Check if we support cross domain requests.
   * @returns {Boolean}
   * @api public
   */

  XHR.check = function (xdomain) {
    try {
      if (io.util.request(xdomain)) {
        return true;
      }
    } catch(e) {}

    return false;
  };

  /**
   * Check if the XHR transport supports corss domain requests.
   *
   * @returns {Boolean}
   * @api public
   */

  XHR.xdomainCheck = function(){
    return XHR.check(true);
  };

})(
    'undefined' != typeof io ? io.Transport : module.exports
  , 'undefined' != typeof io ? io : module.parent.exports
);
<commit_msg>Added XHR#close.<commit_after>
/**
 * socket.io
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

(function (exports, io) {

  /**
   * Expose constructor.
   *
   * @api public
   */

  exports.XHR = XHR;

  /**
   * XHR constructor
   *
   * @costructor
   * @api public
   */

  function XHR (socket) {
    if (!socket) return;

    io.Transport.apply(this, arguments);
    this.sendBuffer = [];
  };

  /**
   * Inherits from Transport.
   */

  io.util.inherit(XHR, io.Transport);

  /**
   * Establish a connection
   *
   * @returns {Transport}
   * @api public
   */

  XHR.prototype.open = function () {
    this.get();
    this.onOpen();

    // we need to make sure the request succeeds since we have no indication
    // whether the request opened or not until it succeeded.
    this.setCloseTimeout();

    return this;
  };

  /**
   * Check if we need to send data to the Socket.IO server, if we have data in our
   * buffer we encode it and forward it to the `post` method.
   *
   * @api private
   */

  XHR.prototype.checkSend = function () {
    if (!this.posting && this.sendBuffer.length) {
      var encoded = io.parser.encodePayload(this.sendBuffer);
      this.sendBuffer = [];
      this.post(encoded);
    }
  };

  /**
   * Send data to the Socket.IO server.
   *
   * @param data The message
   * @returns {Transport}
   * @api public
   */

  XHR.prototype.send = function (data) {
    if (io.util.isArray(data)) {
      this.sendBuffer.push.apply(this.sendBuffer, data);
    } else {
      this.sendBuffer.push(data);
    }

    this.checkSend();

    return this;
  };

  /**
   * Posts a encoded message to the Socket.IO server.
   *
   * @param {String} data A encoded message.
   * @api private
   */

  function empty () { };

  XHR.prototype.post = function (data) {
    var self = this;
    this.posting = true;

    function stateChange () {
      if (this.readyState == 4) {
        this.onreadystatechange = empty;
        self.posting = false;

        if (this.status == 200){
          self.checkSend();
        } else {
          self.onClose();
        }
      }
    }

    function onload () {
      this.onload = empty;
      self.posting = false;
      self.checkSend();
    };

    this.sendXHR = this.request('POST');

    if (window.XDomainRequest && this.sendXHR instanceof XDomainRequest) {
      this.sendXHR.onload = this.sendXHR.onerror = onload;
    } else {
      this.sendXHR.onreadystatechange = stateChange;
    }

    this.sendXHR.send(data);
  };

  /**
   * Closes the connection
   *
   * @api private
   */

  XHR.prototype.close = function () {
    this.onClose();
  };

  /**
   * Handle the disconnect request.
   *
   * @api private
   */

  XHR.prototype.onClose = function(){
    if (this.xhr){
      this.xhr.onreadystatechange = this.xhr.onload = empty;
      try {
        this.xhr.abort();
      } catch(e){}
      this.xhr = null;
    }

    if (this.sendXHR){
      this.sendXHR.onreadystatechange = this.sendXHR.onload = empty;
      try {
        this.sendXHR.abort();
      } catch(e){}
      this.sendXHR = null;
    }

    this.sendBuffer = [];

    io.Transport.prototype.onClose.call(this);
  };

  /**
   * Generates a configured XHR request
   *
   * @param {String} url The url that needs to be requested.
   * @param {String} method The method the request should use.
   * @returns {XMLHttpRequest}
   * @api private
   */

  XHR.prototype.request = function (method) {
    var req = io.util.request(this.socket.isXDomain());
    req.open(method || 'GET', this.prepareUrl() + '?t' + (+ new Date));

    if (method == 'POST') {
      if (req.setRequestHeader) {
        req.setRequestHeader('Content-type', 'text/plain');
      } else {
        // XDomainRequest
        try {
          req.contentType = 'text/plain';
        } catch (e) {}
      }
    }

    return req;
  };

  /**
   * Returns the scheme to use for the transport URLs.
   *
   * @api private
   */

  XHR.prototype.scheme = function () {
    return this.socket.options.secure ? 'https' : 'http';
  };

  /**
   * Check if the XHR transports are supported
   *
   * @param {Boolean} xdomain Check if we support cross domain requests.
   * @returns {Boolean}
   * @api public
   */

  XHR.check = function (xdomain) {
    try {
      if (io.util.request(xdomain)) {
        return true;
      }
    } catch(e) {}

    return false;
  };

  /**
   * Check if the XHR transport supports corss domain requests.
   *
   * @returns {Boolean}
   * @api public
   */

  XHR.xdomainCheck = function(){
    return XHR.check(true);
  };

})(
    'undefined' != typeof io ? io.Transport : module.exports
  , 'undefined' != typeof io ? io : module.parent.exports
);
<|endoftext|>


<commit_before>import { expect } from "chai";
import sinon from "sinon";
import RxPlayer from "../../../src";
import { mockManifestRequest } from "../utils/mock_requests.js";
import sleep from "../utils/sleep.js";
import Mock from "../mocks/dash-if_segment-timeline.js";

describe("dash live SegmentTimeline content", function () {
  let player;
  let fakeServer;

  beforeEach(() => {
    player = new RxPlayer();
    fakeServer = sinon.createFakeServer();
  });

  afterEach(() => {
    player.dispose();
    fakeServer.restore();
  });

  it("should fetch and parse the manifest", async function () {
    mockManifestRequest(fakeServer, Mock);
    player.loadVideo({ url: Mock.manifest.url, transport: "dash" });
    expect(fakeServer.requests.length).to.equal(1);
    await sleep(5);
    fakeServer.respond();
    await sleep(5);

    const manifest = player.getManifest();
    expect(manifest).not.to.equal(null);
    expect(typeof manifest).to.equal("object");
    expect(manifest.getDuration()).to.equal(Infinity);
    expect(manifest.transport).to.equal("dash");
    expect(typeof manifest.id).to.equal("string");
    expect(manifest.isLive).to.equal(true);
    expect(manifest.suggestedPresentationDelay).to.equal(10);
    expect(manifest.timeShiftBufferDepth).to.equal(300);
    expect(manifest.getUrl()).to.equal(Mock.manifest.url);
    expect(manifest.availabilityStartTime).to.equal(0);

    const adaptations = manifest.adaptations;

    expect(adaptations.audio.length).to.equal(1);
    expect(!!adaptations.audio[0].isAudioDescription).to.equal(false);
    expect(adaptations.audio[0].language).to.equal("eng");
    expect(adaptations.audio[0].normalizedLanguage).to.equal("eng");
    expect(adaptations.audio[0].type).to.equal("audio");
    expect(typeof adaptations.audio[0].id).to.equal("string");
    expect(adaptations.audio[0].id).to.not.equal(adaptations.video[0].id);
    expect(adaptations.audio[0].representations.length).to.equal(1);
    expect(adaptations.audio[0].getAvailableBitrates()).to.eql([48000]);

    expect(adaptations.video.length).to.equal(1);
    expect(adaptations.video[0].type).to.equal("video");
    expect(adaptations.video[0].getAvailableBitrates()).to.eql([300000]);

    const audioRepresentation = adaptations.audio[0].representations[0];
    expect(audioRepresentation.bitrate).to.equal(48000);
    expect(audioRepresentation.codec).to.equal("mp4a.40.2");
    expect(audioRepresentation.id).to.equal("A48");
    expect(audioRepresentation.mimeType).to.equal("audio/mp4");
    expect(typeof audioRepresentation.index).to.equal("object");

    const audioRepresentationIndex = audioRepresentation.index;
    const initAudioSegment = audioRepresentationIndex.getInitSegment();
    expect(initAudioSegment.id).to.equal("init");
    expect(initAudioSegment.mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/A48/init.mp4");

    const nextAudioSegment1 = audioRepresentationIndex
      .getSegments(1527507762, 5);
    expect(nextAudioSegment1.length).to.equal(1);
    expect(nextAudioSegment1[0].duration).to.equal(288768);
    expect(nextAudioSegment1[0].id).to.equal("73320372578304");
    expect(nextAudioSegment1[0].isInit).to.equal(false);
    expect(nextAudioSegment1[0].time).to.equal(73320372578304);
    expect(nextAudioSegment1[0].timescale).to.equal(48000);
    expect(nextAudioSegment1[0].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/A48/t73320372578304.m4s");

    const nextAudioSegment2 = audioRepresentationIndex
      .getSegments(1527507762, 11);
    expect(nextAudioSegment2.length).to.equal(2);
    expect(nextAudioSegment2[1].duration).to.equal(287744);
    expect(nextAudioSegment2[1].id).to.equal("73320372867072");
    expect(nextAudioSegment2[1].isInit).to.equal(false);
    expect(nextAudioSegment2[1].time).to.equal(73320372867072);
    expect(nextAudioSegment2[1].timescale).to.equal(48000);
    expect(nextAudioSegment2[1].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/A48/t73320372867072.m4s");

    expect(audioRepresentationIndex.getSegments(1527507762, 294).length)
      .to.equal(49);
    expect(audioRepresentationIndex.getSegments(1527507762, 295).length)
      .to.equal(50);
    expect(
      audioRepresentationIndex.getSegments(1527507762, 300000000000).length
    ).to.equal(50);

    const videoRepresentation = adaptations.video[0].representations[0];
    expect(videoRepresentation.bitrate).to.equal(300000);
    expect(videoRepresentation.codec).to.equal("avc1.64001e");
    expect(videoRepresentation.id).to.equal("V300");
    expect(videoRepresentation.height).to.equal(360);
    expect(videoRepresentation.width).to.equal(640);
    expect(videoRepresentation.mimeType).to.equal("video/mp4");
    expect(typeof videoRepresentation.index).to.equal("object");

    const videoRepresentationIndex = videoRepresentation.index;

    const initVideoSegment = videoRepresentationIndex.getInitSegment();
    expect(initVideoSegment.id).to.equal("init");
    expect(initVideoSegment.mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/V300/init.mp4");

    const nextVideoSegment1 = videoRepresentationIndex
      .getSegments(1527507762, 5);
    expect(nextVideoSegment1.length).to.equal(1);
    expect(nextVideoSegment1[0].duration).to.equal(540000);
    expect(nextVideoSegment1[0].id).to.equal("137475698580000");
    expect(nextVideoSegment1[0].isInit).to.equal(false);
    expect(nextVideoSegment1[0].time).to.equal(137475698580000);
    expect(nextVideoSegment1[0].timescale).to.equal(90000);
    expect(nextVideoSegment1[0].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/V300/t137475698580000.m4s");

    const nextVideoSegment2 = videoRepresentationIndex
      .getSegments(1527507762, 11);
    expect(nextVideoSegment2.length).to.equal(2);
    expect(nextVideoSegment2[1].duration).to.equal(540000);
    expect(nextVideoSegment2[1].id).to.equal("137475699120000");
    expect(nextVideoSegment2[1].isInit).to.equal(false);
    expect(nextVideoSegment2[1].time).to.equal(137475699120000);
    expect(nextVideoSegment2[1].timescale).to.equal(90000);
    expect(nextVideoSegment2[1].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/V300/t137475699120000.m4s");

    expect(videoRepresentationIndex.getSegments(1527507762, 294).length)
      .to.equal(49);
    expect(videoRepresentationIndex.getSegments(1527507762, 295).length)
      .to.equal(50);
    expect(
      videoRepresentationIndex.getSegments(1527507762, 300000000000).length
    ).to.equal(50);

    expect(fakeServer.requests.length).to.equal(3);
    const requestsDone = [
      fakeServer.requests[1].url,
      fakeServer.requests[2].url,
    ];
    expect(requestsDone).to.include(Mock.audio[0].init.url);
    expect(requestsDone).to.include(Mock.video[0].init.url);

    // TODO Do the init segment and the first needed segment in parallel
    // expect(fakeServer.requests.length).to.equal(5);

    // const requestsDone = [
    //   fakeServer.requests[1].url,
    //   fakeServer.requests[2].url,
    //   fakeServer.requests[3].url,
    //   fakeServer.requests[4].url,
    // ];

    // expect(requestsDone).to.include(Mock.audio[0].init.url);
    // expect(requestsDone).to.include(Mock.video[0].init.url);

    // let audioSegmentsDownloaded = 0;
    // let videoSegmentsDownloaded = 0;

    // const audioSegmentsUrl = Mock.audio[0].segments.map(s => s.url);
    // const videoSegmentsUrl = Mock.video[0].segments.map(s => s.url);
    // for (let i = 0; i < requestsDone.length; i++) {
    //   const url = requestsDone[i];
    //   if (audioSegmentsUrl.includes(url)) {
    //     audioSegmentsDownloaded++;
    //   }

    //   if (videoSegmentsUrl.includes(url)) {
    //     videoSegmentsDownloaded++;
    //   }
    // }

    // expect(audioSegmentsDownloaded).to.equal(1);
    // expect(videoSegmentsDownloaded).to.equal(1);
  });

  it("should list the right bitrates", async function () {
    mockManifestRequest(fakeServer, Mock);
    player.loadVideo({ url: Mock.manifest.url, transport: "dash" });

    await sleep(5);
    fakeServer.respond();
    await sleep(5);

    expect(player.getAvailableAudioBitrates()).to.eql([48000]);
    expect(player.getAvailableVideoBitrates()).to.eql([300000]);
  });

  it("should list the right languages", async function () {
    mockManifestRequest(fakeServer, Mock);
    player.loadVideo({ url: Mock.manifest.url, transport: "dash" });

    await sleep(5);
    fakeServer.respond();
    await sleep(5);

    const audioTracks = player.getAvailableAudioTracks();
    const textTracks = player.getAvailableTextTracks();

    expect(audioTracks.length).to.equal(1);
    expect(audioTracks[0].language).to.equal("eng");
    expect(audioTracks[0].normalized).to.equal("eng");
    expect(audioTracks[0].audioDescription).to.equal(false);
    expect(typeof audioTracks[0].id).to.equal("string");
    expect(audioTracks[0].id).to.not.equal("");
    expect(audioTracks[0].active).to.equal(true);

    expect(textTracks.length).to.equal(0);
  });
});
<commit_msg>tests: update integration tests<commit_after>import { expect } from "chai";
import sinon from "sinon";
import RxPlayer from "../../../src";
import { mockManifestRequest } from "../utils/mock_requests.js";
import sleep from "../utils/sleep.js";
import Mock from "../mocks/dash-if_segment-timeline.js";

describe("dash live SegmentTimeline content", function () {
  let player;
  let fakeServer;

  beforeEach(() => {
    player = new RxPlayer();
    fakeServer = sinon.createFakeServer();
  });

  afterEach(() => {
    player.dispose();
    fakeServer.restore();
  });

  it("should fetch and parse the manifest", async function () {
    mockManifestRequest(fakeServer, Mock);
    player.loadVideo({ url: Mock.manifest.url, transport: "dash" });
    expect(fakeServer.requests.length).to.equal(1);
    await sleep(10);
    fakeServer.respond();
    await sleep(10);

    const manifest = player.getManifest();
    expect(manifest).not.to.equal(null);
    expect(typeof manifest).to.equal("object");
    expect(manifest.getDuration()).to.equal(Infinity);
    expect(manifest.transport).to.equal("dash");
    expect(typeof manifest.id).to.equal("string");
    expect(manifest.isLive).to.equal(true);
    expect(manifest.suggestedPresentationDelay).to.equal(10);
    expect(manifest.timeShiftBufferDepth).to.equal(300);
    expect(manifest.getUrl()).to.equal(Mock.manifest.url);
    expect(manifest.availabilityStartTime).to.equal(0);

    const adaptations = manifest.adaptations;

    expect(adaptations.audio.length).to.equal(1);
    expect(!!adaptations.audio[0].isAudioDescription).to.equal(false);
    expect(adaptations.audio[0].language).to.equal("eng");
    expect(adaptations.audio[0].normalizedLanguage).to.equal("eng");
    expect(adaptations.audio[0].type).to.equal("audio");
    expect(typeof adaptations.audio[0].id).to.equal("string");
    expect(adaptations.audio[0].id).to.not.equal(adaptations.video[0].id);
    expect(adaptations.audio[0].representations.length).to.equal(1);
    expect(adaptations.audio[0].getAvailableBitrates()).to.eql([48000]);

    expect(adaptations.video.length).to.equal(1);
    expect(adaptations.video[0].type).to.equal("video");
    expect(adaptations.video[0].getAvailableBitrates()).to.eql([300000]);

    const audioRepresentation = adaptations.audio[0].representations[0];
    expect(audioRepresentation.bitrate).to.equal(48000);
    expect(audioRepresentation.codec).to.equal("mp4a.40.2");
    expect(audioRepresentation.id).to.equal("A48");
    expect(audioRepresentation.mimeType).to.equal("audio/mp4");
    expect(typeof audioRepresentation.index).to.equal("object");

    const audioRepresentationIndex = audioRepresentation.index;
    const initAudioSegment = audioRepresentationIndex.getInitSegment();
    expect(initAudioSegment.id).to.equal("init");
    expect(initAudioSegment.mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/A48/init.mp4");

    const nextAudioSegment1 = audioRepresentationIndex
      .getSegments(1527507762, 5);
    expect(nextAudioSegment1.length).to.equal(1);
    expect(nextAudioSegment1[0].duration).to.equal(288768);
    expect(nextAudioSegment1[0].id).to.equal("73320372578304");
    expect(nextAudioSegment1[0].isInit).to.equal(false);
    expect(nextAudioSegment1[0].time).to.equal(73320372578304);
    expect(nextAudioSegment1[0].timescale).to.equal(48000);
    expect(nextAudioSegment1[0].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/A48/t73320372578304.m4s");

    const nextAudioSegment2 = audioRepresentationIndex
      .getSegments(1527507762, 11);
    expect(nextAudioSegment2.length).to.equal(2);
    expect(nextAudioSegment2[1].duration).to.equal(287744);
    expect(nextAudioSegment2[1].id).to.equal("73320372867072");
    expect(nextAudioSegment2[1].isInit).to.equal(false);
    expect(nextAudioSegment2[1].time).to.equal(73320372867072);
    expect(nextAudioSegment2[1].timescale).to.equal(48000);
    expect(nextAudioSegment2[1].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/A48/t73320372867072.m4s");

    expect(audioRepresentationIndex.getSegments(1527507762, 294).length)
      .to.equal(49);
    expect(audioRepresentationIndex.getSegments(1527507762, 295).length)
      .to.equal(50);
    expect(
      audioRepresentationIndex.getSegments(1527507762, 300000000000).length
    ).to.equal(50);

    const videoRepresentation = adaptations.video[0].representations[0];
    expect(videoRepresentation.bitrate).to.equal(300000);
    expect(videoRepresentation.codec).to.equal("avc1.64001e");
    expect(videoRepresentation.id).to.equal("V300");
    expect(videoRepresentation.height).to.equal(360);
    expect(videoRepresentation.width).to.equal(640);
    expect(videoRepresentation.mimeType).to.equal("video/mp4");
    expect(typeof videoRepresentation.index).to.equal("object");

    const videoRepresentationIndex = videoRepresentation.index;

    const initVideoSegment = videoRepresentationIndex.getInitSegment();
    expect(initVideoSegment.id).to.equal("init");
    expect(initVideoSegment.mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/V300/init.mp4");

    const nextVideoSegment1 = videoRepresentationIndex
      .getSegments(1527507762, 5);
    expect(nextVideoSegment1.length).to.equal(1);
    expect(nextVideoSegment1[0].duration).to.equal(540000);
    expect(nextVideoSegment1[0].id).to.equal("137475698580000");
    expect(nextVideoSegment1[0].isInit).to.equal(false);
    expect(nextVideoSegment1[0].time).to.equal(137475698580000);
    expect(nextVideoSegment1[0].timescale).to.equal(90000);
    expect(nextVideoSegment1[0].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/V300/t137475698580000.m4s");

    const nextVideoSegment2 = videoRepresentationIndex
      .getSegments(1527507762, 11);
    expect(nextVideoSegment2.length).to.equal(2);
    expect(nextVideoSegment2[1].duration).to.equal(540000);
    expect(nextVideoSegment2[1].id).to.equal("137475699120000");
    expect(nextVideoSegment2[1].isInit).to.equal(false);
    expect(nextVideoSegment2[1].time).to.equal(137475699120000);
    expect(nextVideoSegment2[1].timescale).to.equal(90000);
    expect(nextVideoSegment2[1].mediaURL).to
      .equal("http://vm2.dashif.org/livesim-dev/segtimeline_1/testpic_6s/V300/t137475699120000.m4s");

    expect(videoRepresentationIndex.getSegments(1527507762, 294).length)
      .to.equal(49);
    expect(videoRepresentationIndex.getSegments(1527507762, 295).length)
      .to.equal(50);
    expect(
      videoRepresentationIndex.getSegments(1527507762, 300000000000).length
    ).to.equal(50);

    expect(fakeServer.requests.length).to.equal(3);
    const requestsDone = [
      fakeServer.requests[1].url,
      fakeServer.requests[2].url,
    ];
    expect(requestsDone).to.include(Mock.audio[0].init.url);
    expect(requestsDone).to.include(Mock.video[0].init.url);

    // TODO Do the init segment and the first needed segment in parallel
    // expect(fakeServer.requests.length).to.equal(5);

    // const requestsDone = [
    //   fakeServer.requests[1].url,
    //   fakeServer.requests[2].url,
    //   fakeServer.requests[3].url,
    //   fakeServer.requests[4].url,
    // ];

    // expect(requestsDone).to.include(Mock.audio[0].init.url);
    // expect(requestsDone).to.include(Mock.video[0].init.url);

    // let audioSegmentsDownloaded = 0;
    // let videoSegmentsDownloaded = 0;

    // const audioSegmentsUrl = Mock.audio[0].segments.map(s => s.url);
    // const videoSegmentsUrl = Mock.video[0].segments.map(s => s.url);
    // for (let i = 0; i < requestsDone.length; i++) {
    //   const url = requestsDone[i];
    //   if (audioSegmentsUrl.includes(url)) {
    //     audioSegmentsDownloaded++;
    //   }

    //   if (videoSegmentsUrl.includes(url)) {
    //     videoSegmentsDownloaded++;
    //   }
    // }

    // expect(audioSegmentsDownloaded).to.equal(1);
    // expect(videoSegmentsDownloaded).to.equal(1);
  });

  it("should list the right bitrates", async function () {
    mockManifestRequest(fakeServer, Mock);
    player.loadVideo({ url: Mock.manifest.url, transport: "dash" });

    await sleep(5);
    fakeServer.respond();
    await sleep(5);

    expect(player.getAvailableAudioBitrates()).to.eql([48000]);
    expect(player.getAvailableVideoBitrates()).to.eql([300000]);
  });

  it("should list the right languages", async function () {
    mockManifestRequest(fakeServer, Mock);
    player.loadVideo({ url: Mock.manifest.url, transport: "dash" });

    await sleep(5);
    fakeServer.respond();
    await sleep(5);

    const audioTracks = player.getAvailableAudioTracks();
    const textTracks = player.getAvailableTextTracks();

    expect(audioTracks.length).to.equal(1);
    expect(audioTracks[0].language).to.equal("eng");
    expect(audioTracks[0].normalized).to.equal("eng");
    expect(audioTracks[0].audioDescription).to.equal(false);
    expect(typeof audioTracks[0].id).to.equal("string");
    expect(audioTracks[0].id).to.not.equal("");
    expect(audioTracks[0].active).to.equal(true);

    expect(textTracks.length).to.equal(0);
  });
});
<|endoftext|>


<commit_before>// Load in dependencies
var fs = require('fs');
var path = require('path');

// Define and export our install command
exports.installSync = function (destDir) {
  // Copy over each of the .jscsrc and .jshintrc files to the destination
  // TODO: For browser support, accept an option the environment and modify the files
  var jscsrc = fs.readFileSync(__dirname + '/../.jscsrc', 'utf8');
  var jshintrc = fs.readFileSync(__dirname + '/../.jshintrc', 'utf8');
  fs.writeFileSync(path.join(destDir, '.jscsrc'), jscsrc, 'utf8');
  fs.writeFileSync(path.join(destDir, '.jshintrc'), jshintrc, 'utf8');
};
<commit_msg>Updated install script<commit_after>// Load in dependencies
var fs = require('fs');
var path = require('path');

// Define and export our install command
exports.installSync = function (destDir) {
  // Load in the files as if they are JS
  // TODO: For browser support, accept an option (e.g. `environment`) and modify the files
  var jscsrcObj = require('./rc/jscsrc');
  var jshintrcObj = require('./rc/jshintrc');

  // Stringify our configs and save them to our destination
  var jscsrc = JSON.stringify(jscsrcObj, null, 2);
  var jshintrc = JSON.stringify(jshintrcObj, null, 2);
  fs.writeFileSync(path.join(destDir, '.jscsrc'), jscsrc, 'utf8');
  fs.writeFileSync(path.join(destDir, '.jshintrc'), jshintrc, 'utf8');
};
<|endoftext|>


<commit_before>import blobStream from 'blob-stream'
import moment from 'moment'

import PDFDocument from './PDFDocumentWithTables'

/**
 * Fields use to construct PDF letter. The nested arrays
 * correspond to rows of inputs in the form layout.
 */
export const LETTER_FIELDS = [
  [
    {fieldName: 'firstname', placeholder: 'First name'},
    {fieldName: 'lastname', placeholder: 'Last name'}
  ],
  [
    {fieldName: 'address1', placeholder: 'Address 1'},
    {fieldName: 'address2', placeholder: 'Address 2'}
  ],
  [
    {fieldName: 'city', placeholder: 'City'},
    {fieldName: 'state', placeholder: 'State'},
    {fieldName: 'zip', placeholder: 'Zip'}
  ]
]

/**
 * Generate a PDF letter for the provided form data/config.
 * This depends on the headerGraphic config field being provided and a valid URL
 * to a .png image file.
 */
export function createLetter (formData, mailablesConfig) {
  const imageUrl = mailablesConfig.headerGraphic
  // A valid URL must be provided in the config in order for the create letter
  // approach to function properly.
  if (!imageUrl) {
    return alert(`Error constructing letter. Mailables headerGraphic config property is missing.`)
  }
  // This is a very goofy approach to convert an image URL to its image data for
  // writing to the PDF, but it seems to be a solid approach.
  const img = new Image()
  img.crossOrigin = 'anonymous'
  img.onload = () => {
    var canvas = document.createElement('canvas')
    canvas.width = img.width
    canvas.height = img.height

    const ctx = canvas.getContext('2d')
    ctx.drawImage(img, 0, 0)

    const data = canvas.toDataURL('image/png')
    const imageData = {data, height: img.height, width: img.width}
    writePDF(formData, imageData, mailablesConfig)
  }
  img.src = imageUrl
}

/**
 * Write the provided formData and imageData (header image) to a PDF file and
 * open as a new tab.
 */
function writePDF (formData, imageData, otpConfig) {
  const {
    address1 = '',
    address2,
    city = '',
    firstname = '',
    lastname = '',
    mailables,
    state = '',
    zip = ''
  } = formData
  const {horizontalMargin, verticalMargin} = otpConfig
  const margins = {
    bottom: verticalMargin,
    left: horizontalMargin,
    right: horizontalMargin,
    top: verticalMargin
  }
  const doc = new PDFDocument({margins})
  const stream = doc.pipe(blobStream())
  preparePage(doc, imageData, otpConfig)
  doc.on('pageAdded', () => preparePage(doc, imageData, otpConfig))

  // current date
  doc.text(moment().format('MMMM D, YYYY'))

  // recipient address
  doc.moveDown()
    .moveDown()
    .moveDown()
    .text(firstname.toUpperCase() + ' ' + lastname.toUpperCase())
    .text(address1.toUpperCase())
  if (address2 && address2.length > 0) {
    doc.text(address2.toUpperCase())
  }
  doc.text(city.toUpperCase() + ', ' + state.toUpperCase() + ' ' + zip)

  // introduction block
  doc.moveDown()
    .moveDown()
    .text(otpConfig.introduction)

  // table header
  doc.font('Helvetica-Bold')
    .moveDown()
    .moveDown()
    .text('SUMMARY BY ITEM', {align: 'center'})
    .moveDown()
    .moveDown()

  const tableData = {
    headers: ['Item', 'Quantity'],
    rows: mailables.map(mailable => {
      let {largeFormat, largePrint, name, quantity} = mailable
      if (largePrint && largeFormat) {
        name += ' (LARGE PRINT)'
      }
      return [name, quantity]
    })
  }

  doc.table(tableData, {
    prepareHeader: () => doc.font('Helvetica-Bold'),
    prepareRow: (row, i) => doc.font('Helvetica').fontSize(12)
  })

  // conclusion block
  doc.moveDown()
    .moveDown()
    .font('Helvetica')
    .text(otpConfig.conclusion)

  doc.end()
  stream.on('finish', () => {
    const url = stream.toBlobURL('application/pdf')
    window.open(url)
  })
}

/**
 * Handle preparing a new page for the PDF doc, adding a footer, header image,
 * and resetting the currsor to the correct position.
 */
function preparePage (doc, imageData, otpConfig) {
  const {footer, headerGraphic, headerGraphicHeight, headerGraphicWidth} = otpConfig
  // Store true bottom of page while bottom is temporarily moved to 0.
  const bottom = doc.page.margins.bottom
  doc.page.margins.bottom = 0
  // Add footer to page.
  if (footer) {
    doc.fontSize(9)
      .text(
        footer,
        0.5 * (doc.page.width - 500),
        doc.page.height - 50,
        {align: 'center', lineBreak: false, width: 500}
      )
  }
  // Add header image.
  if (headerGraphic) {
    const width = headerGraphicWidth || 72 * imageData.width / 300
    const height = headerGraphicHeight || 72 * imageData.height / 300
    doc.image(
      imageData.data,
      0.5 * (doc.page.width - 300),
      40,
      {align: 'center', height, width}
    )
  }
  // Reset font size, bottom margin, and text writer position.
  doc.fontSize(12)
    .text('', doc.page.margins.left, doc.page.margins.top)
  doc.page.margins.bottom = bottom
}
<commit_msg>refactor(mailables): code split PDFKit dependency<commit_after>import blobStream from 'blob-stream'
import moment from 'moment'

/**
 * Fields use to construct PDF letter. The nested arrays
 * correspond to rows of inputs in the form layout.
 */
export const LETTER_FIELDS = [
  [
    { fieldName: 'firstname', placeholder: 'First name' },
    { fieldName: 'lastname', placeholder: 'Last name' }
  ],
  [
    { fieldName: 'address1', placeholder: 'Address 1' },
    { fieldName: 'address2', placeholder: 'Address 2' }
  ],
  [
    { fieldName: 'city', placeholder: 'City' },
    { fieldName: 'state', placeholder: 'State' },
    { fieldName: 'zip', placeholder: 'Zip' }
  ]
]

/**
 * Handle preparing a new page for the PDF doc, adding a footer, header image,
 * and resetting the currsor to the correct position.
 */
function preparePage(doc, imageData, otpConfig) {
  const { footer, headerGraphic, headerGraphicHeight, headerGraphicWidth } =
    otpConfig
  // Store true bottom of page while bottom is temporarily moved to 0.
  const bottom = doc.page.margins.bottom
  doc.page.margins.bottom = 0
  // Add footer to page.
  if (footer) {
    doc
      .fontSize(9)
      .text(footer, 0.5 * (doc.page.width - 500), doc.page.height - 50, {
        align: 'center',
        lineBreak: false,
        width: 500
      })
  }
  // Add header image.
  if (headerGraphic) {
    const width = headerGraphicWidth || (72 * imageData.width) / 300
    const height = headerGraphicHeight || (72 * imageData.height) / 300
    doc.image(imageData.data, 0.5 * (doc.page.width - 300), 40, {
      align: 'center',
      height,
      width
    })
  }
  // Reset font size, bottom margin, and text writer position.
  doc.fontSize(12).text('', doc.page.margins.left, doc.page.margins.top)
  doc.page.margins.bottom = bottom
}

/**
 * Write the provided formData and imageData (header image) to a PDF file and
 * open as a new tab.
 */
async function writePDF(formData, imageData, otpConfig) {
  const {
    address1 = '',
    address2,
    city = '',
    firstname = '',
    lastname = '',
    mailables,
    state = '',
    zip = ''
  } = formData
  const { horizontalMargin, verticalMargin } = otpConfig
  const margins = {
    bottom: verticalMargin,
    left: horizontalMargin,
    right: horizontalMargin,
    top: verticalMargin
  }
  const { default: PDFDocument } = await import('./PDFDocumentWithTables')
  console.log(PDFDocument)
  const doc = new PDFDocument({ margins })
  const stream = doc.pipe(blobStream())
  preparePage(doc, imageData, otpConfig)
  doc.on('pageAdded', () => preparePage(doc, imageData, otpConfig))

  // current date
  doc.text(moment().format('MMMM D, YYYY'))

  // recipient address
  doc
    .moveDown()
    .moveDown()
    .moveDown()
    .text(firstname.toUpperCase() + ' ' + lastname.toUpperCase())
    .text(address1.toUpperCase())
  if (address2 && address2.length > 0) {
    doc.text(address2.toUpperCase())
  }
  doc.text(city.toUpperCase() + ', ' + state.toUpperCase() + ' ' + zip)

  // introduction block
  doc.moveDown().moveDown().text(otpConfig.introduction)

  // table header
  doc
    .font('Helvetica-Bold')
    .moveDown()
    .moveDown()
    .text('SUMMARY BY ITEM', { align: 'center' })
    .moveDown()
    .moveDown()

  const tableData = {
    headers: ['Item', 'Quantity'],
    rows: mailables.map((mailable) => {
      let { largeFormat, largePrint, name, quantity } = mailable
      if (largePrint && largeFormat) {
        name += ' (LARGE PRINT)'
      }
      return [name, quantity]
    })
  }

  doc.table(tableData, {
    prepareHeader: () => doc.font('Helvetica-Bold'),
    prepareRow: (row, i) => doc.font('Helvetica').fontSize(12)
  })

  // conclusion block
  doc.moveDown().moveDown().font('Helvetica').text(otpConfig.conclusion)

  doc.end()
  stream.on('finish', () => {
    const url = stream.toBlobURL('application/pdf')
    window.open(url)
  })
}

/**
 * Generate a PDF letter for the provided form data/config.
 * This depends on the headerGraphic config field being provided and a valid URL
 * to a .png image file.
 */
export function createLetter(formData, mailablesConfig) {
  const imageUrl = mailablesConfig.headerGraphic
  // A valid URL must be provided in the config in order for the create letter
  // approach to function properly.
  if (!imageUrl) {
    return alert(
      'Error constructing letter. Mailables headerGraphic config property is missing.'
    )
  }
  // This is a very goofy approach to convert an image URL to its image data for
  // writing to the PDF, but it seems to be a solid approach.
  const img = new Image()
  img.crossOrigin = 'anonymous'
  img.onload = () => {
    const canvas = document.createElement('canvas')
    canvas.width = img.width
    canvas.height = img.height

    const ctx = canvas.getContext('2d')
    ctx.drawImage(img, 0, 0)

    const data = canvas.toDataURL('image/png')
    const imageData = { data, height: img.height, width: img.width }
    writePDF(formData, imageData, mailablesConfig)
  }
  img.src = imageUrl
}
<|endoftext|>


<commit_before>var Promise = global.Packages.Promise,
Hold = global.Packages.Hold,
Memcached = global.Packages.Memcached;

var _cacheClient, _defaultExpiryInSeconds = 60 * 60;

var issue = function(details){
    global.Logger.error(JSON.stringify(details));

};

var failure = function(details){
    global.Logger.error(JSON.stringify(details));

};

var reconnecting = function(details){
    global.Logger.error(JSON.stringify(details));

};

var getCacheClient = function(ctx) {

    var needstoreload = false;

	if (!_cacheClient) {
		needstoreload = true;
	}

	if (needstoreload) {

		return ctx.getServers(function(servers) {

				if (_cacheClient) {
					_cacheClient.end();
				}
				var memcached_servers = [];
				for(var index = 0; index < servers.length; index++){
				    console.log(servers[index].server);
				    memcached_servers.push(servers[index].server + ":" + servers[index].port);
				}

				_cacheClient = new Memcached(servers);
				_cacheClient.setMaxListeners(0);
			    return _cacheClient;

		});
	} else {
		return new Promise(function(resolve){
		    resolve(_cacheClient);
		    return;
		});
	}
};

function MemCache(){

}

MemCache.prototype.getServers = function getServers(){
    return new Promise(function (resolve){
        return resolve([
            {
                server : "localhost",
                port : 11211
        }]);
    });
};

function setValue(cache_client,key, value, callback){
    cache_client.set(key, value, function(err){
        if(err){
            callback(err);
            return;
        }
        callback(null);
        return;
    });
}

function getValue(cache_client,key, callback){
    cache_client.get(key, function(err, val){
        if(err){
            callback(err, null);
            return;
        }
        callback(null, val);
        return;
    });
}

MemCache.prototype.set = function set(key, value, expiry_in_seconds){
    var self = this;
    expiry_in_seconds = (expiry_in_seconds && expiry_in_seconds > 0) ? expiry_in_seconds : _defaultExpiryInSeconds;
   return getCacheClient(self).then(function(cache) {
        console.log(cache);
		return Promise.promisify(setValue)(cache, key, value, _defaultExpiryInSeconds).then(function(){
			return;
		});

	}).then(function(){
	    return;
	});
};

MemCache.prototype.get = function get(key) {
    var self = this;
	return getCacheClient(self).then(function(cache) {

		return Promise.promisify(getValue)(cache, key).then(function(value){
			if(value === false) {
			    return null;
			}
			else{
			    return value;
			}
		});

	}).then(function(val){
	    return val;
	});

};

MemCache.prototype.remove = function remove(key){
      var self = this;
	return getCacheClient(self).then(function(cache) {
		return Promise.promisify(cache.del)(key).then(function(){
			return;
		});

	}).then(function(){
	    return;
	});

};

module.exports = MemCache;

<commit_msg>cache fix 1<commit_after>var Promise = global.Packages.Promise,
Hold = global.Packages.Hold,
Memcached = global.Packages.Memcached;

var _cacheClient, _defaultExpiryInSeconds = 60 * 60;

var issue = function(details){
    global.Logger.error(JSON.stringify(details));

};

var failure = function(details){
    global.Logger.error(JSON.stringify(details));

};

var reconnecting = function(details){
    global.Logger.error(JSON.stringify(details));

};

var getCacheClient = function(ctx) {

    var needstoreload = false;

	if (!_cacheClient) {
		needstoreload = true;
	}

	if (needstoreload) {

		return ctx.getServers().then(function(servers) {

				if (_cacheClient) {
					_cacheClient.end();
				}
				var memcached_servers = [];
				for(var index = 0; index < servers.length; index++){
				    console.log(servers[index].server);
				    memcached_servers.push(servers[index].server + ":" + servers[index].port);
				}

				_cacheClient = new Memcached(servers);
				_cacheClient.setMaxListeners(0);
			    return _cacheClient;

		});
	} else {
		return new Promise(function(resolve){
		    resolve(_cacheClient);
		    return;
		});
	}
};

function MemCache(){

}

MemCache.prototype.getServers = function getServers(){
    return new Promise(function (resolve){
        return resolve([
            {
                server : "localhost",
                port : 11211
        }]);
    });
};

function setValue(cache_client,key, value, callback){
    cache_client.set(key, value, function(err){
        if(err){
            callback(err);
            return;
        }
        callback(null);
        return;
    });
}

function getValue(cache_client,key, callback){
    cache_client.get(key, function(err, val){
        if(err){
            callback(err, null);
            return;
        }
        callback(null, val);
        return;
    });
}

MemCache.prototype.set = function set(key, value, expiry_in_seconds){
    var self = this;
    expiry_in_seconds = (expiry_in_seconds && expiry_in_seconds > 0) ? expiry_in_seconds : _defaultExpiryInSeconds;
   return getCacheClient(self).then(function(cache) {
        console.log(cache);
		return Promise.promisify(setValue)(cache, key, value, _defaultExpiryInSeconds).then(function(){
			return;
		});

	}).then(function(){
	    return;
	});
};

MemCache.prototype.get = function get(key) {
    var self = this;
	return getCacheClient(self).then(function(cache) {

		return Promise.promisify(getValue)(cache, key).then(function(value){
			if(value === false) {
			    return null;
			}
			else{
			    return value;
			}
		});

	}).then(function(val){
	    return val;
	});

};

MemCache.prototype.remove = function remove(key){
      var self = this;
	return getCacheClient(self).then(function(cache) {
		return Promise.promisify(cache.del)(key).then(function(){
			return;
		});

	}).then(function(){
	    return;
	});

};

module.exports = MemCache;

<|endoftext|>


<commit_before>'use strict';
const crypto = require('crypto');
const pxconfig = require('../pxconfig').conf();
const pxlogger = require('./pxlogger');

exports.evalCookie = evalCookie;

/**
 * decryptCookie - Decrypt a raw perimeterx cookie into a json holding the risk score of the user .
 *
 * @param {String} cookieKey - Cookie secret key.
 * @param {String} cookie - Cookie raw value.
 *
 * @return {String} An express middleware.
 */
function decryptCookie(cookieKey, cookie) {
    const data = cookie.split(':');
    if (data.length !== 3) {
        pxlogger.debug('invalid cookie format');
        return '';
    }
    const iterations = Number(data[1]);
    const encryptedCookie = data[2];
    /* iterations value is not a number */
    if (!iterations) {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    /* iterations value is not in the legit range */
    if (iterations > 5000 || iterations < 500) {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    /* salt value is not as expected */
    if (!data[0] || typeof data[0] !== 'string' || data[0].length > 100) {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    /* cookie value is not as expected */
    if (!encryptedCookie || typeof encryptedCookie !== 'string') {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    const salt = new Buffer(data[0], 'base64');
    const derivation = crypto.pbkdf2Sync(cookieKey, salt, iterations, pxconfig.CE_KEYLEN + pxconfig.CE_IVLEN, pxconfig.CE_DIGEST);
    const key = derivation.slice(0, pxconfig.CE_KEYLEN);
    const iv = derivation.slice(pxconfig.CE_KEYLEN);

    const cipher = crypto.createDecipheriv(pxconfig.CE_ALGO, key, iv);
    let decrypted = cipher.update(encryptedCookie, 'base64', 'utf8');
    decrypted += cipher.final('utf8');

    return JSON.parse(decrypted);
}

/**
 * isCookieValid - validate cookie content by checking it's hmac signing match the expected.
 *
 * @param {String} cookieKey - Cookie secret key.
 * @param {String} cookie - Cookie raw value.
 * @param {String} ip - user ip, used to validate cookie signing.
 * @param {String} ua - user's useragent, also used to validate cookie signing.
 *
 * @return {Boolean} indicator to the validity of the cookie.
 */
function isCookieValid(cookie, cookieKey, ip, ua) {
    try {
        if (!cookie || !cookie.h || !cookie.t || !cookie.s) {
            return false;
        }

        const hmac = crypto.createHmac(pxconfig.CE_DIGEST, cookieKey);
        const ts = cookie.t.toString();
        hmac.update(ts);

        for (let k of Object.keys(cookie.s).sort()) {
            const score = cookie.s[k].toString();
            hmac.update(score);
        }

        hmac.update(cookie.u);
        hmac.update(ip);
        hmac.update(ua);

        const h = hmac.digest('hex');
        return (cookie.h === h);
    } catch (e) {
        console.error('Error white validating perimeterx cookie', e.stack);
        return false;
    }
}

/**
 * isCookieTimeValid - check the timestamp of the cookie is valid.
 *
 * @param {String} cookie - Cookie secret key.
 *
 * @return {Boolean} indicator to the validity of the cookie.
 */
function isCookieTimeValid(cookie) {
    if (!cookie || !cookie.t) {
        return false;
    }
    const now = new Date().getTime();
    return (cookie.t > now);
}

/**
 * processCookieScore - processing cookie score and return a block indicator.
 *
 * @param {object} decryptedCookie - Cookie secret key.
 * @param {Object} pxCtx - current request context.
 *
 * @return {Boolean} indicator to the cookie score.
 *
 */
function isBadRiskScore(decryptedCookie, pxCtx) {
    if (!decryptedCookie || !decryptedCookie.s) {
        pxlogger.debug('Decrypted cookie has no score');
        return false;
    }

    let score = Math.max(Number(decryptedCookie.s.b), Number(decryptedCookie.s.a));
    if (score >= pxconfig.BLOCKING_SCORE) {
        pxCtx.block_score = score;
        return true;
    }

    return false
}

/**
 * evalCookie - main cookie evaluation function. dectypt, decode and verify cookie content. if score.
 *
 * @param {String} cookie - cookie raw value.
 * @param {String} ip - user ip, used to validate cookie signing.
 * @param {String} ua - user's useragent, also used to validate cookie signing.
 * @param {Object} pxCtx - current request context.
 *
 * @return {Number} evaluation results, derived from configured enum on PX_DEFAULT.COOKIE_EVAL. possible values: (NO_COOKIE, COOKIE_INVALID, COOKIE_EXPIRED, UNEXPECTED_RESULT, BAD_SCORE, GOOD_SCORE).
 *
 */
function evalCookie(cookie, ip, ua, pxCtx) {
    if (!cookie) {
        pxlogger.debug('No cookie found, pause cookie evaluation');
        return pxconfig.SCORE_EVALUATE_ACTION.NO_COOKIE;
    }
    const cookieKey = pxconfig.COOKIE_SECRET_KEY;
    if (!cookieKey) {
        pxlogger.debug('No cookie key found, pause cookie evaluation');
        return pxconfig.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
    }
    try {
        let decryptedCookie;
        if (pxconfig.COOKIE_ENCRYPTION) {
            decryptedCookie = decryptCookie(cookieKey, cookie);
        } else {
            decryptedCookie = new Buffer(cookie, 'base64').toString('utf8');
        }

        if (!decryptedCookie) {
            return pxconfig.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
        }

        /* evaluation risk score */
        if (isBadRiskScore(decryptedCookie, pxCtx)) {
            pxCtx.block_uuid = decryptedCookie.u || '';
            return pxconfig.SCORE_EVALUATE_ACTION.BAD_SCORE;
        }

        /* risk score is not a bad score, need to validate the cookie and its expiry */
        if (!isCookieValid(decryptedCookie, cookieKey, ip, ua)) {
            pxlogger.debug('cookie invalid ' + decryptedCookie);
            return pxconfig.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
        }

        if (!isCookieTimeValid(decryptedCookie)) {
            pxlogger.debug('cookie invalid, expired');
            return pxconfig.SCORE_EVALUATE_ACTION.COOKIE_EXPIRED;
        }

        return pxconfig.SCORE_EVALUATE_ACTION.GOOD_SCORE;
    } catch (e) {
        pxlogger.error('Error while evaluate perimeterx cookie. ' + e.message);
        return pxconfig.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
    }
}<commit_msg>Format invalid cookie error to be printed correctly<commit_after>'use strict';
const crypto = require('crypto');
const pxconfig = require('../pxconfig').conf();
const pxlogger = require('./pxlogger');

exports.evalCookie = evalCookie;

/**
 * decryptCookie - Decrypt a raw perimeterx cookie into a json holding the risk score of the user .
 *
 * @param {String} cookieKey - Cookie secret key.
 * @param {String} cookie - Cookie raw value.
 *
 * @return {String} An express middleware.
 */
function decryptCookie(cookieKey, cookie) {
    const data = cookie.split(':');
    if (data.length !== 3) {
        pxlogger.debug('invalid cookie format');
        return '';
    }
    const iterations = Number(data[1]);
    const encryptedCookie = data[2];
    /* iterations value is not a number */
    if (!iterations) {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    /* iterations value is not in the legit range */
    if (iterations > 5000 || iterations < 500) {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    /* salt value is not as expected */
    if (!data[0] || typeof data[0] !== 'string' || data[0].length > 100) {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    /* cookie value is not as expected */
    if (!encryptedCookie || typeof encryptedCookie !== 'string') {
        pxlogger.debug('invalid cookie format');
        return '';
    }

    const salt = new Buffer(data[0], 'base64');
    const derivation = crypto.pbkdf2Sync(cookieKey, salt, iterations, pxconfig.CE_KEYLEN + pxconfig.CE_IVLEN, pxconfig.CE_DIGEST);
    const key = derivation.slice(0, pxconfig.CE_KEYLEN);
    const iv = derivation.slice(pxconfig.CE_KEYLEN);

    const cipher = crypto.createDecipheriv(pxconfig.CE_ALGO, key, iv);
    let decrypted = cipher.update(encryptedCookie, 'base64', 'utf8');
    decrypted += cipher.final('utf8');

    return JSON.parse(decrypted);
}

/**
 * isCookieValid - validate cookie content by checking it's hmac signing match the expected.
 *
 * @param {String} cookieKey - Cookie secret key.
 * @param {String} cookie - Cookie raw value.
 * @param {String} ip - user ip, used to validate cookie signing.
 * @param {String} ua - user's useragent, also used to validate cookie signing.
 *
 * @return {Boolean} indicator to the validity of the cookie.
 */
function isCookieValid(cookie, cookieKey, ip, ua) {
    try {
        if (!cookie || !cookie.h || !cookie.t || !cookie.s) {
            return false;
        }

        const hmac = crypto.createHmac(pxconfig.CE_DIGEST, cookieKey);
        const ts = cookie.t.toString();
        hmac.update(ts);

        for (let k of Object.keys(cookie.s).sort()) {
            const score = cookie.s[k].toString();
            hmac.update(score);
        }

        hmac.update(cookie.u);
        hmac.update(ip);
        hmac.update(ua);

        const h = hmac.digest('hex');
        return (cookie.h === h);
    } catch (e) {
        console.error('Error white validating perimeterx cookie', e.stack);
        return false;
    }
}

/**
 * isCookieTimeValid - check the timestamp of the cookie is valid.
 *
 * @param {String} cookie - Cookie secret key.
 *
 * @return {Boolean} indicator to the validity of the cookie.
 */
function isCookieTimeValid(cookie) {
    if (!cookie || !cookie.t) {
        return false;
    }
    const now = new Date().getTime();
    return (cookie.t > now);
}

/**
 * processCookieScore - processing cookie score and return a block indicator.
 *
 * @param {object} decryptedCookie - Cookie secret key.
 * @param {Object} pxCtx - current request context.
 *
 * @return {Boolean} indicator to the cookie score.
 *
 */
function isBadRiskScore(decryptedCookie, pxCtx) {
    if (!decryptedCookie || !decryptedCookie.s) {
        pxlogger.debug('Decrypted cookie has no score');
        return false;
    }

    let score = Math.max(Number(decryptedCookie.s.b), Number(decryptedCookie.s.a));
    if (score >= pxconfig.BLOCKING_SCORE) {
        pxCtx.block_score = score;
        return true;
    }

    return false
}

/**
 * evalCookie - main cookie evaluation function. dectypt, decode and verify cookie content. if score.
 *
 * @param {String} cookie - cookie raw value.
 * @param {String} ip - user ip, used to validate cookie signing.
 * @param {String} ua - user's useragent, also used to validate cookie signing.
 * @param {Object} pxCtx - current request context.
 *
 * @return {Number} evaluation results, derived from configured enum on PX_DEFAULT.COOKIE_EVAL. possible values: (NO_COOKIE, COOKIE_INVALID, COOKIE_EXPIRED, UNEXPECTED_RESULT, BAD_SCORE, GOOD_SCORE).
 *
 */
function evalCookie(cookie, ip, ua, pxCtx) {
    if (!cookie) {
        pxlogger.debug('No cookie found, pause cookie evaluation');
        return pxconfig.SCORE_EVALUATE_ACTION.NO_COOKIE;
    }
    const cookieKey = pxconfig.COOKIE_SECRET_KEY;
    if (!cookieKey) {
        pxlogger.debug('No cookie key found, pause cookie evaluation');
        return pxconfig.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
    }
    try {
        let decryptedCookie;
        if (pxconfig.COOKIE_ENCRYPTION) {
            decryptedCookie = decryptCookie(cookieKey, cookie);
        } else {
            decryptedCookie = new Buffer(cookie, 'base64').toString('utf8');
        }

        if (!decryptedCookie) {
            return pxconfig.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
        }

        /* evaluation risk score */
        if (isBadRiskScore(decryptedCookie, pxCtx)) {
            pxCtx.block_uuid = decryptedCookie.u || '';
            return pxconfig.SCORE_EVALUATE_ACTION.BAD_SCORE;
        }

        /* risk score is not a bad score, need to validate the cookie and its expiry */
        if (!isCookieValid(decryptedCookie, cookieKey, ip, ua)) {
            pxlogger.debug('cookie invalid ' + JSON.stringify(decryptedCookie));
            return pxconfig.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
        }

        if (!isCookieTimeValid(decryptedCookie)) {
            pxlogger.debug('cookie invalid, expired');
            return pxconfig.SCORE_EVALUATE_ACTION.COOKIE_EXPIRED;
        }

        return pxconfig.SCORE_EVALUATE_ACTION.GOOD_SCORE;
    } catch (e) {
        pxlogger.error('Error while evaluate perimeterx cookie. ' + e.message);
        return pxconfig.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
    }
}
<|endoftext|>


<commit_before>modules.define('util.dom', [

], function (provide) {

    var utilDom = {
        find: function (query, parentElement) {
            var element;
            if (parentElement) {
                element = parentElement.querySelector(query);
            } else {
                element = document.querySelector(query);
            }

            return element;
        },

        findById: function (query, parentElement) {
            var element;
            if (parentElement) {
                element = parentElement.getElementById(elementId);
            } else {
                element = document.getElementById(elementId);
            }

            return element;
        }
    }

    provide(utilDom);
});
<commit_msg>[util.dom] add .create, change .find parameters<commit_after>modules.define('util.dom', [

], function (provide) {

    var utilDom = {
        /**
         * @param {String} selector
         * @param {HTMLElement} [parentElement = document]
         * @param {Boolean} [findAll = false]
         */
        find: function (options) {
            var element,
                parent = parentElement || document;

            element = findAll ?
                parentElement.querySelectorAll(selector) :
                parentElement.querySelector(selector);

            return element;
        },

        findById: function (query, parentElement) {
            var element;
            if (parentElement) {
                element = parentElement.getElementById(elementId);
            } else {
                element = document.getElementById(elementId);
            }

            return element;
        },

        /**
         * @param {Object|String} [options] Options or element tag name.
         * @param {String} [options.tag = 'div']
         * @param {String} [options.content]
         * @param {Object} [options.attrs]
         * @param {HTMLElement} [options.parentElement]
         * @returns {HTMLElement} Created element.
         */
        create: function (options) {
            options = options || {};

            var tagName = typeof options == 'string' ? options : (options.tag || 'div'),
                element = document.createElement(tagName);

            if (optios.attrs) {
                for (var attribute in options.attrs) {
                    if (options.attrs.hadOwnProperty(attribute)) {
                        element.setAttribute(attribute, options.attrs[attribute]);
                    }
                }
            }

            if (options.parentElement) {
                options.parentElement.appendChild(element);
            }

            return element;
        }
    }

    provide(utilDom);
});
<|endoftext|>


<commit_before>//TODO:Javacsript can check if checked Ids hasv the same scale ID
function extractSelectedItems(submitButton) {
	sourceForm = document.getElementById("modifyFormRows");
	destinationForm = submitButton.form;
	var count = 0; //enforce at least 2 check box are checked

	if (sourceForm && destinationForm) {
		var idList = "";

		// look for checkboxes which have IDs of the form "block-12-11" and keep track of the ones that are checked.
		for (var a=0; a < sourceForm.elements.length; a++) {
			if (sourceForm.elements[a].type=="checkbox" &&
sourceForm.elements[a].checked) {
				if (idList.length > 0) { idList += ","; }
				idList += sourceForm.elements[a].id.substr(sourceForm.elements[a].id.lastIndexOf('-')+1);
				count = count + 1;
			}
		}
		destinationForm.templateItemIds.value=idList;

		alert("ids="+destinationForm.templateItemIds.value);
		if(count < 2){
		 alert("you must select at least 2 items to create a block");
		 return false;
		}
		else return true;
	}

	return false;
}
<commit_msg>Remove the lert message of passing id info for Block creation<commit_after>//TODO:Javacsript can check if checked Ids hasv the same scale ID
function extractSelectedItems(submitButton) {
	sourceForm = document.getElementById("modifyFormRows");
	destinationForm = submitButton.form;
	var count = 0; //enforce at least 2 check box are checked

	if (sourceForm && destinationForm) {
		var idList = "";

		// look for checkboxes which have IDs of the form "block-12-11" and keep track of the ones that are checked.
		for (var a=0; a < sourceForm.elements.length; a++) {
			if (sourceForm.elements[a].type=="checkbox" &&
sourceForm.elements[a].checked) {
				if (idList.length > 0) { idList += ","; }
				idList += sourceForm.elements[a].id.substr(sourceForm.elements[a].id.lastIndexOf('-')+1);
				count = count + 1;
			}
		}
		destinationForm.templateItemIds.value=idList;

		//alert("ids="+destinationForm.templateItemIds.value);
		if(count < 2){
		 alert("you must select at least 2 items to create a block");
		 return false;
		}
		else return true;
	}

	return false;
}
<|endoftext|>


<commit_before>/*global define */

define(
  [
    'marionette',
    'components/Root.react',
    'react',
    'context',
    'rsvp',
    'models/volume',
    'controllers/providers',
    'components/volumes/VolumeDetailsPage.react',
    'controllers/notifications'
  ],
  function (Marionette, Root, React, context, RSVP, Volume, ProviderController, VolumeDetailsPage, NotificationController) {
    'use strict';

    var Router = Marionette.AppRouter.extend({
      appRoutes: {
        'provider/:provider_id/identity/:identity_id/volumes/:volume_id': 'showVolumeDetail'
      }
    });

    var Controller = Marionette.Controller.extend({

      render: function (content, route) {
        var app = Root({
          session: context.session,
          profile: context.profile,
          content: content,
          route: route || Backbone.history.getFragment()
        });
        React.renderComponent(app, document.getElementById('application'));
      },

      fetchVolume: function (providerId, identityId, volumeId) {
        var promise = new RSVP.Promise(function (resolve, reject) {
          var volume = new Volume({
            identity: {
              provider: providerId,
              id: identityId
            },
            id: volumeId
          });

          volume.fetch({
            success: function (volumeAttributes) {
              resolve(volume);
            },
            error: function () {
              NotificationController.danger("Unknown Volume", "The requested volume does not exist.");
            }
          });
        });
        return promise;
      },

      fetchProviders: function () {
        return ProviderController.getProviders();
      },

      showVolumeDetail: function (providerId, identityId, volumeId) {
//        RSVP.hash({
//          volume: this.fetchVolume(providerId, identityId, volumeId),
//          providers: this.fetchProviders()
//        })
//        .then(function (results) {
//          var content = VolumeDetailsPage({
//            volume: results.volume,
//            providers: results.providers
//          });
//
//          this.render(content, "projects");
//        }.bind(this));

        this.render(VolumeDetailsPage({
          providerId: providerId,
          identityId: identityId,
          volumeId: volumeId
        }), "projects");
      }

    });

    return {
      start: function () {
        var controller = new Controller();
        var router = new Router({
          controller: controller
        });
      }
    }

  });
<commit_msg>remove unused code from volume router<commit_after>/*global define */

define(
  [
    'marionette',
    'components/Root.react',
    'react',
    'context',
    'rsvp',
    'models/volume',
    'controllers/providers',
    'components/volumes/VolumeDetailsPage.react',
    'controllers/notifications'
  ],
  function (Marionette, Root, React, context, VolumeDetailsPage) {
    'use strict';

    var Router = Marionette.AppRouter.extend({
      appRoutes: {
        'provider/:provider_id/identity/:identity_id/volumes/:volume_id': 'showVolumeDetail'
      }
    });

    var Controller = Marionette.Controller.extend({

      render: function (content, route) {
        var app = Root({
          session: context.session,
          profile: context.profile,
          content: content,
          route: route || Backbone.history.getFragment()
        });
        React.renderComponent(app, document.getElementById('application'));
      },

      showVolumeDetail: function (providerId, identityId, volumeId) {
        this.render(VolumeDetailsPage({
          providerId: providerId,
          identityId: identityId,
          volumeId: volumeId
        }), "projects");
      }

    });

    return {
      start: function () {
        var controller = new Controller();
        var router = new Router({
          controller: controller
        });
      }
    }

  });
<|endoftext|>