<commit_before>var _ = require('lodash'),
    utils = require('../utils'),
    driver = utils.getRuntimeDriver(),
    C = driver.constants,
    util = require('util');

exports.make = function(runtimeData, intents, register) {

    var ordersCreatedDuringTick = 0, cachedOrders = {}, cachedHistory = {},
        _incomingTransactions, _outgoingTransactions, _orders;

    function _getOrders(resourceType) {
        if(!resourceType) {
            resourceType = 'all';
        }
        if(!cachedOrders[resourceType]) {
            if(resourceType != 'all' && !_.contains(C.RESOURCES_ALL, resourceType) && !_.contains(C.INTERSHARD_RESOURCES, resourceType)) {
                return {};
            }
            cachedOrders[resourceType] = JSON.parse(JSON.stringify(runtimeData.market.orders[resourceType]) || '{}');
            for(var i in cachedOrders[resourceType]) {
                cachedOrders[resourceType][i].price /= 1000;
            }
        }
        return cachedOrders[resourceType];
    }

    var market = {

        calcTransactionCost: register.wrapFn(function(amount, roomName1, roomName2) {
            var distance = utils.calcRoomsDistance(roomName1, roomName2, true);
            return utils.calcTerminalEnergyCost(amount, distance);
        }),

        getAllOrders: register.wrapFn(function(filter) {
            var orders = _getOrders(filter && filter.resourceType);
            return _.filter(orders, filter);
        }),

        getHistory: register.wrapFn(function(resourceType) {
            if(!resourceType) {
                resourceType = 'all';
            }

            if(!cachedHistory[resourceType]) {
                if(resourceType != 'all' && !_.contains(C.RESOURCES_ALL, resourceType) && !_.contains(C.INTERSHARD_RESOURCES, resourceType)) {
                    return {};
                }
                cachedHistory[resourceType] = JSON.parse(JSON.stringify(runtimeData.market.history[resourceType] || {}));
                return cachedHistory[resourceType];
            }
        }),

        getOrderById: register.wrapFn(function(id) {
            const order = runtimeData.market.orders.all[id] || this.orders[id];
            if(!order) {
                return null;
            }
            const result = JSON.parse(JSON.stringify(order));
            result.price /= 1000;
            return result;
        }),

        createOrder: register.wrapFn(function(type, resourceType, price, totalAmount, roomName) {
            if(!_.contains(C.RESOURCES_ALL, resourceType) && !_.contains(C.INTERSHARD_RESOURCES, resourceType)) {
                return C.ERR_INVALID_ARGS;
            }
            if(type != C.ORDER_BUY && type != C.ORDER_SELL) {
                return C.ERR_INVALID_ARGS;
            }
            price = parseFloat(price);
            totalAmount = parseInt(totalAmount);
            if(!price || price <= 0 || !totalAmount) {
                return C.ERR_INVALID_ARGS;
            }
            if(price * totalAmount * C.MARKET_FEE > this.credits) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }
            if(!_.contains(C.INTERSHARD_RESOURCES, resourceType) &&
                (!roomName || !_.any(runtimeData.userObjects, {type: 'terminal', room: roomName}))) {
                return C.ERR_NOT_OWNER;
            }
            if(_.size(this.orders) + ordersCreatedDuringTick >= C.MARKET_MAX_ORDERS) {
                return C.ERR_FULL;
            }
            ordersCreatedDuringTick++;
            intents.pushByName('global', 'createOrder', {
                type, resourceType, price, totalAmount, roomName
            });
            return C.OK;
        }),

        cancelOrder: register.wrapFn(function(orderId) {
            if(!this.orders[orderId]) {
                return C.ERR_INVALID_ARGS;
            }
            intents.pushByName('global', 'cancelOrder', {orderId}, 50);
            return C.OK;
        }),

        deal: register.wrapFn(function(orderId, amount, targetRoomName) {
            var order = runtimeData.market.orders.all[orderId];
            if(!order) {
                return C.ERR_INVALID_ARGS;
            }
            amount = parseInt(amount);
            if(!amount || amount < 0) {
                return C.ERR_INVALID_ARGS;
            }
            if(_.contains(C.INTERSHARD_RESOURCES, order.resourceType)) {
                if(order.resourceType == C.SUBSCRIPTION_TOKEN) {
                    if(order.type == C.ORDER_BUY && (runtimeData.user.subscriptionTokens||0) < amount) {
                        return C.ERR_NOT_ENOUGH_RESOURCES;
                    }
                }
            }
            else {
                if(!targetRoomName) {
                    return C.ERR_INVALID_ARGS;
                }
                var terminal = _.find(runtimeData.userObjects, {type: 'terminal', room: targetRoomName}),
                    transferCost = this.calcTransactionCost(amount, targetRoomName, order.roomName);
                if(!terminal) {
                    return C.ERR_NOT_OWNER;
                }
                if(terminal[C.RESOURCE_ENERGY] < transferCost) {
                    return C.ERR_NOT_ENOUGH_RESOURCES;
                }
                if(terminal.cooldownTime > runtimeData.time) {
                    return C.ERR_TIRED;
                }
                if(order.type == C.ORDER_BUY) {
                    if(order.resourceType != C.RESOURCE_ENERGY && (!terminal[order.resourceType] || terminal[order.resourceType] < amount) ||
                         order.resourceType == C.RESOURCE_ENERGY && terminal[C.RESOURCE_ENERGY] < amount + transferCost) {
                        return C.ERR_NOT_ENOUGH_RESOURCES;
                    }
                }
            }

            if(order.type == C.ORDER_SELL && (runtimeData.user.money || 0) < amount * order.price) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }

            if(!intents.pushByName('global', 'deal', {orderId, targetRoomName, amount}, 10)) {
                return C.ERR_FULL;
            }
            return C.OK;
        }),

        changeOrderPrice: register.wrapFn(function(orderId, newPrice) {
            var order = this.orders[orderId];
            if(!order) {
                return C.ERR_INVALID_ARGS;
            }
            newPrice = parseFloat(newPrice);
            if(!newPrice || newPrice <= 0) {
                return C.ERR_INVALID_ARGS;
            }
            if(newPrice > order.price && (newPrice - order.price) * order.remainingAmount * C.MARKET_FEE > this.credits) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }

            intents.pushByName('global', 'changeOrderPrice', {
                orderId, newPrice
            }, 50);
            return C.OK;
        }),

        extendOrder: register.wrapFn(function(orderId, addAmount) {
            var order = this.orders[orderId];
            if(!order) {
                return C.ERR_INVALID_ARGS;
            }
            addAmount = parseInt(addAmount);
            if(!addAmount || addAmount <= 0) {
                return C.ERR_INVALID_ARGS;
            }
            if(order.price * addAmount * C.MARKET_FEE > this.credits) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }

            intents.pushByName('global', 'extendOrder', {
                orderId, addAmount
            }, 50);
            return C.OK;
        }),
    };

    Object.defineProperties(market, {

        credits: {
            enumerable: true,
            get() {
                return (runtimeData.user.money || 0)/1000;
            }
        },

        incomingTransactions: {
            enumerable: true,
            get() {
                if(!_incomingTransactions) {
                    _incomingTransactions = _.map(runtimeData.transactions.incoming || [], i => {
                        i.transactionId = "" + i._id;
                        delete i._id;
                        i.sender = i.sender ? {username: runtimeData.users[i.sender].username} : undefined;
                        i.recipient = i.recipient ? {username: runtimeData.users[i.recipient].username} : undefined;
                        return i;
                    });
                }
                return _incomingTransactions;
            }
        },

        outgoingTransactions: {
            enumerable: true,
            get() {
                if(!_outgoingTransactions) {
                    _outgoingTransactions = _.map(runtimeData.transactions.outgoing || [], i => {
                        i.transactionId = ""+i._id;
                        delete i._id;
                        i.sender = i.sender ? {username: runtimeData.users[i.sender].username} : undefined;
                        i.recipient = i.recipient ? {username: runtimeData.users[i.recipient].username} : undefined;
                        return i;
                    });
                }
                return _outgoingTransactions;
            }
        },

        orders: {
            enumerable: true,
            get() {
                if(!_orders) {
                    _orders = _(runtimeData.market.myOrders).map(i => {
                        i.id = ""+i._id;
                        delete i._id;
                        delete i.user;
                        i.price /= 1000;
                        return i;
                    }).indexBy('id').value();
                }
                return _orders;
            }
        }
    });

    return market;
};
<commit_msg>hotfix(processor): Market.deal regression<commit_after>var _ = require('lodash'),
    utils = require('../utils'),
    driver = utils.getRuntimeDriver(),
    C = driver.constants,
    util = require('util');

exports.make = function(runtimeData, intents, register) {

    var ordersCreatedDuringTick = 0, cachedOrders = {}, cachedHistory = {},
        _incomingTransactions, _outgoingTransactions, _orders;

    function _getOrders(resourceType) {
        if(!resourceType) {
            resourceType = 'all';
        }
        if(!cachedOrders[resourceType]) {
            if(resourceType != 'all' && !_.contains(C.RESOURCES_ALL, resourceType) && !_.contains(C.INTERSHARD_RESOURCES, resourceType)) {
                return {};
            }
            cachedOrders[resourceType] = JSON.parse(JSON.stringify(runtimeData.market.orders[resourceType]) || '{}');
            for(var i in cachedOrders[resourceType]) {
                cachedOrders[resourceType][i].price /= 1000;
            }
        }
        return cachedOrders[resourceType];
    }

    var market = {

        calcTransactionCost: register.wrapFn(function(amount, roomName1, roomName2) {
            var distance = utils.calcRoomsDistance(roomName1, roomName2, true);
            return utils.calcTerminalEnergyCost(amount, distance);
        }),

        getAllOrders: register.wrapFn(function(filter) {
            var orders = _getOrders(filter && filter.resourceType);
            return _.filter(orders, filter);
        }),

        getHistory: register.wrapFn(function(resourceType) {
            if(!resourceType) {
                resourceType = 'all';
            }

            if(!cachedHistory[resourceType]) {
                if(resourceType != 'all' && !_.contains(C.RESOURCES_ALL, resourceType) && !_.contains(C.INTERSHARD_RESOURCES, resourceType)) {
                    return {};
                }
                cachedHistory[resourceType] = JSON.parse(JSON.stringify(runtimeData.market.history[resourceType] || {}));
                return cachedHistory[resourceType];
            }
        }),

        getOrderById: register.wrapFn(function(id) {
            const order = runtimeData.market.orders.all[id] || this.orders[id];
            if(!order) {
                return null;
            }
            const result = JSON.parse(JSON.stringify(order));
            result.price /= 1000;
            return result;
        }),

        createOrder: register.wrapFn(function(type, resourceType, price, totalAmount, roomName) {
            if(!_.contains(C.RESOURCES_ALL, resourceType) && !_.contains(C.INTERSHARD_RESOURCES, resourceType)) {
                return C.ERR_INVALID_ARGS;
            }
            if(type != C.ORDER_BUY && type != C.ORDER_SELL) {
                return C.ERR_INVALID_ARGS;
            }
            price = parseFloat(price);
            totalAmount = parseInt(totalAmount);
            if(!price || price <= 0 || !totalAmount) {
                return C.ERR_INVALID_ARGS;
            }
            if(price * totalAmount * C.MARKET_FEE > this.credits) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }
            if(!_.contains(C.INTERSHARD_RESOURCES, resourceType) &&
                (!roomName || !_.any(runtimeData.userObjects, {type: 'terminal', room: roomName}))) {
                return C.ERR_NOT_OWNER;
            }
            if(_.size(this.orders) + ordersCreatedDuringTick >= C.MARKET_MAX_ORDERS) {
                return C.ERR_FULL;
            }
            ordersCreatedDuringTick++;
            intents.pushByName('global', 'createOrder', {
                type, resourceType, price, totalAmount, roomName
            });
            return C.OK;
        }),

        cancelOrder: register.wrapFn(function(orderId) {
            if(!this.orders[orderId]) {
                return C.ERR_INVALID_ARGS;
            }
            intents.pushByName('global', 'cancelOrder', {orderId}, 50);
            return C.OK;
        }),

        deal: register.wrapFn(function(orderId, amount, targetRoomName) {
            var order = runtimeData.market.orders.all[orderId];
            if(!order) {
                return C.ERR_INVALID_ARGS;
            }
            amount = parseInt(amount);
            if(!amount || amount < 0) {
                return C.ERR_INVALID_ARGS;
            }
            if(_.contains(C.INTERSHARD_RESOURCES, order.resourceType)) {
                if(order.resourceType == C.SUBSCRIPTION_TOKEN) {
                    if(order.type == C.ORDER_BUY && (runtimeData.user.subscriptionTokens||0) < amount) {
                        return C.ERR_NOT_ENOUGH_RESOURCES;
                    }
                }
            }
            else {
                if(!targetRoomName) {
                    return C.ERR_INVALID_ARGS;
                }
                var terminal = _.find(runtimeData.userObjects, {type: 'terminal', room: targetRoomName}),
                    transferCost = this.calcTransactionCost(amount, targetRoomName, order.roomName);
                if(!terminal) {
                    return C.ERR_NOT_OWNER;
                }
                if(!terminal.store || terminal.store[C.RESOURCE_ENERGY] < transferCost) {
                    return C.ERR_NOT_ENOUGH_RESOURCES;
                }
                if(terminal.cooldownTime > runtimeData.time) {
                    return C.ERR_TIRED;
                }
                if(order.type == C.ORDER_BUY) {
                    if(order.resourceType != C.RESOURCE_ENERGY && (!terminal.store[order.resourceType] || terminal.store[order.resourceType] < amount) ||
                         order.resourceType == C.RESOURCE_ENERGY && terminal.store[C.RESOURCE_ENERGY] < amount + transferCost) {
                        return C.ERR_NOT_ENOUGH_RESOURCES;
                    }
                }
            }

            if(order.type == C.ORDER_SELL && (runtimeData.user.money || 0) < amount * order.price) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }

            if(!intents.pushByName('global', 'deal', {orderId, targetRoomName, amount}, 10)) {
                return C.ERR_FULL;
            }
            return C.OK;
        }),

        changeOrderPrice: register.wrapFn(function(orderId, newPrice) {
            var order = this.orders[orderId];
            if(!order) {
                return C.ERR_INVALID_ARGS;
            }
            newPrice = parseFloat(newPrice);
            if(!newPrice || newPrice <= 0) {
                return C.ERR_INVALID_ARGS;
            }
            if(newPrice > order.price && (newPrice - order.price) * order.remainingAmount * C.MARKET_FEE > this.credits) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }

            intents.pushByName('global', 'changeOrderPrice', {
                orderId, newPrice
            }, 50);
            return C.OK;
        }),

        extendOrder: register.wrapFn(function(orderId, addAmount) {
            var order = this.orders[orderId];
            if(!order) {
                return C.ERR_INVALID_ARGS;
            }
            addAmount = parseInt(addAmount);
            if(!addAmount || addAmount <= 0) {
                return C.ERR_INVALID_ARGS;
            }
            if(order.price * addAmount * C.MARKET_FEE > this.credits) {
                return C.ERR_NOT_ENOUGH_RESOURCES;
            }

            intents.pushByName('global', 'extendOrder', {
                orderId, addAmount
            }, 50);
            return C.OK;
        }),
    };

    Object.defineProperties(market, {

        credits: {
            enumerable: true,
            get() {
                return (runtimeData.user.money || 0)/1000;
            }
        },

        incomingTransactions: {
            enumerable: true,
            get() {
                if(!_incomingTransactions) {
                    _incomingTransactions = _.map(runtimeData.transactions.incoming || [], i => {
                        i.transactionId = "" + i._id;
                        delete i._id;
                        i.sender = i.sender ? {username: runtimeData.users[i.sender].username} : undefined;
                        i.recipient = i.recipient ? {username: runtimeData.users[i.recipient].username} : undefined;
                        return i;
                    });
                }
                return _incomingTransactions;
            }
        },

        outgoingTransactions: {
            enumerable: true,
            get() {
                if(!_outgoingTransactions) {
                    _outgoingTransactions = _.map(runtimeData.transactions.outgoing || [], i => {
                        i.transactionId = ""+i._id;
                        delete i._id;
                        i.sender = i.sender ? {username: runtimeData.users[i.sender].username} : undefined;
                        i.recipient = i.recipient ? {username: runtimeData.users[i.recipient].username} : undefined;
                        return i;
                    });
                }
                return _outgoingTransactions;
            }
        },

        orders: {
            enumerable: true,
            get() {
                if(!_orders) {
                    _orders = _(runtimeData.market.myOrders).map(i => {
                        i.id = ""+i._id;
                        delete i._id;
                        delete i.user;
                        i.price /= 1000;
                        return i;
                    }).indexBy('id').value();
                }
                return _orders;
            }
        }
    });

    return market;
};
<|endoftext|>


<commit_before>var assert = require('assert');

describe('Question set', function () {
  let QuestionSet = require('../src/QuestionSet');
  let questions;

  beforeEach(function () {
    questions = new QuestionSet();
  });
  afterEach(function() {
    delete questions;
  });

  it('can be chained', function() {
    assert.equal(questions, questions.narrow());
    assert.equal(questions, questions.noanswers);
  });
  it('#array returns question objects', function() {
    let question = questions.array[0];

    assert.notEqual(question.text, undefined);
    assert.notEqual(question.answer, undefined);
  });
  it('#narrow(10).array returns a 10 element array', function() {
    assert.equal(questions.narrow(10).array.length, 10);
  });
  it('#narrow(0).array returns an empty array', function() {
    let result = questions.narrow(0).array;

    assert.equal(result.constructor, Array);
    assert.equal(result.length, 0);
  });
  it('#narrow(1000).array returns all questions', function() {
    let length = questions.array.length;

    assert.equal(questions.narrow(1000).array.length, length);
  });
  it('#noanswers leaves no answer properties', function() {
    let result = questions.noanswers.array;

    for (var i in result) {
      assert.equal(result[i].answer, undefined);
    }
  });
});
<commit_msg>Change test for better coverage<commit_after>var assert = require('assert');

describe('Question set', function () {
  let QuestionSet = require('../src/QuestionSet');
  let questions;

  beforeEach(function () {
    questions = new QuestionSet();
  });
  afterEach(function() {
    delete questions;
  });

  it('can be chained', function() {
    assert.equal(questions, questions.narrow());
    assert.equal(questions, questions.noanswers);
  });
  it('#array returns valid question objects', function() {
    questions.forEach((question) => {
      assert.notEqual(question.text, undefined);
      assert.notEqual(question.answer, undefined);
      assert.notEqual(question.choice_a, undefined);
    });
  });
  it('#narrow(10).array returns a 10 element array', function() {
    assert.equal(questions.narrow(10).array.length, 10);
  });
  it('#narrow(0).array returns an empty array', function() {
    let result = questions.narrow(0).array;

    assert.equal(result.constructor, Array);
    assert.equal(result.length, 0);
  });
  it('#narrow(1000).array returns all questions', function() {
    let length = questions.array.length;

    assert.equal(questions.narrow(1000).array.length, length);
  });
  it('#noanswers leaves no answer properties', function() {
    let result = questions.noanswers.array;

    for (var i in result) {
      assert.equal(result[i].answer, undefined);
    }
  });
});
<|endoftext|>


<commit_before>var assert = require('assert');
require('../triviabot');

module.exports = (function () {
  var Assertion = CactusJuice.Dev.Assertion;

  function createSampleTrivia(options) {
    var trivia = new Trivia(options);
    trivia.createQuestion("What is 3/2?", ["1.5", "1,5"]);
    return trivia;
  }

  return {
    test : function () {
      var assertException = Assertion.exception.bind(Assertion, assert);
      var trivia = new Trivia();
      trivia.createQuestion("What is 1+1?", ["2", "two"]);
      assert.eql(1, trivia.getQuestionCount());

      // Don't rand an index outside of the array.
      // This would probably break...
      assert.isDefined(trivia._getRandomQuestion());
      assert.isDefined(trivia._getRandomQuestion());
      assert.isDefined(trivia._getRandomQuestion());
      assert.isDefined(trivia._getRandomQuestion());

      trivia.createQuestion("What is 0-0?", ["0", "zero"]);

      var events = [];
      var startTriggers = 0;
      var newQuestionTriggers = 0;
      trivia.subscribe("Started", function () {
        startTriggers++;
        events.push("Started");
      });

      // Can only get question string if trivia is started.
      assertException(/:getCurrentQuestionString:.+not started/i, trivia.getCurrentQuestionString.bind(trivia));

      trivia.subscribe("NewQuestion", function () {
        // Current question should be set when trivia starts.
        assert.ok(typeof trivia.getCurrentQuestionString() === "string");
        newQuestionTriggers++;
        events.push("NewQuestion");
      });
      trivia.start();
      assert.eql(1, startTriggers);
      assertException(/trivia is already started/i, trivia.start.bind(trivia));
      assert.eql(1, startTriggers);
      assert.eql(1, newQuestionTriggers);

      // Send Start before NewQuestion.
      assert.eql("Started", events[0]);
      assert.eql("NewQuestion", events[1]);

      var triggered = false;
      trivia.subscribe("NewQuestion", function () {
        triggered = true;
        assert.ok(typeof trivia.getCurrentQuestionString() === "string");
      });
      trivia.newQuestion();
      assert.eql(2, newQuestionTriggers);
      assert.ok(triggered);

      trivia = new Trivia();

      // Need to have questions added before starting.
      assert.ok(!trivia.hasQuestions());
      assertException(/:start:.+has no questions/i, trivia.start.bind(trivia));

      // Stopping.
      trivia = new Trivia();
      assert.ok(!trivia.isStarted());
      trivia.createQuestion("What is 2*2?", ["4", "four"]);
      trivia.start();
      assert.ok(trivia.isStarted());
      var stoppedTriggered = false;
      trivia.subscribe("Stopped", function () {
        stoppedTriggered = true;
      });
      trivia.stop();
      assert.ok(!trivia.isStarted());
      assert.ok(stoppedTriggered);

      assertException(/:stop:.+already stopped/i, trivia.stop.bind(trivia));

      assertException(/:newQuestion:.+is stopped/i, trivia.newQuestion.bind(trivia));
    },
    answering : function () {
      var trivia = createSampleTrivia();
      trivia.start();
      var answeredTriggered = false;
      trivia.subscribe("Answered", function (trivia, person) {
        assert.eql("me", person);
        answeredTriggered = true;
      });
      trivia.answer("me", "1.5");
      assert.ok(answeredTriggered);
    },
    score : function () {
      var trivia = createSampleTrivia();
      trivia.start();
      trivia.answer("me", "1");
      trivia.answer("me", "1.5");
      assert.eql(1, trivia.getScore("me"));
      trivia.answer("me", "1.5");
      assert.eql(2, trivia.getScore("me"));
      assert.eql(0, trivia.getScore("other"));
    },
    "question number" : function () {
      var trivia = createSampleTrivia();
      trivia.start();
      assert.eql(1, trivia.getQuestionNumber());
      trivia.answer("me", "1.5");
      assert.eql(2, trivia.getQuestionNumber());
    },
    "times up" : function () {
      var trivia = createSampleTrivia();
      var newQuestionTriggers = 0;
      trivia.subscribe("NewQuestion", function () {
        newQuestionTriggers++;
      });
      var timesUpTriggers = 0;
      trivia.subscribe("TimesUp", function () {
        timesUpTriggers++;
      });
      trivia.start();
      assert.eql(1, newQuestionTriggers);
      trivia.timesUp();
      assert.eql(1, timesUpTriggers);
      assert.eql(2, newQuestionTriggers);
    },
    "stop after unanswered streak" : function () {
      var trivia = createSampleTrivia({
        stopAfterUnansweredStreak : 3
      });
      var stoppedTriggered = false;
      trivia.subscribe("Stopped", function () {
        stoppedTriggered = true;
      });
      trivia.start();
      trivia.timesUp();
      trivia.timesUp();
      assert.ok(!stoppedTriggered, "Premature stop.");
      trivia.timesUp();
      assert.ok(stoppedTriggered, "Did not stop.");
      // Reset counter after stop.
      stoppedTriggered = false;
      trivia.start();
      trivia.timesUp();
      assert.ok(!stoppedTriggered, "Premature stop after restart.");
    },
    serialize : function () {
      var trivia = new Trivia();
      trivia.createQuestion("foo", ["bar", "baz"]);
      trivia.start();
      trivia.answer("me", "bar");
      var h = trivia.serialize();
      assert.eql('[{"question":"foo","answers":["bar","baz"]}]',
                JSON.stringify(h.questions));
      assert.eql(1, h.score[0][1]);
    },
    unserialize : function () {
      var trivia = Trivia.unserialize({
        questions : [{
          question : "foo",
          answers : ["bar", "baz"]
        }, {
          question : "what is 1+1?",
          answers : ["2", "two"]
        }],
        score : [
          ["me", 1]
        ]
      });
      assert.eql(2, trivia.getQuestionCount());
      assert.eql(1, trivia.getScore("me"));
    },
    serializeUnserializeCompoundPerson : function () {
      Class("Person2", {
        has : {
          name : null
        },
        methods : {
          serialize : function () {
            return {
              name : this.name
            };
          }
        }
      });
      var p;
      var trivia = Trivia.unserialize({
        questions : [{
          question : "foo",
          answers : ["bar"]
        }],
        score : [[{ name : "x" }, 1]]
      }, {
        personUnserializer : function (h) {
          p = new Person2({
            name : h.name
          });
          return p;
        }
      });
      assert.eql(1, trivia.getScore(p));
    }
  };
})();
<commit_msg>Person test class no longer clashes with global.<commit_after>var assert = require('assert');
require('../triviabot');

module.exports = (function () {
  var Assertion = CactusJuice.Dev.Assertion;

  function createSampleTrivia(options) {
    var trivia = new Trivia(options);
    trivia.createQuestion("What is 3/2?", ["1.5", "1,5"]);
    return trivia;
  }

  return {
    test : function () {
      var assertException = Assertion.exception.bind(Assertion, assert);
      var trivia = new Trivia();
      trivia.createQuestion("What is 1+1?", ["2", "two"]);
      assert.eql(1, trivia.getQuestionCount());

      // Don't rand an index outside of the array.
      // This would probably break...
      assert.isDefined(trivia._getRandomQuestion());
      assert.isDefined(trivia._getRandomQuestion());
      assert.isDefined(trivia._getRandomQuestion());
      assert.isDefined(trivia._getRandomQuestion());

      trivia.createQuestion("What is 0-0?", ["0", "zero"]);

      var events = [];
      var startTriggers = 0;
      var newQuestionTriggers = 0;
      trivia.subscribe("Started", function () {
        startTriggers++;
        events.push("Started");
      });

      // Can only get question string if trivia is started.
      assertException(/:getCurrentQuestionString:.+not started/i, trivia.getCurrentQuestionString.bind(trivia));

      trivia.subscribe("NewQuestion", function () {
        // Current question should be set when trivia starts.
        assert.ok(typeof trivia.getCurrentQuestionString() === "string");
        newQuestionTriggers++;
        events.push("NewQuestion");
      });
      trivia.start();
      assert.eql(1, startTriggers);
      assertException(/trivia is already started/i, trivia.start.bind(trivia));
      assert.eql(1, startTriggers);
      assert.eql(1, newQuestionTriggers);

      // Send Start before NewQuestion.
      assert.eql("Started", events[0]);
      assert.eql("NewQuestion", events[1]);

      var triggered = false;
      trivia.subscribe("NewQuestion", function () {
        triggered = true;
        assert.ok(typeof trivia.getCurrentQuestionString() === "string");
      });
      trivia.newQuestion();
      assert.eql(2, newQuestionTriggers);
      assert.ok(triggered);

      trivia = new Trivia();

      // Need to have questions added before starting.
      assert.ok(!trivia.hasQuestions());
      assertException(/:start:.+has no questions/i, trivia.start.bind(trivia));

      // Stopping.
      trivia = new Trivia();
      assert.ok(!trivia.isStarted());
      trivia.createQuestion("What is 2*2?", ["4", "four"]);
      trivia.start();
      assert.ok(trivia.isStarted());
      var stoppedTriggered = false;
      trivia.subscribe("Stopped", function () {
        stoppedTriggered = true;
      });
      trivia.stop();
      assert.ok(!trivia.isStarted());
      assert.ok(stoppedTriggered);

      assertException(/:stop:.+already stopped/i, trivia.stop.bind(trivia));

      assertException(/:newQuestion:.+is stopped/i, trivia.newQuestion.bind(trivia));
    },
    answering : function () {
      var trivia = createSampleTrivia();
      trivia.start();
      var answeredTriggered = false;
      trivia.subscribe("Answered", function (trivia, person) {
        assert.eql("me", person);
        answeredTriggered = true;
      });
      trivia.answer("me", "1.5");
      assert.ok(answeredTriggered);
    },
    score : function () {
      var trivia = createSampleTrivia();
      trivia.start();
      trivia.answer("me", "1");
      trivia.answer("me", "1.5");
      assert.eql(1, trivia.getScore("me"));
      trivia.answer("me", "1.5");
      assert.eql(2, trivia.getScore("me"));
      assert.eql(0, trivia.getScore("other"));
    },
    "question number" : function () {
      var trivia = createSampleTrivia();
      trivia.start();
      assert.eql(1, trivia.getQuestionNumber());
      trivia.answer("me", "1.5");
      assert.eql(2, trivia.getQuestionNumber());
    },
    "times up" : function () {
      var trivia = createSampleTrivia();
      var newQuestionTriggers = 0;
      trivia.subscribe("NewQuestion", function () {
        newQuestionTriggers++;
      });
      var timesUpTriggers = 0;
      trivia.subscribe("TimesUp", function () {
        timesUpTriggers++;
      });
      trivia.start();
      assert.eql(1, newQuestionTriggers);
      trivia.timesUp();
      assert.eql(1, timesUpTriggers);
      assert.eql(2, newQuestionTriggers);
    },
    "stop after unanswered streak" : function () {
      var trivia = createSampleTrivia({
        stopAfterUnansweredStreak : 3
      });
      var stoppedTriggered = false;
      trivia.subscribe("Stopped", function () {
        stoppedTriggered = true;
      });
      trivia.start();
      trivia.timesUp();
      trivia.timesUp();
      assert.ok(!stoppedTriggered, "Premature stop.");
      trivia.timesUp();
      assert.ok(stoppedTriggered, "Did not stop.");
      // Reset counter after stop.
      stoppedTriggered = false;
      trivia.start();
      trivia.timesUp();
      assert.ok(!stoppedTriggered, "Premature stop after restart.");
    },
    serialize : function () {
      var trivia = new Trivia();
      trivia.createQuestion("foo", ["bar", "baz"]);
      trivia.start();
      trivia.answer("me", "bar");
      var h = trivia.serialize();
      assert.eql('[{"question":"foo","answers":["bar","baz"]}]',
                JSON.stringify(h.questions));
      assert.eql(1, h.score[0][1]);
    },
    unserialize : function () {
      var trivia = Trivia.unserialize({
        questions : [{
          question : "foo",
          answers : ["bar", "baz"]
        }, {
          question : "what is 1+1?",
          answers : ["2", "two"]
        }],
        score : [
          ["me", 1]
        ]
      });
      assert.eql(2, trivia.getQuestionCount());
      assert.eql(1, trivia.getScore("me"));
    },
    serializeUnserializeCompoundPerson : function () {
      Class("Person", {
        has : {
          name : null
        },
        methods : {
          serialize : function () {
            return {
              name : this.name
            };
          }
        }
      });
      var p;
      var trivia = Trivia.unserialize({
        questions : [{
          question : "foo",
          answers : ["bar"]
        }],
        score : [[{ name : "x" }, 1]]
      }, {
        personUnserializer : function (h) {
          p = new Person({
            name : h.name
          });
          return p;
        }
      });
      assert.eql(1, trivia.getScore(p));
    }
  };
})();
<|endoftext|>


<commit_before>"use strict";

var Config = {
  supportsHistory : !!(window.history && history.pushState),
  inMaintenanceMode : false,

  treeName: 'mozilla-central',
  hgBaseURL: "https://hg.mozilla.org/",
  hgURL: "https://hg.mozilla.org/mozilla-central/",
  hgRevURL: "https://hg.mozilla.org/mozilla-central/rev/",
  hgPushlogURL: "https://hg.mozilla.org/mozilla-central/pushloghtml?changeset=",
  showBugURL: "https://bugzilla.mozilla.org/show_bug.cgi?id=",
  productionURL: "https://tbpl.mozilla.org/mcmerge/",

  // Here be dragons
  versionRE: /^mozilla\d+$/i,
  csetInputRE: /^(tip|[\da-f]{12,40})$/i,
  csetIDRE: /\b([\da-f]{12,40})\b/ig,
  leaveOpenRE: /(?:leave|keep)(?:-|\s+)open/i,
  bugNumRE: /\b(\d{4,7})\b/g,
  strictBugNumRE: /^(\d{4,7})$/,
  mergeRE: /\bmerg(ed?|ing)\b/i,
  backoutRE: /back(ing|ed)?\s*out/i,
  revertRE: /revert(ing)?/i,
  partialRevertRE: /(?:from|in)(?:\s+(?:rev(?:ision)?|c(?:hange)?set))?\s+([\da-f]{12,40})/i,
  partialTestRE: /test\s+for/i,
  revertRangeRE: /revert(?:ing)?\s+(?:\S+\s+)?to(?:\s+(?:rev(?:ision)?|c(?:hange)set))?\s+([\da-f]{12,40})/i,
  csetRangeRE: /\b([\da-f]{12,40})\s*(?:to|:|-|through)\s*([\da-f]{12,40})/i,
  hgRevRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\//ig,
  hgRevFullRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\/[\da-f]{12}/ig,
  hgPushlogRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/pushloghtml\?changeset=/ig,
  emailRE: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,   // I'm not looking for RFC 822 compliance here!

  // The many ways bug numbers are specified
  bugRE1: /b(?:ug)?=(\d{4,7})\b/i,                       // e.g. b=XXXXXX
  bugRE2: /^fix(?:es)?\s*(?:for\s*)?(\d{4,7})\b/i,                         // Fix is sometimes used as a synonym for bug
  bugRE3: /to\s+fix\s+bug\s+(\d{4,7})/i,
  bugRE4: /\((\d{4,7}), r=/i,                          // e.g. JS-team style (XXXXXX, r=foo)
  bugRE5: /but\s*(\d{4,7})/i,                          // The typo but XXXXXX happens quite often
  bugRE6: /\bb(?:u?g(?:zilla)?)?:?\s*#?(\d{4,7})\b/i,  // The catchall
  bugRE7: /^(\d{4,7})\b/i,                              // e.g. XXXXXX,

  repoMergeRE: "\\s+(&|and|with|<->|<>|-?->|(in|on)?to)\\s+",

  mcSynonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"],

  rewriteTrees: {
    "thunderbird-trunk": "comm-central",
    "thunderbird-aurora": "comm-aurora",
    "thunderbird-beta": "comm-beta",
    "thunderbird-esr24": "comm-esr24",
  },

  treeInfo: {
    "mozilla-central": {
      repo: "mozilla-central",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"]
    },
    "b2g-inbound": {
      repo: "integration/b2g-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["b2g-inbound", "b-i", "bi"]
    },
    "mozilla-inbound": {
      repo: "integration/mozilla-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["mozilla-inbound", "inbound", "m-i", "mi"]
    },
    "mozilla-aurora": {
      repo: "releases/mozilla-aurora",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-aurora", "mozilla-aurora"]
    },
    "mozilla-beta": {
      repo: "releases/mozilla-beta",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-beta", "mozilla-beta"]
    },
    "mozilla-release": {
      repo: "releases/mozilla-release",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-release", "mozilla-release"]
    },
    "mozilla-esr24": {
      repo: "releases/mozilla-esr24",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr24", "releases/mozilla-esr24", "mozilla-esr24"]
    },
    "mozilla-b2g26-v1.2": {
      repo: "releases/mozilla-b2g26_v1_2",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g26", "mozilla-b2g26", "mozilla-b2g26_v1_2", "v1.2"]
    },
    "mozilla-b2g28-v1.3": {
      repo: "releases/mozilla-b2g28_v1_3",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28", "mozilla-b2g28", "mozilla-b2g28_v1_3", "v1.3"]
    },
    "mozilla-b2g28-v1.3t": {
      repo: "releases/mozilla-b2g28_v1_3t",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28t", "mozilla-b2g28t", "mozilla-b2g28_v1_3t", "v1.3t"]
    },
    "mozilla-b2g18": {
      repo: "releases/mozilla-b2g18",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g18", "mozilla-b2g18"]
    },
    "mozilla-b2g18-v1.1.0hd": {
      repo: "releases/mozilla-b2g18_v1_1_0_hd",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g18_v1_1_0_hd", "b2g18_v1_1_0hd", "mozilla-b2g18_v1_1_0_hd", "v1.1hd"]
    },
    "jetpack": {
      repo: "projects/addon-sdk",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["projects/addon-sdk", "addon-sdk"]
    },
    "build-system": {
      repo: "projects/build-system",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["build-system", "b-s", "bs"]
    },
    "fx-team": {
      repo: "integration/fx-team",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["fx-team"],
      additions: "[fixed-in-fx-team]"
    },
    "graphics": {
      repo: "projects/graphics",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/graphics", "graphics"]
    },
    "ionmonkey": {
      repo: "projects/ionmonkey",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/ionmonkey", "ionmonkey", "im"]
    },
    "services-central": {
      repo: "services/services-central",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["services-central", "s-c", "sc", "services/services-central"],
      additions: "[fixed in services]"
    },
    "ux": {
      unconditionalFlag: false,
      repo: "projects/ux",
      synonyms: ["ux", "projects/ux"]
    },
    "alder": {
      repo: "projects/alder",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["alder", "projects/alder"]
    },
    "ash": {
      repo: "projects/ash",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["ash", "projects/ash"]
    },
    "birch": {
      repo: "projects/birch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/birch", "birch"]
    },
    "cedar": {
      repo: "projects/cedar",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cedar", "cedar"]
    },
    "cypress": {
      repo: "projects/cypress",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cypress", "cypress"]
    },
    "date": {
      repo: "projects/date",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/date", "date"]
    },
    "elm": {
      repo: "projects/elm",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/elm", "elm"]
    },
    "fig": {
      repo: "projects/fig",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/fig", "fig"],
      additions: "[fixed-in-fig]"
    },
    "gum": {
      repo: "projects/gum",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/gum", "gum"]
    },
    "holly": {
      repo: "projects/holly",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/holly", "holly"]
    },
    "jamun": {
      repo: "projects/jamun",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jamun", "jamun"]
    },
    "larch": {
      repo: "projects/larch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/larch", "larch"]
    },
    "maple": {
      repo: "projects/maple",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/maple", "maple"]
    },
    "oak": {
      repo: "projects/oak",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/oak", "oak"]
    },
    "pine": {
      repo: "projects/pine",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/pine", "pine"]
    },
    "comm-central": {
      repo: "comm-central",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["comm-central", "c-c"]
    },
    "comm-aurora": {
      repo: "releases/comm-aurora",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-aurora", "comm-aurora"]
    },
    "comm-beta": {
      repo: "releases/comm-beta",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-beta", "comm-beta"]
    },
    "comm-esr24": {
      repo: "releases/comm-esr24",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr24", "comm-esr24", "comm-release"]
    }
  }
};

Config.bugNumberREs = [Config.bugRE1, Config.bugRE2, Config.bugRE3,
                       Config.bugRE4, Config.bugRE5, Config.bugRE6, Config.bugRE7];

(function () {
  var base = Config.hgBaseURL;
  for (var treeName in Config.treeInfo) {
    var repo = Config.treeInfo[treeName].repo;
    Config.treeInfo[treeName]['hgURL'] = base + repo + '/';
    Config.treeInfo[treeName]['hgRevURL'] = base + repo + '/rev/';
    Config.treeInfo[treeName]['hgPushlogURL'] = base + repo + '/pushloghtml?changeset=';
    Config.treeInfo[treeName]['hgRevRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/', 'ig');
    Config.treeInfo[treeName]['hgRevFullRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/[\\da-f]{12}', 'ig');
    Config.treeInfo[treeName]['hgPushlogRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/pushloghtml\\?changeset=', 'ig');
  }
})();

// Detect input type="email" support
$(document).ready(function() {
  Config.needsValidation = $('#username').prop('type') != 'email';
});
<commit_msg>bug 998539 - add b2g30_v1_4. r=ryanvm<commit_after>"use strict";

var Config = {
  supportsHistory : !!(window.history && history.pushState),
  inMaintenanceMode : false,

  treeName: 'mozilla-central',
  hgBaseURL: "https://hg.mozilla.org/",
  hgURL: "https://hg.mozilla.org/mozilla-central/",
  hgRevURL: "https://hg.mozilla.org/mozilla-central/rev/",
  hgPushlogURL: "https://hg.mozilla.org/mozilla-central/pushloghtml?changeset=",
  showBugURL: "https://bugzilla.mozilla.org/show_bug.cgi?id=",
  productionURL: "https://tbpl.mozilla.org/mcmerge/",

  // Here be dragons
  versionRE: /^mozilla\d+$/i,
  csetInputRE: /^(tip|[\da-f]{12,40})$/i,
  csetIDRE: /\b([\da-f]{12,40})\b/ig,
  leaveOpenRE: /(?:leave|keep)(?:-|\s+)open/i,
  bugNumRE: /\b(\d{4,7})\b/g,
  strictBugNumRE: /^(\d{4,7})$/,
  mergeRE: /\bmerg(ed?|ing)\b/i,
  backoutRE: /back(ing|ed)?\s*out/i,
  revertRE: /revert(ing)?/i,
  partialRevertRE: /(?:from|in)(?:\s+(?:rev(?:ision)?|c(?:hange)?set))?\s+([\da-f]{12,40})/i,
  partialTestRE: /test\s+for/i,
  revertRangeRE: /revert(?:ing)?\s+(?:\S+\s+)?to(?:\s+(?:rev(?:ision)?|c(?:hange)set))?\s+([\da-f]{12,40})/i,
  csetRangeRE: /\b([\da-f]{12,40})\s*(?:to|:|-|through)\s*([\da-f]{12,40})/i,
  hgRevRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\//ig,
  hgRevFullRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/rev\/[\da-f]{12}/ig,
  hgPushlogRE: /https?:\/\/hg.mozilla.org\/mozilla-central\/pushloghtml\?changeset=/ig,
  emailRE: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,   // I'm not looking for RFC 822 compliance here!

  // The many ways bug numbers are specified
  bugRE1: /b(?:ug)?=(\d{4,7})\b/i,                       // e.g. b=XXXXXX
  bugRE2: /^fix(?:es)?\s*(?:for\s*)?(\d{4,7})\b/i,                         // Fix is sometimes used as a synonym for bug
  bugRE3: /to\s+fix\s+bug\s+(\d{4,7})/i,
  bugRE4: /\((\d{4,7}), r=/i,                          // e.g. JS-team style (XXXXXX, r=foo)
  bugRE5: /but\s*(\d{4,7})/i,                          // The typo but XXXXXX happens quite often
  bugRE6: /\bb(?:u?g(?:zilla)?)?:?\s*#?(\d{4,7})\b/i,  // The catchall
  bugRE7: /^(\d{4,7})\b/i,                              // e.g. XXXXXX,

  repoMergeRE: "\\s+(&|and|with|<->|<>|-?->|(in|on)?to)\\s+",

  mcSynonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"],

  rewriteTrees: {
    "thunderbird-trunk": "comm-central",
    "thunderbird-aurora": "comm-aurora",
    "thunderbird-beta": "comm-beta",
    "thunderbird-esr24": "comm-esr24",
  },

  treeInfo: {
    "mozilla-central": {
      repo: "mozilla-central",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["mozilla-central", "central", "m-c", "mc", "mozilla central"]
    },
    "b2g-inbound": {
      repo: "integration/b2g-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["b2g-inbound", "b-i", "bi"]
    },
    "mozilla-inbound": {
      repo: "integration/mozilla-inbound",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["mozilla-inbound", "inbound", "m-i", "mi"]
    },
    "mozilla-aurora": {
      repo: "releases/mozilla-aurora",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-aurora", "mozilla-aurora"]
    },
    "mozilla-beta": {
      repo: "releases/mozilla-beta",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-beta", "mozilla-beta"]
    },
    "mozilla-release": {
      repo: "releases/mozilla-release",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["releases/mozilla-release", "mozilla-release"]
    },
    "mozilla-esr24": {
      repo: "releases/mozilla-esr24",
      trackedTree: true,
      unconditionalFlag: false,
      synonyms: ["esr24", "releases/mozilla-esr24", "mozilla-esr24"]
    },
    "mozilla-b2g26-v1.2": {
      repo: "releases/mozilla-b2g26_v1_2",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g26", "mozilla-b2g26", "mozilla-b2g26_v1_2", "v1.2"]
    },
    "mozilla-b2g30-v1.4": {
      repo: "releases/mozilla-b2g30_v1_4",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g30", "mozilla-b2g30", "mozilla-b2g30_v1_4", "v1.4"]
    },
    "mozilla-b2g28-v1.3": {
      repo: "releases/mozilla-b2g28_v1_3",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28", "mozilla-b2g28", "mozilla-b2g28_v1_3", "v1.3"]
    },
    "mozilla-b2g28-v1.3t": {
      repo: "releases/mozilla-b2g28_v1_3t",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g28t", "mozilla-b2g28t", "mozilla-b2g28_v1_3t", "v1.3t"]
    },
    "mozilla-b2g18": {
      repo: "releases/mozilla-b2g18",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g18", "mozilla-b2g18"]
    },
    "mozilla-b2g18-v1.1.0hd": {
      repo: "releases/mozilla-b2g18_v1_1_0_hd",
      unconditionalFlag: true,
      trackedTree: true,
      synonyms: ["b2g18_v1_1_0_hd", "b2g18_v1_1_0hd", "mozilla-b2g18_v1_1_0_hd", "v1.1hd"]
    },
    "jetpack": {
      repo: "projects/addon-sdk",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["projects/addon-sdk", "addon-sdk"]
    },
    "build-system": {
      repo: "projects/build-system",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["build-system", "b-s", "bs"]
    },
    "fx-team": {
      repo: "integration/fx-team",
      trackedTree: false,
      unconditionalFlag: false,
      synonyms: ["fx-team"],
      additions: "[fixed-in-fx-team]"
    },
    "graphics": {
      repo: "projects/graphics",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/graphics", "graphics"]
    },
    "ionmonkey": {
      repo: "projects/ionmonkey",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/ionmonkey", "ionmonkey", "im"]
    },
    "services-central": {
      repo: "services/services-central",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["services-central", "s-c", "sc", "services/services-central"],
      additions: "[fixed in services]"
    },
    "ux": {
      unconditionalFlag: false,
      repo: "projects/ux",
      synonyms: ["ux", "projects/ux"]
    },
    "alder": {
      repo: "projects/alder",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["alder", "projects/alder"]
    },
    "ash": {
      repo: "projects/ash",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["ash", "projects/ash"]
    },
    "birch": {
      repo: "projects/birch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/birch", "birch"]
    },
    "cedar": {
      repo: "projects/cedar",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cedar", "cedar"]
    },
    "cypress": {
      repo: "projects/cypress",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/cypress", "cypress"]
    },
    "date": {
      repo: "projects/date",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/date", "date"]
    },
    "elm": {
      repo: "projects/elm",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/elm", "elm"]
    },
    "fig": {
      repo: "projects/fig",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/fig", "fig"],
      additions: "[fixed-in-fig]"
    },
    "gum": {
      repo: "projects/gum",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/gum", "gum"]
    },
    "holly": {
      repo: "projects/holly",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/holly", "holly"]
    },
    "jamun": {
      repo: "projects/jamun",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/jamun", "jamun"]
    },
    "larch": {
      repo: "projects/larch",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/larch", "larch"]
    },
    "maple": {
      repo: "projects/maple",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/maple", "maple"]
    },
    "oak": {
      repo: "projects/oak",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/oak", "oak"]
    },
    "pine": {
      repo: "projects/pine",
      unconditionalFlag: false,
      trackedTree: false,
      synonyms: ["projects/pine", "pine"]
    },
    "comm-central": {
      repo: "comm-central",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["comm-central", "c-c"]
    },
    "comm-aurora": {
      repo: "releases/comm-aurora",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-aurora", "comm-aurora"]
    },
    "comm-beta": {
      repo: "releases/comm-beta",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-beta", "comm-beta"]
    },
    "comm-esr24": {
      repo: "releases/comm-esr24",
      unconditionalFlag: false,
      trackedTree: true,
      synonyms: ["releases/comm-esr24", "comm-esr24", "comm-release"]
    }
  }
};

Config.bugNumberREs = [Config.bugRE1, Config.bugRE2, Config.bugRE3,
                       Config.bugRE4, Config.bugRE5, Config.bugRE6, Config.bugRE7];

(function () {
  var base = Config.hgBaseURL;
  for (var treeName in Config.treeInfo) {
    var repo = Config.treeInfo[treeName].repo;
    Config.treeInfo[treeName]['hgURL'] = base + repo + '/';
    Config.treeInfo[treeName]['hgRevURL'] = base + repo + '/rev/';
    Config.treeInfo[treeName]['hgPushlogURL'] = base + repo + '/pushloghtml?changeset=';
    Config.treeInfo[treeName]['hgRevRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/', 'ig');
    Config.treeInfo[treeName]['hgRevFullRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/rev\/[\\da-f]{12}', 'ig');
    Config.treeInfo[treeName]['hgPushlogRE'] = new RegExp('https?:\/\/hg.mozilla.org\/' + repo + '\/pushloghtml\\?changeset=', 'ig');
  }
})();

// Detect input type="email" support
$(document).ready(function() {
  Config.needsValidation = $('#username').prop('type') != 'email';
});
<|endoftext|>


<commit_before>/*
 * Code for laying out rectangles, given that layout is also happening
 * in adjacent blocks at the same time
 *
 * This code does a lot of linear searching; n should be low enough that
 * it's not a problem but if it turns out to be, some of it can be changed to
 * binary searching without too much work.  Another possibility is to merge
 * contour spans and give up some packing closeness in exchange for speed
 * (the code already merges spans that have the same x-coord and are vertically
 * contiguous).
 */

function Contour(top) {
    /*
     * A contour is described by a set of vertical lines of varying heights,
     * like this:
     *                         |
     *                         |
     *               |
     *                   |
     *                   |
     *                   |
     *
     * The contour is the union of the rectangles ending on the right side
     * at those lines, and extending leftward toward negative infinity.
     *
     * <=======================|
     * <=======================|
     * <==========|
     * <=================|
     * <=================|
     * <=================|
     *
     * x -->
     *
     * As we add new vertical spans, the contour expands, either downward
     * or in the direction of increasing x.
     */
    // takes: top, a number indicating where the first span of the contour
    // will go
    if (top === undefined) top = 0;

    // spans is an array of {top, x, height} objects representing
    // the boundaries of the contour
    // they're always sorted by top
    this.spans = [{top: top, x: Infinity, height: 0}];
}

// finds a space in the contour into which the given span fits
// (i.e., the given span has higher x than the contour over its vertical span)
// returns an ojbect {above, count}; above is the index of the last span above
// where the given span will fit, count is the number of spans being
// replaced by the given span
Contour.prototype.getFit = function(x, height, minTop) {
    var aboveBottom, curSpan;
    var above = 0;
    if (minTop) {
        // set above = (index of the first span that starts below minTop)
        for (; this.spans[above].top < minTop; above++) {
            if (above >= (this.spans.length - 1))
                return {above: this.spans.length - 1, count: 0};
        }
    }
    // slide down the contour
    ABOVE: for (; above < this.spans.length; above++) {
        aboveBottom = this.spans[above].top + this.spans[above].height;
        for (var count = 1; above + count < this.spans.length; count++) {
            curSpan = this.spans[above + count];
            if ((aboveBottom + height) <= curSpan.top) {
                // the given span fits between span[above] and
                // curSpan, keeping curSpan
                return {above: above, count: count - 1};
            }
            if (curSpan.x > x) {
                // the span at [above + count] overlaps the given span,
                // so we continue down the contour
                continue ABOVE;
            }
            if ((curSpan.x <= x) &&
                ((aboveBottom + height) < (curSpan.top + curSpan.height))) {
                // the given span partially covers curSpan, and
                // will overlap it, so we keep curSpan
                return {above: above, count: count - 1};
            }
        }
        // the given span fits below span[above], replacing any
        // lower spans in the contour
        return {above: above, count: count - 1};
    }
    // the given span fits at the end of the contour, replacing no spans
    return {above: above, count: 0};
};

// add the given span to this contour where it fits, as given
// by getFit
Contour.prototype.insertFit = function(fit, x, top, height) {
    // if the previous span and the current span have the same x-coord,
    // and are vertically contiguous, merge them.
    var prevSpan = this.spans[fit.above];
    if ((Math.abs(prevSpan.x - x) < 1)
        && (Math.abs((prevSpan.top + prevSpan.height) - top) < 1) ) {
        prevSpan.height = (top + height) - prevSpan.top;
        // a bit of slop here is conservative if we take the max
        // (means things might get laid out slightly farther apart
        // than they would otherwise)
        prevSpan.x = Math.max(prevSpan.x, x);
        this.spans.splice(fit.above + 1, fit.count);
    } else {
        this.spans.splice(fit.above + 1, fit.count,
                          {
                              top: top,
                              x: x,
                              height: height
                          });
    }
};

// add the given span to this contour at the given location, if
// it would extend the contour
Contour.prototype.unionWith = function(x, top, height) {
    var startBottom, startIndex, endIndex, startSpan, endSpan;
    var bottom = top + height;
    START: for (startIndex = 0; startIndex < this.spans.length; startIndex++) {
        startSpan = this.spans[startIndex];
        startBottom = startSpan.top + startSpan.height;
        if (startSpan.top > top) {
            // the given span extends above an existing span
            endIndex = startIndex;
            break START;
        }
        if (startBottom > top) {
            // if startSpan covers (at least some of) the given span,
            if (startSpan.x >= x) {
                var covered = startBottom - top;
                // we don't have to worry about the covered area any more
                top += covered;
                height -= covered;
                // if we've eaten up the whole span, then it's submerged
                // and we don't have to do anything
                if (top >= bottom) return;
                continue;
            } else {
                // find the first span not covered by the given span
                for (endIndex = startIndex;
                     endIndex < this.spans.length;
                     endIndex++) {
                    endSpan = this.spans[endIndex];
                    // if endSpan extends below or to the right
                    // of the given span, then we need to keep it
                    if (((endSpan.top + endSpan.height) > bottom)
                        || endSpan.x > x) {
                        break START;
                    }
                }
                break START;
            }
        }
    }

    // if the previous span and the current span have the same x-coord,
    // and are vertically contiguous, merge them.
    var prevSpan = this.spans[startIndex - 1];
    if ((Math.abs(prevSpan.x - x) < 1)
        && (Math.abs((prevSpan.top + prevSpan.height) - top) < 1) ) {
        prevSpan.height = (top + height) - prevSpan.top;
        prevSpan.x = Math.max(prevSpan.x, x);
        this.spans.splice(startIndex, endIndex - startIndex);
    } else {
        this.spans.splice(startIndex, endIndex - startIndex,
                          {
                              top: top,
                              x: x,
                              height: height
                          });
    }
};

// returns the top of the to-be-added span that fits into "fit"
// (as returned by getFit)
Contour.prototype.getNextTop = function(fit) {
    return this.spans[fit.above].top + this.spans[fit.above].height;
};

function Layout(leftBound, rightBound) {
    this.leftBound = leftBound;
    this.rightBound = rightBound;
    // a Layout contains a left contour and a right contour;
    // the area between the contours is allocated, and the
    // area outside the contours is free.
    this.leftContour = new Contour();
    this.rightContour = new Contour();
    this.seen = {};
    this.leftOverlaps = [];
    this.rightOverlaps = [];
    this.totalHeight = 0;
}

Layout.prototype.addRect = function(id, left, right, height) {
    if (this.seen[id] !== undefined) return this.seen[id];
    // for each contour, we test the fit on the near side of the given rect,
    var leftFit = this.tryLeftFit(left, right, height, 0);
    var rightFit = this.tryRightFit(left, right, height, 0);

    var top;

    // and insert the far side from the side we tested
    // (we want to make sure the near side fits, but we want to extend
    //  the contour to cover the far side)
    if (leftFit.top < rightFit.top) {
        top = leftFit.top;
        this.leftContour.insertFit(leftFit.fit, this.rightBound - left,
                                   top, height);
        this.rightContour.unionWith(right - this.leftBound, top, height);
    } else {
        top = rightFit.top;
        this.rightContour.insertFit(rightFit.fit, right - this.leftBound,
                                    top, height);
        this.leftContour.unionWith(this.rightBound - left, top, height);
    }

    var existing = {id: id, left: left, right: right,
                    top: top, height: height};
    this.seen[id] = top;
    if (left < this.leftBound) {
        this.leftOverlaps.push(existing);
        if (this.leftLayout) this.leftLayout.addExisting(existing);
    }
    if (right > this.rightBound) {
        this.rightOverlaps.push(existing);
        if (this.rightLayout) this.rightLayout.addExisting(existing);
    }
    this.seen[id] = top;
    this.totalHeight = Math.max(this.totalHeight, top + height);
    return top;
};

// this method is called by the block to the left to see if a given fit works
// in this layout
// takes: proposed rectangle
// returns: {top: value that makes the rectangle fit in this layout,
//           fit: "fit" for passing to insertFit}
Layout.prototype.tryLeftFit = function(left, right, height, top) {
    var fit, nextFit;
    var curTop = top;
    while (true) {
        // check if the rectangle fits at curTop
        fit = this.leftContour.getFit(this.rightBound - right, height, curTop);
        curTop = Math.max(this.leftContour.getNextTop(fit), curTop);
        // if the rectangle extends onto the next block to the right;
        if (this.rightLayout && (right >= this.rightBound)) {
            // check if the rectangle fits into that block at this position
            nextFit = this.rightLayout.tryLeftFit(left, right, height, curTop);
            // if not, nextTop will be the next y-value where the rectangle
            // fits into that block
            if (nextFit.top > curTop) {
                // in that case, try again to see if that y-value works
                curTop = nextFit.top;
                continue;
            }
        }
        break;
    }
    return {top: curTop, fit: fit};
};

// this method is called by the block to the right to see if a given fit works
// in this layout
// takes: proposed rectangle
// returns: {top: value that makes the rectangle fit in this layout,
//           fit: "fit" for passing to insertFit}
Layout.prototype.tryRightFit = function(left, right, height, top) {
    var fit, nextFit;
    var curTop = top;
    while (true) {
        // check if the rectangle fits at curTop
        fit = this.rightContour.getFit(left - this.leftBound, height, curTop);
        curTop = Math.max(this.rightContour.getNextTop(fit), curTop);
        // if the rectangle extends onto the next block to the left;
        if (this.leftLayout && (left <= this.leftBound)) {
            // check if the rectangle fits into that block at this position
            nextFit = this.leftLayout.tryRightFit(left, right, height, curTop);
            // if not, nextTop will be the next y-value where the rectangle
            // fits into that block
            if (nextFit.top > curTop) {
                // in that case, try again to see if that y-value works
                curTop = nextFit.top;
                continue;
            }
        }
        break;
    }
    return {top: curTop, fit: fit};
};

Layout.prototype.hasSeen = function(id) {
    return (this.seen[id] !== undefined);
};

Layout.prototype.setLeftLayout = function(left) {
    for (var i = 0; i < this.leftOverlaps.length; i++) {
        left.addExisting(this.leftOverlaps[i]);
    }
    this.leftLayout = left;
};

Layout.prototype.setRightLayout = function(right) {
    for (var i = 0; i < this.rightOverlaps.length; i++) {
        right.addExisting(this.rightOverlaps[i]);
    }
    this.rightLayout = right;
};

Layout.prototype.cleanup = function() {
    this.leftLayout = undefined;
    this.rightLayout = undefined;
};

//expects an {id, left, right, height, top} object
Layout.prototype.addExisting = function(existing) {
    if (this.seen[existing.id] !== undefined) return;
    this.seen[existing.id] = existing.top;

    if (existing.left <= this.leftBound) {
        this.leftOverlaps.push(existing);
        if (this.leftLayout) this.leftLayout.addExisting(existing);
    }
    if (existing.right >= this.rightBound) {
        this.rightOverlaps.push(existing);
        if (this.rightLayout) this.rightLayout.addExisting(existing);
    }

    this.leftContour.unionWith(this.rightBound - existing.left,
                               existing.top,
                               existing.height);
    this.rightContour.unionWith(existing.right - this.leftBound,
                                existing.top,
                                existing.height);
};

/*

Copyright (c) 2007-2010 The Evolutionary Software Foundation

Created by Mitchell Skinner <mitch_skinner@berkeley.edu>

This package and its accompanying libraries are free software; you can
redistribute it and/or modify it under the terms of the LGPL (either
version 2.1, or at your option, any later version) or the Artistic
License 2.0.  Refer to LICENSE for the full license text.

*/
<commit_msg>include features that touch the boundary in the "overlaps" arrays<commit_after>/*
 * Code for laying out rectangles, given that layout is also happening
 * in adjacent blocks at the same time
 *
 * This code does a lot of linear searching; n should be low enough that
 * it's not a problem but if it turns out to be, some of it can be changed to
 * binary searching without too much work.  Another possibility is to merge
 * contour spans and give up some packing closeness in exchange for speed
 * (the code already merges spans that have the same x-coord and are vertically
 * contiguous).
 */

function Contour(top) {
    /*
     * A contour is described by a set of vertical lines of varying heights,
     * like this:
     *                         |
     *                         |
     *               |
     *                   |
     *                   |
     *                   |
     *
     * The contour is the union of the rectangles ending on the right side
     * at those lines, and extending leftward toward negative infinity.
     *
     * <=======================|
     * <=======================|
     * <==========|
     * <=================|
     * <=================|
     * <=================|
     *
     * x -->
     *
     * As we add new vertical spans, the contour expands, either downward
     * or in the direction of increasing x.
     */
    // takes: top, a number indicating where the first span of the contour
    // will go
    if (top === undefined) top = 0;

    // spans is an array of {top, x, height} objects representing
    // the boundaries of the contour
    // they're always sorted by top
    this.spans = [{top: top, x: Infinity, height: 0}];
}

// finds a space in the contour into which the given span fits
// (i.e., the given span has higher x than the contour over its vertical span)
// returns an ojbect {above, count}; above is the index of the last span above
// where the given span will fit, count is the number of spans being
// replaced by the given span
Contour.prototype.getFit = function(x, height, minTop) {
    var aboveBottom, curSpan;
    var above = 0;
    if (minTop) {
        // set above = (index of the first span that starts below minTop)
        for (; this.spans[above].top < minTop; above++) {
            if (above >= (this.spans.length - 1))
                return {above: this.spans.length - 1, count: 0};
        }
    }
    // slide down the contour
    ABOVE: for (; above < this.spans.length; above++) {
        aboveBottom = this.spans[above].top + this.spans[above].height;
        for (var count = 1; above + count < this.spans.length; count++) {
            curSpan = this.spans[above + count];
            if ((aboveBottom + height) <= curSpan.top) {
                // the given span fits between span[above] and
                // curSpan, keeping curSpan
                return {above: above, count: count - 1};
            }
            if (curSpan.x > x) {
                // the span at [above + count] overlaps the given span,
                // so we continue down the contour
                continue ABOVE;
            }
            if ((curSpan.x <= x) &&
                ((aboveBottom + height) < (curSpan.top + curSpan.height))) {
                // the given span partially covers curSpan, and
                // will overlap it, so we keep curSpan
                return {above: above, count: count - 1};
            }
        }
        // the given span fits below span[above], replacing any
        // lower spans in the contour
        return {above: above, count: count - 1};
    }
    // the given span fits at the end of the contour, replacing no spans
    return {above: above, count: 0};
};

// add the given span to this contour where it fits, as given
// by getFit
Contour.prototype.insertFit = function(fit, x, top, height) {
    // if the previous span and the current span have the same x-coord,
    // and are vertically contiguous, merge them.
    var prevSpan = this.spans[fit.above];
    if ((Math.abs(prevSpan.x - x) < 1)
        && (Math.abs((prevSpan.top + prevSpan.height) - top) < 1) ) {
        prevSpan.height = (top + height) - prevSpan.top;
        // a bit of slop here is conservative if we take the max
        // (means things might get laid out slightly farther apart
        // than they would otherwise)
        prevSpan.x = Math.max(prevSpan.x, x);
        this.spans.splice(fit.above + 1, fit.count);
    } else {
        this.spans.splice(fit.above + 1, fit.count,
                          {
                              top: top,
                              x: x,
                              height: height
                          });
    }
};

// add the given span to this contour at the given location, if
// it would extend the contour
Contour.prototype.unionWith = function(x, top, height) {
    var startBottom, startIndex, endIndex, startSpan, endSpan;
    var bottom = top + height;
    START: for (startIndex = 0; startIndex < this.spans.length; startIndex++) {
        startSpan = this.spans[startIndex];
        startBottom = startSpan.top + startSpan.height;
        if (startSpan.top > top) {
            // the given span extends above an existing span
            endIndex = startIndex;
            break START;
        }
        if (startBottom > top) {
            // if startSpan covers (at least some of) the given span,
            if (startSpan.x >= x) {
                var covered = startBottom - top;
                // we don't have to worry about the covered area any more
                top += covered;
                height -= covered;
                // if we've eaten up the whole span, then it's submerged
                // and we don't have to do anything
                if (top >= bottom) return;
                continue;
            } else {
                // find the first span not covered by the given span
                for (endIndex = startIndex;
                     endIndex < this.spans.length;
                     endIndex++) {
                    endSpan = this.spans[endIndex];
                    // if endSpan extends below or to the right
                    // of the given span, then we need to keep it
                    if (((endSpan.top + endSpan.height) > bottom)
                        || endSpan.x > x) {
                        break START;
                    }
                }
                break START;
            }
        }
    }

    // if the previous span and the current span have the same x-coord,
    // and are vertically contiguous, merge them.
    var prevSpan = this.spans[startIndex - 1];
    if ((Math.abs(prevSpan.x - x) < 1)
        && (Math.abs((prevSpan.top + prevSpan.height) - top) < 1) ) {
        prevSpan.height = (top + height) - prevSpan.top;
        prevSpan.x = Math.max(prevSpan.x, x);
        this.spans.splice(startIndex, endIndex - startIndex);
    } else {
        this.spans.splice(startIndex, endIndex - startIndex,
                          {
                              top: top,
                              x: x,
                              height: height
                          });
    }
};

// returns the top of the to-be-added span that fits into "fit"
// (as returned by getFit)
Contour.prototype.getNextTop = function(fit) {
    return this.spans[fit.above].top + this.spans[fit.above].height;
};

function Layout(leftBound, rightBound) {
    this.leftBound = leftBound;
    this.rightBound = rightBound;
    // a Layout contains a left contour and a right contour;
    // the area between the contours is allocated, and the
    // area outside the contours is free.
    this.leftContour = new Contour();
    this.rightContour = new Contour();
    this.seen = {};
    this.leftOverlaps = [];
    this.rightOverlaps = [];
    this.totalHeight = 0;
}

Layout.prototype.addRect = function(id, left, right, height) {
    if (this.seen[id] !== undefined) return this.seen[id];
    // for each contour, we test the fit on the near side of the given rect,
    var leftFit = this.tryLeftFit(left, right, height, 0);
    var rightFit = this.tryRightFit(left, right, height, 0);

    var top;

    // and insert the far side from the side we tested
    // (we want to make sure the near side fits, but we want to extend
    //  the contour to cover the far side)
    if (leftFit.top < rightFit.top) {
        top = leftFit.top;
        this.leftContour.insertFit(leftFit.fit, this.rightBound - left,
                                   top, height);
        this.rightContour.unionWith(right - this.leftBound, top, height);
    } else {
        top = rightFit.top;
        this.rightContour.insertFit(rightFit.fit, right - this.leftBound,
                                    top, height);
        this.leftContour.unionWith(this.rightBound - left, top, height);
    }

    var existing = {id: id, left: left, right: right,
                    top: top, height: height};
    this.seen[id] = top;
    if (left <= this.leftBound) {
        this.leftOverlaps.push(existing);
        if (this.leftLayout) this.leftLayout.addExisting(existing);
    }
    if (right >= this.rightBound) {
        this.rightOverlaps.push(existing);
        if (this.rightLayout) this.rightLayout.addExisting(existing);
    }
    this.seen[id] = top;
    this.totalHeight = Math.max(this.totalHeight, top + height);
    return top;
};

// this method is called by the block to the left to see if a given fit works
// in this layout
// takes: proposed rectangle
// returns: {top: value that makes the rectangle fit in this layout,
//           fit: "fit" for passing to insertFit}
Layout.prototype.tryLeftFit = function(left, right, height, top) {
    var fit, nextFit;
    var curTop = top;
    while (true) {
        // check if the rectangle fits at curTop
        fit = this.leftContour.getFit(this.rightBound - right, height, curTop);
        curTop = Math.max(this.leftContour.getNextTop(fit), curTop);
        // if the rectangle extends onto the next block to the right;
        if (this.rightLayout && (right >= this.rightBound)) {
            // check if the rectangle fits into that block at this position
            nextFit = this.rightLayout.tryLeftFit(left, right, height, curTop);
            // if not, nextTop will be the next y-value where the rectangle
            // fits into that block
            if (nextFit.top > curTop) {
                // in that case, try again to see if that y-value works
                curTop = nextFit.top;
                continue;
            }
        }
        break;
    }
    return {top: curTop, fit: fit};
};

// this method is called by the block to the right to see if a given fit works
// in this layout
// takes: proposed rectangle
// returns: {top: value that makes the rectangle fit in this layout,
//           fit: "fit" for passing to insertFit}
Layout.prototype.tryRightFit = function(left, right, height, top) {
    var fit, nextFit;
    var curTop = top;
    while (true) {
        // check if the rectangle fits at curTop
        fit = this.rightContour.getFit(left - this.leftBound, height, curTop);
        curTop = Math.max(this.rightContour.getNextTop(fit), curTop);
        // if the rectangle extends onto the next block to the left;
        if (this.leftLayout && (left <= this.leftBound)) {
            // check if the rectangle fits into that block at this position
            nextFit = this.leftLayout.tryRightFit(left, right, height, curTop);
            // if not, nextTop will be the next y-value where the rectangle
            // fits into that block
            if (nextFit.top > curTop) {
                // in that case, try again to see if that y-value works
                curTop = nextFit.top;
                continue;
            }
        }
        break;
    }
    return {top: curTop, fit: fit};
};

Layout.prototype.hasSeen = function(id) {
    return (this.seen[id] !== undefined);
};

Layout.prototype.setLeftLayout = function(left) {
    for (var i = 0; i < this.leftOverlaps.length; i++) {
        left.addExisting(this.leftOverlaps[i]);
    }
    this.leftLayout = left;
};

Layout.prototype.setRightLayout = function(right) {
    for (var i = 0; i < this.rightOverlaps.length; i++) {
        right.addExisting(this.rightOverlaps[i]);
    }
    this.rightLayout = right;
};

Layout.prototype.cleanup = function() {
    this.leftLayout = undefined;
    this.rightLayout = undefined;
};

//expects an {id, left, right, height, top} object
Layout.prototype.addExisting = function(existing) {
    if (this.seen[existing.id] !== undefined) return;
    this.seen[existing.id] = existing.top;

    if (existing.left <= this.leftBound) {
        this.leftOverlaps.push(existing);
        if (this.leftLayout) this.leftLayout.addExisting(existing);
    }
    if (existing.right >= this.rightBound) {
        this.rightOverlaps.push(existing);
        if (this.rightLayout) this.rightLayout.addExisting(existing);
    }

    this.leftContour.unionWith(this.rightBound - existing.left,
                               existing.top,
                               existing.height);
    this.rightContour.unionWith(existing.right - this.leftBound,
                                existing.top,
                                existing.height);
};

/*

Copyright (c) 2007-2010 The Evolutionary Software Foundation

Created by Mitchell Skinner <mitch_skinner@berkeley.edu>

This package and its accompanying libraries are free software; you can
redistribute it and/or modify it under the terms of the LGPL (either
version 2.1, or at your option, any later version) or the Artistic
License 2.0.  Refer to LICENSE for the full license text.

*/
<|endoftext|>


<commit_before>module.exports =
cmd='ocaml'
prompt=''
//until they fix OCAML syntax
args=['-noprompt']
endSequence=';;\n'
<commit_msg>Prompt Camel<commit_after>module.exports =
cmd='ocaml'
prompt='>>>'
//until they fix OCAML syntax
args=['-noprompt']
endSequence=';;\n'
<|endoftext|>


<commit_before>/// <reference path="../../typings/_custom.d.ts" />

 /*
  * TODO: ES5 for now until I make a webpack plugin for protractor
  */
describe('App', function() {
  var subject;
  var result;

  beforeEach(function() {
    browser.get('/');
  });

  afterEach(function() {
    expect(subject).toEqual(result);
  });

  it('should have a title', function() {
    subject = browser.getTitle();
    result  = 'Angular2 Webpack Starter by @gdi2990 from @AngularClass';
  });

  it('should have <header>', function() {
    subject = element(by.deepCss('app /deep/ header')).isPresent();
    result  = true;
  });

  it('should have <main>', function() {
    subject = element(by.deepCss('app /deep/ main')).isPresent();
    result  = true;
  });

  it('should have <footer>', function() {
    subject = element(by.deepCss('app /deep/ footer')).getText();
    result  = 'WebPack Angular 2 Starter by @AngularClass';
  });

});
<commit_msg>refactor(app.e2e): expect each test<commit_after>/// <reference path="../../typings/_custom.d.ts" />

 /*
  * TODO: ES5 for now until I make a webpack plugin for protractor
  */
describe('App', function() {

  beforeEach(function() {
    browser.get('/');
  });

  it('should have a title', function() {
    var subject = browser.getTitle();
    var result  = 'Angular2 Webpack Starter by @gdi2990 from @AngularClass';
    expect(subject).toEqual(result);
  });

  it('should have <header>', function() {
    var subject = element(by.deepCss('app /deep/ header')).isPresent();
    var result  = true;
    expect(subject).toEqual(result);
  });

  it('should have <main>', function() {
    var subject = element(by.deepCss('app /deep/ main')).isPresent();
    var result  = true;
    expect(subject).toEqual(result);
  });

  it('should have <footer>', function() {
    var subject = element(by.deepCss('app /deep/ footer')).getText();
    var result  = 'WebPack Angular 2 Starter by @AngularClass';
    expect(subject).toEqual(result);
  });

});
<|endoftext|>


<commit_before>'use strict';

(function(app) {

  /**
   * Defines a factory describing a player.
   *
   * All players must implement the methods of this object.
   * All events are dispatched to the given jPlayerElement.
   * The following events are emitted by the player :
   *  - "ovPlay" : Player starts playing
   *  - "ovPause" : Player pauses
   *  - "ovLoadProgress" : Player is buffering
   *  - "ovPlayProgress" : Player is playing
   *  - "ivReady" : Player is ready to play the media
   *  - "ovDurationChange" : Media duration has changed
   *  - "ovEnd" : Media has reached the end
   *  - "ovWaiting" : Media is waiting for buffering
   *  - "ovPlaying" : Media is ready to play after buffering
   *  - "ovError" : Player as encountered an error
   *
   * e.g.
   *
   * // Get an instance of the OvVimeoPlayer
   * // (which extends OvPlayer)
   * var OvVimeoPlayer = $injector.get('OvVimeoPlayer');
   * var player = new OvVimeoPlayer(element, 'player_id', '118786909');
   *
   * @module ov.player
   * @class OvPlayer
   */
  function OvPlayer() {

    /**
     * Defines a Player interface which every Player must implement.
     */
    function Player() {}

    /**
     * Checks if data object is valid.
     *
     * @method init
     * @param {Object} jPlayerElement The JQLite HTML element corresponding
     * to the HTML element which will receive events dispatched by
     * the player
     * @param {String} id The player id to use as the "id" attribute
     */
    Player.prototype.init = function(jPlayerElement, id) {
      if (!jPlayerElement)
        throw new Error('A player JQLite Element is expected as Player argument');

      this.jPlayerElement = jPlayerElement;
      this.selectedSourceIndex = 0;
      this.playerId = id;
    };

    /**
     * Sets the source.
     *
     * @method setMediaSource
     * @param {Number} index of the source in the list of media sources
     */
    Player.prototype.setMediaSource = function(index) {
      this.selectedSourceIndex = index;
    };

    /**
     * Gets index of the selected media source.
     *
     * @method getSourceIndex
     * @return {Number} index of the selected media source in the list of sources
     */
    Player.prototype.getSourceIndex = function() {
      return this.selectedSourceIndex;
    };

    /**
     * Gets media ids.
     *
     * @method getMediaIds
     * @return {Array} The list of media ids
     */
    Player.prototype.getMediaIds = function() {
      return this.media.mediaId;
    };

    /**
     * Gets player id.
     *
     * @method getId
     * @return {String} The player id
     */
    Player.prototype.getId = function() {
      return this.playerId;
    };

    /**
     * Sets current media.
     *
     * @method setMedia
     * @param {Objet} media New media
     */
    Player.prototype.setMedia = function(media) {
      if (!media)
        throw new Error('Player needs a valid media');

      this.media = media;
    };

    /**
     * Gets the url of the selected source.
     *
     * @method getSourceUrl
     * @return {String|Null} The url of the source
     */
    Player.prototype.getSourceUrl = function() {
      return null;
    };

    /**
     * Gets media MIME Type.
     *
     * @method getMediaUrl
     * @param {Object} definition Media definition object
     * @return {String} The media url
     */
    Player.prototype.getMediaMIME = function(definition) {
      return definition && definition.mimeType ? definition.mimeType : 'video/mp4';
    };

    /**
     * Gets media definitions.
     *
     * @method getAvailableDefinitions
     * @return {Array} The list of definitions
     */
    Player.prototype.getAvailableDefinitions = function() {
      throw new Error('getAvailableDefinitions method not implemented for this player');
    };

    /**
     * Gets media thumbnail.
     *
     * @method getMediaThumbnail
     * @return {String} The media thumbnail
     */
    Player.prototype.getMediaThumbnail = function() {
      throw new Error('getMediaThumbnail method not implemented for this player');
    };

    /**
     * Inititializes the player after DOM is loaded.
     *
     * @method initialize
     */
    Player.prototype.initialize = function() {
      throw new Error('initialize method not implemented for this player');
    };

    /**
     * Loads current media.
     *
     * @method load
     */
    Player.prototype.load = function() {
      throw new Error('load method not implemented for this player');
    };

    /**
     * Tests if player actual state is pause.
     *
     * @method isPaused
     * @param {Boolean} true if paused, false otherwise
     */
    Player.prototype.isPaused = function() {
      throw new Error('isPaused method not implemented for this player');
    };

    /**
     * Tests if player actual state is playing.
     *
     * @method isPlaying
     * @return {Boolean} true if playing, false otherwise
     */
    Player.prototype.isPlaying = function() {
      throw new Error('isPlaying method not implemented for this player');
    };

    /**
     * Plays or pauses the media depending on media actual state.
     *
     * @method playPause
     */
    Player.prototype.playPause = function() {
      throw new Error('play method not implemented for this player');
    };

    /**
     * Sets volume.
     *
     * @method setVolume
     * @param {Number} volume The new volume from 0 to 100.
     */
    Player.prototype.setVolume = function() {
      throw new Error('setVolume method not implemented for this player');
    };

    /**
     * Sets time.
     *
     * @method setTime
     * @param {Number} time The time to seek to in milliseconds
     */
    Player.prototype.setTime = function() {
      throw new Error('setTime method not implemented for this player');
    };

    /**
     * Gets player type.
     *
     * @method getPlayerTYpe
     * @return {String} A string representation of the player type
     */
    Player.prototype.getPlayerType = function() {
      throw new Error('getPlayerType method not implemented for this player');
    };

    /**
     * Destroys the player.
     *
     * @method destroy
     */
    Player.prototype.destroy = function() {
      throw new Error('destroy method not implemented for this player');
    };

    /**
     * Changes definition of the current source.
     *
     * @method setDefinition
     * @param {Object} definition Definition from the list of available definitions
     */
    Player.prototype.setDefinition = function(definition) {
      throw new Error('setDefinition method not implemented for this player');
    };

    return Player;
  }

  app.factory('OvPlayer', OvPlayer);

})(angular.module('ov.player'));
<commit_msg>Fix typing error in documentation<commit_after>'use strict';

(function(app) {

  /**
   * Defines a factory describing a player.
   *
   * All players must implement the methods of this object.
   * All events are dispatched to the given jPlayerElement.
   * The following events are emitted by the player :
   *  - "ovPlay" : Player starts playing
   *  - "ovPause" : Player pauses
   *  - "ovLoadProgress" : Player is buffering
   *  - "ovPlayProgress" : Player is playing
   *  - "ovReady" : Player is ready to play the media
   *  - "ovDurationChange" : Media duration has changed
   *  - "ovEnd" : Media has reached the end
   *  - "ovWaiting" : Media is waiting for buffering
   *  - "ovPlaying" : Media is ready to play after buffering
   *  - "ovError" : Player as encountered an error
   *
   * e.g.
   *
   * // Get an instance of the OvVimeoPlayer
   * // (which extends OvPlayer)
   * var OvVimeoPlayer = $injector.get('OvVimeoPlayer');
   * var player = new OvVimeoPlayer(element, 'player_id', '118786909');
   *
   * @module ov.player
   * @class OvPlayer
   */
  function OvPlayer() {

    /**
     * Defines a Player interface which every Player must implement.
     */
    function Player() {}

    /**
     * Checks if data object is valid.
     *
     * @method init
     * @param {Object} jPlayerElement The JQLite HTML element corresponding
     * to the HTML element which will receive events dispatched by
     * the player
     * @param {String} id The player id to use as the "id" attribute
     */
    Player.prototype.init = function(jPlayerElement, id) {
      if (!jPlayerElement)
        throw new Error('A player JQLite Element is expected as Player argument');

      this.jPlayerElement = jPlayerElement;
      this.selectedSourceIndex = 0;
      this.playerId = id;
    };

    /**
     * Sets the source.
     *
     * @method setMediaSource
     * @param {Number} index of the source in the list of media sources
     */
    Player.prototype.setMediaSource = function(index) {
      this.selectedSourceIndex = index;
    };

    /**
     * Gets index of the selected media source.
     *
     * @method getSourceIndex
     * @return {Number} index of the selected media source in the list of sources
     */
    Player.prototype.getSourceIndex = function() {
      return this.selectedSourceIndex;
    };

    /**
     * Gets media ids.
     *
     * @method getMediaIds
     * @return {Array} The list of media ids
     */
    Player.prototype.getMediaIds = function() {
      return this.media.mediaId;
    };

    /**
     * Gets player id.
     *
     * @method getId
     * @return {String} The player id
     */
    Player.prototype.getId = function() {
      return this.playerId;
    };

    /**
     * Sets current media.
     *
     * @method setMedia
     * @param {Objet} media New media
     */
    Player.prototype.setMedia = function(media) {
      if (!media)
        throw new Error('Player needs a valid media');

      this.media = media;
    };

    /**
     * Gets the url of the selected source.
     *
     * @method getSourceUrl
     * @return {String|Null} The url of the source
     */
    Player.prototype.getSourceUrl = function() {
      return null;
    };

    /**
     * Gets media MIME Type.
     *
     * @method getMediaUrl
     * @param {Object} definition Media definition object
     * @return {String} The media url
     */
    Player.prototype.getMediaMIME = function(definition) {
      return definition && definition.mimeType ? definition.mimeType : 'video/mp4';
    };

    /**
     * Gets media definitions.
     *
     * @method getAvailableDefinitions
     * @return {Array} The list of definitions
     */
    Player.prototype.getAvailableDefinitions = function() {
      throw new Error('getAvailableDefinitions method not implemented for this player');
    };

    /**
     * Gets media thumbnail.
     *
     * @method getMediaThumbnail
     * @return {String} The media thumbnail
     */
    Player.prototype.getMediaThumbnail = function() {
      throw new Error('getMediaThumbnail method not implemented for this player');
    };

    /**
     * Inititializes the player after DOM is loaded.
     *
     * @method initialize
     */
    Player.prototype.initialize = function() {
      throw new Error('initialize method not implemented for this player');
    };

    /**
     * Loads current media.
     *
     * @method load
     */
    Player.prototype.load = function() {
      throw new Error('load method not implemented for this player');
    };

    /**
     * Tests if player actual state is pause.
     *
     * @method isPaused
     * @param {Boolean} true if paused, false otherwise
     */
    Player.prototype.isPaused = function() {
      throw new Error('isPaused method not implemented for this player');
    };

    /**
     * Tests if player actual state is playing.
     *
     * @method isPlaying
     * @return {Boolean} true if playing, false otherwise
     */
    Player.prototype.isPlaying = function() {
      throw new Error('isPlaying method not implemented for this player');
    };

    /**
     * Plays or pauses the media depending on media actual state.
     *
     * @method playPause
     */
    Player.prototype.playPause = function() {
      throw new Error('play method not implemented for this player');
    };

    /**
     * Sets volume.
     *
     * @method setVolume
     * @param {Number} volume The new volume from 0 to 100.
     */
    Player.prototype.setVolume = function() {
      throw new Error('setVolume method not implemented for this player');
    };

    /**
     * Sets time.
     *
     * @method setTime
     * @param {Number} time The time to seek to in milliseconds
     */
    Player.prototype.setTime = function() {
      throw new Error('setTime method not implemented for this player');
    };

    /**
     * Gets player type.
     *
     * @method getPlayerTYpe
     * @return {String} A string representation of the player type
     */
    Player.prototype.getPlayerType = function() {
      throw new Error('getPlayerType method not implemented for this player');
    };

    /**
     * Destroys the player.
     *
     * @method destroy
     */
    Player.prototype.destroy = function() {
      throw new Error('destroy method not implemented for this player');
    };

    /**
     * Changes definition of the current source.
     *
     * @method setDefinition
     * @param {Object} definition Definition from the list of available definitions
     */
    Player.prototype.setDefinition = function(definition) {
      throw new Error('setDefinition method not implemented for this player');
    };

    return Player;
  }

  app.factory('OvPlayer', OvPlayer);

})(angular.module('ov.player'));
<|endoftext|>