<commit_before>describe('Routines', function() {
  var el, input, trueRadioInput, falseRadioInput;

  beforeEach(function() {
    rivets.configure({
      adapter: {
        subscribe: function() {},
        unsubscribe: function() {},
        read: function() {},
        publish: function() {}
      }
    });

    el = document.createElement('div');
    document.body.appendChild(el);

    input = document.createElement('input');
    input.setAttribute('type', 'text');
    document.body.appendChild(input);

    // to test the radio input scenario when its value is "true"
    trueRadioInput = document.createElement('input');
    trueRadioInput.setAttribute('type', 'radio');
    trueRadioInput.value = 'true';
    document.body.appendChild(trueRadioInput);

    // to test the radio input scenario when its value is "false"
    falseRadioInput = document.createElement('input');
    falseRadioInput.setAttribute('type', 'radio');
    falseRadioInput.value = 'false';
    document.body.appendChild(falseRadioInput);

  });

  afterEach(function(){
    el.parentNode.removeChild(el);
    input.parentNode.removeChild(input);
    trueRadioInput.parentNode.removeChild(trueRadioInput);
    falseRadioInput.parentNode.removeChild(falseRadioInput);
  });

  describe('text', function() {
    it("sets the element's text content", function() {
      rivets.binders.text(el, 'gluten-free');
      expect(el.textContent || el.innerText).toBe('gluten-free');
      expect(el.innerHTML).toBe('<em>gluten-free</em>');
    });

    it("sets the element's text content to zero when a numeric zero is passed", function() {
        rivets.binders.text(el, 0);
        expect(el.textContent || el.innerText).toBe('0');
        expect(el.innerHTML).toBe('0');
    });
  });

  describe('html', function() {
    it("sets the element's HTML content", function() {
      rivets.binders.html(el, 'fixie');
      expect(el.textContent || el.innerText).toBe('fixie');
      expect(el.innerHTML).toBe('fixie');
    });

    it("sets the element's HTML content to zero when a zero value is passed", function() {
      rivets.binders.html(el, 0);
        expect(el.textContent || el.innerText).toBe('0');
        expect(el.innerHTML).toBe('0');
    });
  });

  describe('value', function() {
    it("sets the element's value", function() {
      rivets.binders.value.routine(input, 'pitchfork');
      expect(input.value).toBe('pitchfork');
    });

    it("applies a default value to the element when the model doesn't contain it", function() {
      rivets.binders.value.routine(input, undefined);
      expect(input.value).toBe('');
    });

    it("sets the element's value to zero when a zero value is passed", function() {
      rivets.binders.value.routine(input, 0);
      expect(input.value).toBe('0');
    });
  });

  describe('show', function() {
    describe('with a truthy value', function() {
      it('shows the element', function() {
        rivets.binders.show(el, true);
        expect(el.style.display).toBe('');
      });
    });

    describe('with a falsey value', function() {
      it('hides the element', function() {
        rivets.binders.show(el, false);
        expect(el.style.display).toBe('none');
      });
    });
  });

  describe('hide', function() {
    describe('with a truthy value', function() {
      it('hides the element', function() {
        rivets.binders.hide(el, true);
        expect(el.style.display).toBe('none');
      });
    });

    describe('with a falsey value', function() {
      it('shows the element', function() {
        rivets.binders.hide(el, false);
        expect(el.style.display).toBe('');
      });
    });
  });

  describe('enabled', function() {
    describe('with a truthy value', function() {
      it('enables the element', function() {
        rivets.binders.enabled(el, true);
        expect(el.disabled).toBe(false);
      });
    });

    describe('with a falsey value', function() {
      it('disables the element', function() {
        rivets.binders.enabled(el, false);
        expect(el.disabled).toBe(true);
      });
    });
  });

  describe('disabled', function() {
    describe('with a truthy value', function() {
      it('disables the element', function() {
        rivets.binders.disabled(el, true);
        expect(el.disabled).toBe(true);
      });
    });

    describe('with a falsey value', function() {
      it('enables the element', function() {
        rivets.binders.disabled(el, false);
        expect(el.disabled).toBe(false);
      });
    });
  });

  describe('checked', function() {
    describe('with a radio input with value="true"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.checked.routine(trueRadioInput, true);
          expect(trueRadioInput.checked).toBe(true);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.checked.routine(trueRadioInput, false);
          expect(trueRadioInput.checked).toBe(false);
        });
      });
    });

    describe('with a radio input with value="false"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.checked.routine(falseRadioInput, true);
          expect(falseRadioInput.checked).toBe(false);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.checked.routine(falseRadioInput, false);
          expect(falseRadioInput.checked).toBe(true);
        });
      });
    });
  });

  describe('unchecked', function() {
    describe('with a radio input with value="true"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.unchecked.routine(trueRadioInput, true);
          expect(trueRadioInput.checked).toBe(false);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.unchecked.routine(trueRadioInput, false);
          expect(trueRadioInput.checked).toBe(true);
        });
      });
    });

    describe('with a radio input with value="false"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.unchecked.routine(falseRadioInput, true);
          expect(falseRadioInput.checked).toBe(true);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.unchecked.routine(falseRadioInput, false);
          expect(falseRadioInput.checked).toBe(false);
        });
      });
    });
  });
});
<commit_msg>- add checkboxes for testing too - factorise some code into a createInputElement() function<commit_after>describe('Routines', function() {
  var el, input, trueRadioInput, falseRadioInput, checkboxInput;

  var createInputElement = function(type, value) {
    var elem = document.createElement('input');
    elem.setAttribute('type', type);
    if (value !== undefined){
      elem.setAttribute('value', value);
    }
    document.body.appendChild(elem);
    return elem;
  };

  beforeEach(function() {
    rivets.configure({
      adapter: {
        subscribe: function() {},
        unsubscribe: function() {},
        read: function() {},
        publish: function() {}
      }
    });

    el = document.createElement('div');
    document.body.appendChild(el);

    input = createInputElement('text');

    // to test the radio input scenario when its value is "true"
    trueRadioInput = createInputElement('radio', 'true');

    // to test the radio input scenario when its value is "false"
    falseRadioInput = createInputElement('radio', 'false');

    // to test the checkbox input scenario
    checkboxInput = createInputElement('checkbox');

  });

  afterEach(function(){
    el.parentNode.removeChild(el);
    input.parentNode.removeChild(input);
    trueRadioInput.parentNode.removeChild(trueRadioInput);
    falseRadioInput.parentNode.removeChild(falseRadioInput);
    checkboxInput.parentNode.removeChild(checkboxInput);
  });

  describe('text', function() {
    it("sets the element's text content", function() {
      rivets.binders.text(el, 'gluten-free');
      expect(el.textContent || el.innerText).toBe('gluten-free');
      expect(el.innerHTML).toBe('<em>gluten-free</em>');
    });

    it("sets the element's text content to zero when a numeric zero is passed", function() {
        rivets.binders.text(el, 0);
        expect(el.textContent || el.innerText).toBe('0');
        expect(el.innerHTML).toBe('0');
    });
  });

  describe('html', function() {
    it("sets the element's HTML content", function() {
      rivets.binders.html(el, 'fixie');
      expect(el.textContent || el.innerText).toBe('fixie');
      expect(el.innerHTML).toBe('fixie');
    });

    it("sets the element's HTML content to zero when a zero value is passed", function() {
      rivets.binders.html(el, 0);
        expect(el.textContent || el.innerText).toBe('0');
        expect(el.innerHTML).toBe('0');
    });
  });

  describe('value', function() {
    it("sets the element's value", function() {
      rivets.binders.value.routine(input, 'pitchfork');
      expect(input.value).toBe('pitchfork');
    });

    it("applies a default value to the element when the model doesn't contain it", function() {
      rivets.binders.value.routine(input, undefined);
      expect(input.value).toBe('');
    });

    it("sets the element's value to zero when a zero value is passed", function() {
      rivets.binders.value.routine(input, 0);
      expect(input.value).toBe('0');
    });
  });

  describe('show', function() {
    describe('with a truthy value', function() {
      it('shows the element', function() {
        rivets.binders.show(el, true);
        expect(el.style.display).toBe('');
      });
    });

    describe('with a falsey value', function() {
      it('hides the element', function() {
        rivets.binders.show(el, false);
        expect(el.style.display).toBe('none');
      });
    });
  });

  describe('hide', function() {
    describe('with a truthy value', function() {
      it('hides the element', function() {
        rivets.binders.hide(el, true);
        expect(el.style.display).toBe('none');
      });
    });

    describe('with a falsey value', function() {
      it('shows the element', function() {
        rivets.binders.hide(el, false);
        expect(el.style.display).toBe('');
      });
    });
  });

  describe('enabled', function() {
    describe('with a truthy value', function() {
      it('enables the element', function() {
        rivets.binders.enabled(el, true);
        expect(el.disabled).toBe(false);
      });
    });

    describe('with a falsey value', function() {
      it('disables the element', function() {
        rivets.binders.enabled(el, false);
        expect(el.disabled).toBe(true);
      });
    });
  });

  describe('disabled', function() {
    describe('with a truthy value', function() {
      it('disables the element', function() {
        rivets.binders.disabled(el, true);
        expect(el.disabled).toBe(true);
      });
    });

    describe('with a falsey value', function() {
      it('enables the element', function() {
        rivets.binders.disabled(el, false);
        expect(el.disabled).toBe(false);
      });
    });
  });

  describe('checked', function() {
    describe('with a checkbox input', function() {
      describe('and a truthy value', function() {
        it('checks the checkbox input', function() {
          rivets.binders.checked.routine(checkboxInput, true);
          expect(checkboxInput.checked).toBe(true);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the checkbox input', function() {
          rivets.binders.checked.routine(checkboxInput, false);
          expect(checkboxInput.checked).toBe(false);
        });
      });
    });

    describe('with a radio input with value="true"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.checked.routine(trueRadioInput, true);
          expect(trueRadioInput.checked).toBe(true);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.checked.routine(trueRadioInput, false);
          expect(trueRadioInput.checked).toBe(false);
        });
      });
    });

    describe('with a radio input with value="false"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.checked.routine(falseRadioInput, true);
          expect(falseRadioInput.checked).toBe(false);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.checked.routine(falseRadioInput, false);
          expect(falseRadioInput.checked).toBe(true);
        });
      });
    });
  });

  describe('unchecked', function() {
    describe('and a truthy value', function() {
      describe('and a truthy value', function() {
        it('checks the checkbox input', function() {
          rivets.binders.unchecked.routine(checkboxInput, true);
          expect(checkboxInput.checked).toBe(false);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the checkbox input', function() {
          rivets.binders.unchecked.routine(checkboxInput, false);
          expect(checkboxInput.checked).toBe(true);
        });
      });
    });

    describe('with a radio input with value="true"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.unchecked.routine(trueRadioInput, true);
          expect(trueRadioInput.checked).toBe(false);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.unchecked.routine(trueRadioInput, false);
          expect(trueRadioInput.checked).toBe(true);
        });
      });
    });

    describe('with a radio input with value="false"', function() {
      describe('and a truthy value', function() {
        it('checks the radio input', function() {
          rivets.binders.unchecked.routine(falseRadioInput, true);
          expect(falseRadioInput.checked).toBe(true);
        });
      });

      describe('with a falsey value', function() {
        it('unchecks the radio input', function() {
          rivets.binders.unchecked.routine(falseRadioInput, false);
          expect(falseRadioInput.checked).toBe(false);
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>/*js script to do pop pyramid thang*/

var margin = {top: 60, right: 80, bottom: 40, left: 80},
    width = 1200 - margin.left - margin.right,
    height = 600 - margin.top - margin.bottom,
    barWidth = Math.floor(width / 17) - 1;

var x = d3.scale.linear()
    .range([barWidth / 2, width - barWidth / 2]);

var y = d3.scale.linear()
    .range([height, 0]);

var formatPercent = d3.format("0.0%");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickSize(width)
    .tickFormat(formatPercent);

var tip = d3.tip()
  .attr('class', 'd3-tip')
  .offset([-10, 0])
  .html(function(d) {console.log(d); var dp = Number(d).toFixed(4);
    return "Frequency: " + dp + "";
  })

// An SVG element with a bottom-right origin.
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.call(tip);

// A sliding container to hold the bars by birthyear.
var birthyears = svg.append("g")
    .attr("class", "birthyears");

// A label for the current year.
var title = svg.append("text")
    .attr("class", "title")
    .attr("dy", ".71em")
    .attr("x", width - 700)
    .text("Segi");

/*currently cheating as there is a flip somewhere?*/
/*d3.csv("mydata3.csv", type, function(error, data) {*/
d3.csv("mydata4.csv", type, function(error, data) {

  // Convert strings to numbers.
  data.forEach(function(d) {
    d.people = +d.people;
    d.year = +d.year;
    d.age = +d.age;
  });

  // Compute the extent of the data set in age and years.
  var age1 = d3.max(data, function(d) { return d.age; }),
      year0 = d3.min(data, function(d) { return d.year; }),
      year1 = d3.max(data, function(d) { return d.year; }),
      year = year1;
      /*std = function(d) { return d.std; };*/

  /*var stan = function(d) { return d.std; };*/

  // Update the scale domains.
  x.domain([year1 - age1, year1]);
  y.domain([0, d3.max(data, function(d) { return d.people; })]);

  // Produce a map from year and birthyear to [male, female].
  data = d3.nest()
      .key(function(d) { return d.year; })
      .key(function(d) { return d.year - d.age; })
      .rollup(function(v) { return v.map(function(d) { return d.people; }); })
      .map(data);

  // Add an axis to show the population values.
  svg.append("g")
      .attr("class", "y axis")
      .attr("transform", "translate(" + width + ",0)")
      .call(yAxis)
    .selectAll("g")
    .filter(function(value) { return !value; })
      .classed("zero", true);

  // Add labeled rects for each birthyear (so that no enter or exit is required).
  var birthyear = birthyears.selectAll(".birthyear")
      .data(d3.range(year0 - age1, year1 + 1, 5))
    .enter().append("g")
      .attr("class", "birthyear")
      .attr("transform", function(birthyear) { return "translate(" + x(birthyear) + ",0)"; });

  birthyear.selectAll("rect")
      .data(function(birthyear) { return data[year][birthyear] || [0, 0]; })
    .enter().append("rect")
      .attr("x", -barWidth / 2)
      .attr("width", barWidth)
      .attr("y", y)
      .attr("height", function(value) { return height - y(value); })
      .on('mouseover', tip.show)
      .on('mouseout', tip.hide);

  // Add labels to show birthyear.
  /*birthyear.append("text")
      .attr("y", height - 4)
      .text(function(birthyear) { return birthyear; });*/

  // Add labels to show age (separate; not animated).
  svg.selectAll(".age")
      .data(d3.range(0, age1 + 1, 5))
    .enter().append("text")
      .attr("class", "age")
      .attr("x", function(age) { return x(year - age); })
      .attr("y", height + 4)
      .attr("dy", ".71em")
      .text(function(age) { if(age === 0){ return (80 - age + "+"); }else{ return (80 - age + "-" + (84 - age));} });

  // Allow the arrow keys to change the displayed year.
  window.focus();
  d3.select(window).on("keydown", function() {
    switch (d3.event.keyCode) {
      case 37: year = Math.max(year0, year - 90); break;
      case 39: year = Math.min(year1, year + 90); break;
    }
    update();
  });

  function update() {
    if (!(year in data)) return;
    if (year === 900) title.text("Segi");
    if (year === 810) title.text("WHO");
    if (year === 540) title.text("Maori - Census 01");
    if (year === 630) title.text("Maori - Census 06");
    if (year === 720) title.text("Maori - Census 13");
    if (year === 270) title.text("Pacific - Census 01");
    if (year === 360) title.text("Pacific - Census 06");
    if (year === 450) title.text("Pacific - Census 13");
    if (year === 0) title.text("Asian - Census 01");
    if (year === 90) title.text("Asian - Census 06");
    if (year === 180) title.text("Asian - Census 13");

    /*title.text("");
    /*function(data){return(data.std);});*/
    /*function(year) { if(year = 1850){ return("Maori"); }else if(year == 1920){ return("WHO"); }else{ return("Segi"); }});*/

    birthyears.transition()
        .duration(750)
        .attr("transform", "translate(" + (x(year1) - x(year)) + ",0)");

    birthyear.selectAll("rect")
        .data(function(birthyear) { return data[year][birthyear] || [0, 0]; })
      .transition()
        .duration(750)
        .attr("y", y)
        .attr("height", function(value) { return height - y(value); });
  }
});

function type(d) {
  d.people = +d.people;
  return d;
}
<commit_msg>Delete struct.js<commit_after><|endoftext|>


<commit_before>// ==UserScript==
// @name         Unlock spiegel.de articles
// @namespace    theshark
// @version      0.3
// @description  Unblocks articles on spiegel.de
// @author       theshark
// @match        http://www.spiegel.de/*
// @grant        none
// @updateURL    https://github.com/therealshark/spiegelunblock/raw/master/spiegel-unblock.user.js
// @downloadURL  https://github.com/therealshark/spiegelunblock/raw/master/spiegel-unblock.user.js
// ==/UserScript==

(function() {
    'use strict';

    function isTextNode(node){
        return node.nodeType == node.TEXT_NODE;
    }

    var decodableClasses = ['text-link-int', 'text-link-ext', 'lp-text-link-int', 'lp-text-link-ext', 'spCelink'];
    function isDecodableNode(node){
        return node.nodeType == node.ELEMENT_NODE && decodableClasses.every(function(cssclass){
            return node.className.indexOf(cssclass) == -1;
        });
    }

    // "Decodes" the Text. Caesarchiffre with an offset of one?, cooooome oooon
    var decodeTable = {
        177: '&',
        178: '!',
        180: ';',
        181: '=',
        32: ' '
    };
    function decode(text){
            return text.split('').map(function(char){
                var charCode = char.charCodeAt(0);
                return decodeTable[charCode] || String.fromCharCode(charCode - 1);
            }).join('');
    }

    // Walks through nodes and decodes the text
    function nodeWalker(nodes){
        for(var i = 0; i < nodes.length; i++){
            var node = nodes[i];
            if(isTextNode(node)){
                node.data = decode(node.data);
            }else if(isDecodableNode(node)){ // <- links aren't encoded, so no need to decode them
                nodeWalker(node.childNodes);
            }
        }
    }

    function start(){
        // Find the div
        var blurDiv = document.querySelector('.lp_mwi_svg-filter-blur');

        // remove the crap overlays
        while(blurDiv.nextSibling !== null){
            blurDiv.nextSibling.remove();
        }
        while(blurDiv.previousSibling !== null){
            blurDiv.previousSibling.remove();
        }

        // remove the blurring
        blurDiv.className = '';

        // decode the text
        nodeWalker(document.querySelectorAll('.obfuscated-content .obfuscated'));
    }

    // lets wait for everything to be initialized
    function waitForStart(){
        if(document.querySelector('.lp_mwi_svg-filter-blur')){
            start();
        }else{
            setTimeout(waitForStart, 250);
        }
    }

    // Is it a paid article?
    if(document.querySelector('h2.article-title a.laterpay-icon')){
        waitForStart();
    }
})();
<commit_msg>fixed initialize detection, added log messages for easier debugging in the future<commit_after>// ==UserScript==
// @name         Unlock spiegel.de articles
// @namespace    theshark
// @version      0.4
// @description  Unblocks articles on spiegel.de
// @author       theshark
// @match        http://www.spiegel.de/*
// @grant        none
// @updateURL    https://github.com/therealshark/spiegelunblock/raw/master/spiegel-unblock.user.js
// @downloadURL  https://github.com/therealshark/spiegelunblock/raw/master/spiegel-unblock.user.js
// ==/UserScript==

(function() {
    'use strict';

    function log(txt){
        console.log('[SPIEGEL-Unblock]: ' + txt);
    }

    function isTextNode(node){
        return node.nodeType == node.TEXT_NODE;
    }

    var decodableClasses = ['text-link-int', 'text-link-ext', 'lp-text-link-int', 'lp-text-link-ext', 'spCelink'];
    function isDecodableNode(node){
        return node.nodeType == node.ELEMENT_NODE && decodableClasses.every(function(cssclass){
            return node.className.indexOf(cssclass) == -1;
        });
    }

    // "Decodes" the Text. Caesarchiffre with an offset of one?, cooooome oooon
    var decodeTable = {
        177: '&',
        178: '!',
        180: ';',
        181: '=',
        32: ' '
    };
    function decode(text){
        return text.split('').map(function(char){
            var charCode = char.charCodeAt(0);
            return decodeTable[charCode] || String.fromCharCode(charCode - 1);
        }).join('');
    }

    // Walks through nodes and decodes the text
    function nodeWalker(nodes){
        for(var i = 0; i < nodes.length; i++){
            var node = nodes[i];
            if(isTextNode(node)){
                node.data = decode(node.data);
            }else if(isDecodableNode(node)){ // <- links aren't encoded, so no need to decode them
                nodeWalker(node.childNodes);
            }
        }
    }

    function start(){
        // Find the div
        var blurDiv = document.querySelector('.obfuscated-content').parentElement;
        log('found blur div');
        // remove the crap overlays
        while(blurDiv.nextSibling !== null){
            blurDiv.nextSibling.remove();
        }
        while(blurDiv.previousSibling !== null){
            blurDiv.previousSibling.remove();
        }
        log('removed overlays');
        // remove the blurring
        blurDiv.className = '';
        log('removed blurring');
        log('starting to "decode" text');
        // decode the text
        nodeWalker(document.querySelectorAll('.obfuscated-content .obfuscated'));
        log('done');
    }

    // lets wait for everything to be initialized
    function waitForStart(){
        if(document.querySelector('[data-lp="mwi-purchase-form"]')){
            log("Initialized");
            start();
        }else{
            setTimeout(waitForStart, 250);
            log("waiting for initialization");
        }
    }

    // Is it a paid article?
    if(document.querySelector('h2.article-title a.laterpay-icon')){
        log("paid article");
        waitForStart();
    }
})();
<|endoftext|>


<commit_before>function BowlingGame() {
  this.ball = 1;
  this.frameCounter = 1;
  this.frameHolder = [];
  this.rollTracker = [];
};

BowlingGame.prototype.roll = function(pins){
  this.rollTracker.push(pins);
  if ( this.ball === 1 && this.frameCounter != 10 ) {
        this.frameHolder[0] = pins;
        this.ball++;
        return this.frameValidate();
  } else if ( this.ball === 2 && this.frameCounter != 10 ) {
        this.frameHolder[1] = pins;
        return this.frameValidate();
        this.nextFrame();
  } else if ( this.tenthFrame() ) {
        return 'TESTING PLEASE WORK';
  }
};

BowlingGame.prototype.tenthFrame = function() {
  this.frameCounter === 10;
};

BowlingGame.prototype.frameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
    if ( validate > 10 ) {
      this.ball = 2;
      this.rollTracker.pop();
      this.frameHolder.pop();
      throw new Error("Oops! You can't hit more than 10 pins per frame!");
  } else if ( this.frameHolder[0] === 10 && this.frameCounter != 10) {
      this.nextFrame();
      return 'X';
  } else if ( validate === 10 && this.ball === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
      return '/';
  } else if ( this.frameHolder.length === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
  }
};

BowlingGame.prototype.nextFrame = function(){
  this.ball = 1;
  this.frameCounter++;
  this.frameHolder = [];
  if ( this.frameCounter > 10 ) {
      throw new Error("You've played 10 frames, start a new game!")
  }
};

BowlingGame.prototype.autoRoll = function(){
  pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  if ( this.ball === 1 ) {
      roll_1 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_1);
      this.frameHolder[0] = roll_1;
      this.ball++;
      return roll_1;
  } else if ( this.ball === 2 ) {
      roll_2 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_2);
      this.frameHolder[1] = roll_2;
      this.autoFrameValidate();
      return roll_2;
  }
};

BowlingGame.prototype.autoFrameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
      if (validate > 10 ) {
        this.rollTracker.pop();
        this.frameHolder.pop();
        this.autoRoll().roll_2;
    } else {
      return this.frameValidate();
    }
  };

BowlingGame.prototype.score = function() {
  let calc = 0;
  let i = 0;
  for (var frame = 0; frame < 10; frame++) {
      if (this.scoreStrike(i)) {
          calc += 10 + this.rollTracker[i+1] + this.rollTracker[i+2];
          i++;
      } else if (this.scoreSpare(i)){
          calc += 10 + this.rollTracker[i+2];
          i+=2;
      } else {
          calc += this.rollTracker[i] + this.rollTracker[i+1];
          i+=2;
      }
  }
  return calc;
};

BowlingGame.prototype.gameOver = function() {
  return 'game over';
  if (this.score() < 150 ) {
        return "Ouch you need practice!";
        return 'your final score:' + this.score();
  } else if (this.score() > 250 ) {
        return "Woah! Are you a bowling pro?!";
        return 'your final score:' + this.score();
  } else {
        return "Not too bad, keep bowling!";
        return 'your final score:' + this.score();
  }
};

BowlingGame.prototype.frameLayout = function(){
  frames = this.rollTracker;
  frame = 1;
  for (i = 0; frames; i++) {
    // add logic for returning frames in pairs from index 0
  }
};

BowlingGame.prototype.scoreSpare = function(i) {
  return this.rollTracker[i] + this.rollTracker[i+1] === 10;
};

BowlingGame.prototype.scoreStrike = function(i) {
  return this.rollTracker[i] === 10;
};
<commit_msg>Refactoring tenth frame fx<commit_after>function BowlingGame() {
  this.ball = 1;
  this.frameCounter = 1;
  this.frameHolder = [];
  this.rollTracker = [];
};

BowlingGame.prototype.roll = function(pins){
  this.rollTracker.push(pins);
  if ( this.ball === 1 && this.frameCounter != 10 ) {
        this.frameHolder[0] = pins;
        this.ball++;
        return this.frameValidate();
  } else if ( this.ball === 2 && this.frameCounter != 10 ) {
        this.frameHolder[1] = pins;
        return this.frameValidate();
        this.nextFrame();
  } else if ( this.frameCounter === 10 ) {
        return this.tenthFrame();
  }
};

BowlingGame.prototype.tenthFrame = function() {
  return 'TESTING PLEASE WORK';
};

BowlingGame.prototype.frameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
    if ( validate > 10 ) {
      this.ball = 2;
      this.rollTracker.pop();
      this.frameHolder.pop();
      throw new Error("Oops! You can't hit more than 10 pins per frame!");
  } else if ( this.frameHolder[0] === 10 && this.frameCounter != 10) {
      this.nextFrame();
      return 'X';
  } else if ( validate === 10 && this.ball === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
      return '/';
  } else if ( this.frameHolder.length === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
  }
};

BowlingGame.prototype.nextFrame = function(){
  this.ball = 1;
  this.frameCounter++;
  this.frameHolder = [];
  if ( this.frameCounter > 10 ) {
      throw new Error("You've played 10 frames, start a new game!")
  }
};

BowlingGame.prototype.autoRoll = function(){
  pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  if ( this.ball === 1 ) {
      roll_1 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_1);
      this.frameHolder[0] = roll_1;
      this.ball++;
      return roll_1;
  } else if ( this.ball === 2 ) {
      roll_2 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_2);
      this.frameHolder[1] = roll_2;
      this.autoFrameValidate();
      return roll_2;
  }
};

BowlingGame.prototype.autoFrameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
      if (validate > 10 ) {
        this.rollTracker.pop();
        this.frameHolder.pop();
        this.autoRoll().roll_2;
    } else {
      return this.frameValidate();
    }
  };

BowlingGame.prototype.score = function() {
  let calc = 0;
  let i = 0;
  for (var frame = 0; frame < 10; frame++) {
      if (this.scoreStrike(i)) {
          calc += 10 + this.rollTracker[i+1] + this.rollTracker[i+2];
          i++;
      } else if (this.scoreSpare(i)){
          calc += 10 + this.rollTracker[i+2];
          i+=2;
      } else {
          calc += this.rollTracker[i] + this.rollTracker[i+1];
          i+=2;
      }
  }
  return calc;
};

BowlingGame.prototype.gameOver = function() {
  return 'game over';
  if (this.score() < 150 ) {
        return "Ouch you need practice!";
        return 'your final score:' + this.score();
  } else if (this.score() > 250 ) {
        return "Woah! Are you a bowling pro?!";
        return 'your final score:' + this.score();
  } else {
        return "Not too bad, keep bowling!";
        return 'your final score:' + this.score();
  }
};

BowlingGame.prototype.frameLayout = function(){
  frames = this.rollTracker;
  frame = 1;
  for (i = 0; frames; i++) {
    // add logic for returning frames in pairs from index 0
  }
};

BowlingGame.prototype.scoreSpare = function(i) {
  return this.rollTracker[i] + this.rollTracker[i+1] === 10;
};

BowlingGame.prototype.scoreStrike = function(i) {
  return this.rollTracker[i] === 10;
};
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';

import {
  ListView,
  View,
} from 'react-native';

import shallowequal from 'shallowequal';
import InvertibleScrollView from 'react-native-invertible-scroll-view';
import md5 from 'md5';
import LoadEarlier from './LoadEarlier';
import Message from './Message';

export default class MessageContainer extends React.Component {
  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
    this.renderFooter = this.renderFooter.bind(this);
    this.renderLoadEarlier = this.renderLoadEarlier.bind(this);
    this.renderScrollComponent = this.renderScrollComponent.bind(this);

    const dataSource = new ListView.DataSource({
      rowHasChanged: (r1, r2) => {
        return r1.hash !== r2.hash;
      }
    });

    const messagesData = this.prepareMessages(props.messages);
    this.state = {
      dataSource: dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    };
  }

  prepareMessages(messages) {
    return {
      keys: messages.map(m => m._id),
      blob: messages.reduce((o, m, i) => {
        const previousMessage = messages[i + 1] || {};
        const nextMessage = messages[i - 1] || {};
        // add next and previous messages to hash to ensure updates
        const toHash = JSON.stringify(m) + previousMessage._id + nextMessage._id;
        o[m._id] = {
          ...m,
          previousMessage,
          nextMessage,
          hash: md5(toHash)
        };
        return o;
      }, {})
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (!shallowequal(this.props, nextProps)) {
      return true;
    }
    if (!shallowequal(this.state, nextState)) {
      return true;
    }
    return false;
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.messages === nextProps.messages) {
      return;
    }
    const messagesData = this.prepareMessages(nextProps.messages);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    });
  }

  renderFooter() {
    if (this.props.renderFooter) {
      const footerProps = {
        ...this.props,
      };
      return this.props.renderFooter(footerProps);
    }
    return null;
  }

  renderLoadEarlier() {
    if (this.props.loadEarlier === true) {
      const loadEarlierProps = {
        ...this.props,
      };
      if (this.props.renderLoadEarlier) {
        return this.props.renderLoadEarlier(loadEarlierProps);
      }
      return (
        <LoadEarlier {...loadEarlierProps}/>
      );
    }
    return null;
  }

  scrollTo(options) {
    this._invertibleScrollViewRef.scrollTo(options);
  }

  renderRow(message, sectionId, rowId) {
    if (!message._id && message._id !== 0) {
      console.warn('GiftedChat: `_id` is missing for message', JSON.stringify(message));
    }
    if (!message.user) {
      console.warn('GiftedChat: `user` is missing for message', JSON.stringify(message));
      message.user = {};
    }

    const messageProps = {
      ...this.props,
      key: message._id,
      currentMessage: message,
      previousMessage: message.previousMessage,
      nextMessage: message.nextMessage,
      position: message.user._id === this.props.user._id ? 'right' : 'left',
    };

    if (this.props.renderMessage) {
      return this.props.renderMessage(messageProps);
    }
    return <Message {...messageProps}/>;
  }

  renderScrollComponent(props) {
    const invertibleScrollViewProps = this.props.invertibleScrollViewProps;
    return (
      <InvertibleScrollView
        {...props}
        {...invertibleScrollViewProps}
        ref={component => this._invertibleScrollViewRef = component}
      />
    );
  }

  render() {
    return (
      <View ref='container' style={{flex:1}}>
        <ListView
          enableEmptySections={true}
          automaticallyAdjustContentInsets={false}
          initialListSize={20}
          pageSize={20}

          {...this.props.listViewProps}

          dataSource={this.state.dataSource}

          renderRow={this.renderRow}
          renderHeader={this.renderFooter}
          renderFooter={this.renderLoadEarlier}
          renderScrollComponent={this.renderScrollComponent}
        />
      </View>
    );
  }
}

MessageContainer.defaultProps = {
  messages: [],
  user: {},
  renderFooter: null,
  renderMessage: null,
  onLoadEarlier: () => {
  },
};

MessageContainer.propTypes = {
  messages: PropTypes.array,
  user: PropTypes.object,
  renderFooter: PropTypes.func,
  renderMessage: PropTypes.func,
  onLoadEarlier: PropTypes.func,
  listViewProps: PropTypes.object,
};
<commit_msg>set fixed style<commit_after>import PropTypes from 'prop-types';
import React from 'react';

import {
  ListView,
  View,
  StyleSheet,
} from 'react-native';

import shallowequal from 'shallowequal';
import InvertibleScrollView from 'react-native-invertible-scroll-view';
import md5 from 'md5';
import LoadEarlier from './LoadEarlier';
import Message from './Message';

export default class MessageContainer extends React.Component {
  constructor(props) {
    super(props);

    this.renderRow = this.renderRow.bind(this);
    this.renderFooter = this.renderFooter.bind(this);
    this.renderLoadEarlier = this.renderLoadEarlier.bind(this);
    this.renderScrollComponent = this.renderScrollComponent.bind(this);

    const dataSource = new ListView.DataSource({
      rowHasChanged: (r1, r2) => {
        return r1.hash !== r2.hash;
      }
    });

    const messagesData = this.prepareMessages(props.messages);
    this.state = {
      dataSource: dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    };
  }

  prepareMessages(messages) {
    return {
      keys: messages.map(m => m._id),
      blob: messages.reduce((o, m, i) => {
        const previousMessage = messages[i + 1] || {};
        const nextMessage = messages[i - 1] || {};
        // add next and previous messages to hash to ensure updates
        const toHash = JSON.stringify(m) + previousMessage._id + nextMessage._id;
        o[m._id] = {
          ...m,
          previousMessage,
          nextMessage,
          hash: md5(toHash)
        };
        return o;
      }, {})
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (!shallowequal(this.props, nextProps)) {
      return true;
    }
    if (!shallowequal(this.state, nextState)) {
      return true;
    }
    return false;
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.messages === nextProps.messages) {
      return;
    }
    const messagesData = this.prepareMessages(nextProps.messages);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(messagesData.blob, messagesData.keys)
    });
  }

  renderFooter() {
    if (this.props.renderFooter) {
      const footerProps = {
        ...this.props,
      };
      return this.props.renderFooter(footerProps);
    }
    return null;
  }

  renderLoadEarlier() {
    if (this.props.loadEarlier === true) {
      const loadEarlierProps = {
        ...this.props,
      };
      if (this.props.renderLoadEarlier) {
        return this.props.renderLoadEarlier(loadEarlierProps);
      }
      return (
        <LoadEarlier {...loadEarlierProps}/>
      );
    }
    return null;
  }

  scrollTo(options) {
    this._invertibleScrollViewRef.scrollTo(options);
  }

  renderRow(message, sectionId, rowId) {
    if (!message._id && message._id !== 0) {
      console.warn('GiftedChat: `_id` is missing for message', JSON.stringify(message));
    }
    if (!message.user) {
      console.warn('GiftedChat: `user` is missing for message', JSON.stringify(message));
      message.user = {};
    }

    const messageProps = {
      ...this.props,
      key: message._id,
      currentMessage: message,
      previousMessage: message.previousMessage,
      nextMessage: message.nextMessage,
      position: message.user._id === this.props.user._id ? 'right' : 'left',
    };

    if (this.props.renderMessage) {
      return this.props.renderMessage(messageProps);
    }
    return <Message {...messageProps}/>;
  }

  renderScrollComponent(props) {
    const invertibleScrollViewProps = this.props.invertibleScrollViewProps;
    return (
      <InvertibleScrollView
        {...props}
        {...invertibleScrollViewProps}
        ref={component => this._invertibleScrollViewRef = component}
      />
    );
  }

  render() {
    return (
      <View
        ref='container'
        style={styles.container}
      >
        <ListView
          enableEmptySections={true}
          automaticallyAdjustContentInsets={false}
          initialListSize={20}
          pageSize={20}

          {...this.props.listViewProps}

          dataSource={this.state.dataSource}

          renderRow={this.renderRow}
          renderHeader={this.renderFooter}
          renderFooter={this.renderLoadEarlier}
          renderScrollComponent={this.renderScrollComponent}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  }
});

MessageContainer.defaultProps = {
  messages: [],
  user: {},
  renderFooter: null,
  renderMessage: null,
  onLoadEarlier: () => {
  },
};

MessageContainer.propTypes = {
  messages: PropTypes.array,
  user: PropTypes.object,
  renderFooter: PropTypes.func,
  renderMessage: PropTypes.func,
  onLoadEarlier: PropTypes.func,
  listViewProps: PropTypes.object,
};
<|endoftext|>


<commit_before>'use strict';

const SortOptions = require('./SortOptions');
const JoinOptions = require('./JoinOptions');
const ProgramError = require('./error/ProgramError');

class PublicApiOptions
{
	constructor(options = {})
	{
		this.tmpDir = null;
		this.sortOptions = null;
		this.joinOptions = null;
		this.dataSourceResolvers = [];

		for (const k in options) {
			if (!this.hasOwnProperty(k)) {
				throw new ProgramError('Unknown API option: ' + k);
			}

			this[k] = options[k];
		}

		if (!options.sortOptions) {
			this.sortOptions = new SortOptions({});
		} else if (!(options.sortOptions instanceof SortOptions)) {
			this.sortOptions = new SortOptions(options.sortOptions);
		}

		if (!options.joinOptions) {
			this.joinOptions = new JoinOptions({});
		} else if (!(options.sortOptions instanceof JoinOptions)) {
			this.joinOptions = new JoinOptions(options.joinOptions);
		}

		if (this.tmpDir) {
			if (!options.sortOptions.tmpDir) {
				options.sortOptions.tmpDir = this.tmpDir;
			}

			if (!options.joinOptions.tmpDir) {
				options.joinOptions.tmpDir = this.tmpDir;
			}
		}
	}
}

module.exports = PublicApiOptions;
<commit_msg>Fix: tmpDir now has effect on JOIN and ORDER BY<commit_after>'use strict';

const SortOptions = require('./SortOptions');
const JoinOptions = require('./JoinOptions');
const ProgramError = require('./error/ProgramError');

class PublicApiOptions
{
	constructor(options = {})
	{
		this.tmpDir = null;
		this.sortOptions = null;
		this.joinOptions = null;
		this.dataSourceResolvers = [];

		for (const k in options) {
			if (!this.hasOwnProperty(k)) {
				throw new ProgramError('Unknown API option: ' + k);
			}

			this[k] = options[k];
		}

		if (!options.sortOptions) {
			this.sortOptions = new SortOptions({});
		} else if (!(options.sortOptions instanceof SortOptions)) {
			this.sortOptions = new SortOptions(options.sortOptions);
		}

		if (!options.joinOptions) {
			this.joinOptions = new JoinOptions({});
		} else if (!(options.sortOptions instanceof JoinOptions)) {
			this.joinOptions = new JoinOptions(options.joinOptions);
		}

		if (this.tmpDir) {
			if (!(options.sortOptions && options.sortOptions.tmpDir)) {
				this.sortOptions.tmpDir = this.tmpDir;
			}

			if (!(options.joinOptions && options.joinOptions.tmpDir)) {
				this.joinOptions.tmpDir = this.tmpDir;
			}
		}
	}
}

module.exports = PublicApiOptions;
<|endoftext|>


<commit_before>/* global mhaStrings */
/* exported Received */

var Received = (function () {
    var receivedRows = [];
    var sortColumn = "hop";
    var sortOrder = 1;

    // Builds array of values for each header in receivedHeaderNames.
    // This algorithm should work regardless of the order of the headers, given:
    //  - The date, if present, is always at the end, separated by a ";".
    // Values not attached to a header will not be reflected in output.
    function parseHeader(receivedHeader) {
        var parsedRow = {
            sourceHeader: receivedHeader,
            delaySort: -1, // Force the "no previous or current time" rows to sort before the 0 second rows
            percent: 0
        }

        if (!receivedHeader) { return parsedRow; }
        // Strip linefeeds first
        receivedHeader = receivedHeader.replace(/\r|\n|\r\n/g, ' ')

        var receivedHeaderNames = ["from", "by", "with", "id", "for", "via"];

        // Build array of header locations
        var headerMatches = [];

        // Some bad dates don't wrap UTC in paren - fix that first
        receivedHeader = receivedHeader.replace(/UTC|\(UTC\)/gi, "(UTC)");

        // Read out the date first, then clear it from the string
        var iDate = receivedHeader.lastIndexOf(";");

        // No semicolon means no date - or maybe there's one there?
        // Sendgrid is bad about this
        if (iDate === -1) {
            // First try to find a day of the week
            receivedHeader = receivedHeader.replace(/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/g, "; $1");
            iDate = receivedHeader.lastIndexOf(";");
        }

        if (iDate === -1) {
            // Next we look for year-month-day, 4-1/2-1/2
            receivedHeader = receivedHeader.replace(/\s*(\d{4}-\d{1,2}-\d{1,2})/g, "; $1");
            iDate = receivedHeader.lastIndexOf(";");
        }

        if (iDate !== -1 && receivedHeader.length !== iDate + 1) {
            var date = receivedHeader.substring(iDate + 1);
            receivedHeader = receivedHeader.substring(0, iDate);

            // Invert any backwards dates: 2018-01-28 -> 01-28-2018
            date = date.replace(/\s*(\d{4})-(\d{1,2})-(\d{1,2})/g, "$2/$3/$1");
            // Replace dashes with slashes
            date = date.replace(/\s*(\d{1,2})-(\d{1,2})-(\d{4})/g, "$1/$2/$3");

            // If we don't have a +xxxx or -xxxx on our date, it will be interpreted in local time
            // This likely isn't the intended timezone, so we add a +0000 to get UTC
            var offset = date.match(/[+|-]\d{4}/);
            if (!offset || offset.length !== 1) {
                date += " +0000";
            }

            // And now we can parse our date
            var time = moment(date);
            parsedRow.dateNum = time.valueOf();

            //                date = time.format("ddd MMM D YYYY HH:mm:ss [GMT]ZZ");
            parsedRow.date = time.format("LL LTS");
            parsedRow.dateSort = parsedRow.dateNum;
        }

        // Scan for malformed postFix headers
        // Received: by example.com (Postfix, from userid 1001)
        //   id 1234ABCD; Thu, 21 Aug 2014 12:12:48 +0200 (CEST)
        var postFix = receivedHeader.match(/(.*)by (.*? \(Postfix, from userid .*?\))(.*)/mi);
        if (postFix) {
            parsedRow.by = postFix[2];
            receivedHeader = postFix[1] + postFix[3];
            receivedHeaderNames = removeEntry(receivedHeaderNames, "by");
        }

        // Scan for malformed qmail headers
        // Received: (qmail 10876 invoked from network); 24 Aug 2014 16:13:38 -0000
        var qmail = receivedHeader.match(/(.*)\((qmail .*? invoked from .*?)\)(.*)/mi);
        if (qmail) {
            parsedRow.by = qmail[2];
            receivedHeader = qmail[1] + qmail[3];
            receivedHeaderNames = removeEntry(receivedHeaderNames, "by");
        }

        // Split up the string now so we can look for our headers
        var tokens = receivedHeader.split(/\s+/);

        var iMatch = 0;
        receivedHeaderNames.forEach(function (receivedHeaderName, iHeader) {
            tokens.some(function (token, iToken) {
                if (receivedHeaderName.toUpperCase() === token.toUpperCase()) {
                    headerMatches[iMatch++] = { iHeader: iHeader, iToken: iToken };
                    // We don't return true so we can match any duplicate headers
                    // In doing this, we risk failing to parse a string where a header
                    // keyword appears as the value for another header
                    // Both situations are invalid input
                    // We're just picking which one we'd prefer to handle
                }
            });
        });

        // Next bit assumes headerMatches[x,y] is increasing on y.
        // Sort it so it is.
        headerMatches.sort(function (a, b) { return a.iToken - b.iToken; });

        headerMatches.forEach(function (headerMatch, iMatch) {
            var iNextTokenHeader;
            if (iMatch + 1 < headerMatches.length) {
                iNextTokenHeader = headerMatches[iMatch + 1].iToken;
            } else {
                iNextTokenHeader = tokens.length;
            }

            var headerName = receivedHeaderNames[headerMatch.iHeader];
            if (parsedRow[headerName] === undefined) { parsedRow[headerName] = ""; }
            if (parsedRow[headerName] !== "") { parsedRow[headerName] += "; "; }
            parsedRow[headerName] += tokens.slice(headerMatch.iToken + 1, iNextTokenHeader).join(" ").trim();
        });

        return parsedRow;
    }

    function exists() { return receivedRows.length > 0; }

    function doSort(col) {
        if (sortColumn === col) {
            sortOrder *= -1;
        } else {
            sortColumn = col;
            sortOrder = 1;
        }

        if (sortColumn + "Sort" in receivedRows[0]) {
            col = col + "Sort";
        }

        var that = this;
        receivedRows.sort(function (a, b) {
            return that.sortOrder * (a[col] < b[col] ? -1 : 1);
        });
    }

    function removeEntry(stringArray, entry) {
        var i = stringArray.indexOf(entry);
        if (i >= 0) {
            stringArray.splice(i, 1);
        }

        return stringArray;
    }

    function init(receivedHeader) { receivedRows.push(parseHeader(receivedHeader)); }

    function computeDeltas() {
        // Process received headers in reverse order
        receivedRows.reverse();

        // Parse rows and compute values needed for the "Delay" column
        var iStartTime = 0;
        var iEndTime = 0;
        var iLastTime = NaN;
        var iDelta = 0; // This will be the sum of our positive deltas

        receivedRows.forEach(function (row) {
            if (!isNaN(row.dateNum)) {
                if (!isNaN(iLastTime) && iLastTime < row.dateNum) {
                    iDelta += row.dateNum - iLastTime;
                }

                iStartTime = iStartTime || row.dateNum;
                iEndTime = row.dateNum;
                iLastTime = row.dateNum;
            }
        });

        iLastTime = NaN;

        receivedRows.forEach(function (row, index) {
            row.hop = index + 1;
            row.delay = computeTime(row.dateNum, iLastTime);

            if (!isNaN(row.dateNum) && !isNaN(iLastTime) && iDelta !== 0) {
                row.delaySort = row.dateNum - iLastTime;

                // Only positive delays will get percentage bars. Negative delays will be color coded at render time.
                if (row.delaySort > 0) {
                    row.percent = 100 * row.delaySort / iDelta;
                }
            }

            if (!isNaN(row.dateNum)) {
                iLastTime = row.dateNum;
            }
        });

        // Total time is still last minus first, even if negative.
        return iEndTime !== iStartTime ? computeTime(iEndTime, iStartTime) : "";
    }

    // Computes min/sec from the diff of current and last.
    // Returns nothing if last or current is NaN.
    function computeTime(current, last) {
        var time = [];

        if (isNaN(current) || isNaN(last)) { return ""; }
        var diff = current - last;
        var iDelay;
        var printedMinutes = false;

        if (Math.abs(diff) < 1000) {
            return "0 " + mhaStrings.mha_seconds;
        }

        if (diff < 0) {
            time.push(mhaStrings.mha_negative);
            diff = -diff;
        }

        if (diff >= 1000 * 60) {
            iDelay = Math.floor(diff / 1000 / 60);
            time.push(iDelay, " ");
            if (iDelay === 1) {
                time.push(mhaStrings.mha_minute);
            } else {
                time.push(mhaStrings.mha_minutes);
            }

            diff -= iDelay * 1000 * 60;
            printedMinutes = true;
        }

        if (printedMinutes && diff) {
            time.push(" ");
        }

        if (!printedMinutes || diff) {
            iDelay = Math.floor(diff / 1000);
            time.push(iDelay, " ");
            if (iDelay === 1) {
                time.push(mhaStrings.mha_second);
            } else {
                time.push(mhaStrings.mha_seconds);
            }
        }

        return time.join("");
    }

    return {
        init: init,
        exists: exists,
        doSort: doSort,
        computeDeltas: computeDeltas,
        get receivedRows() { return receivedRows; },
        get sortColumn() { return sortColumn; },
        get sortOrder() { return sortOrder; },
        parseHeader: parseHeader, // For testing only
        computeTime: computeTime // For testing only
    }
});<commit_msg>format moment output to match edge<commit_after>/* global mhaStrings */
/* exported Received */

var Received = (function () {
    var receivedRows = [];
    var sortColumn = "hop";
    var sortOrder = 1;

    // Builds array of values for each header in receivedHeaderNames.
    // This algorithm should work regardless of the order of the headers, given:
    //  - The date, if present, is always at the end, separated by a ";".
    // Values not attached to a header will not be reflected in output.
    function parseHeader(receivedHeader) {
        var parsedRow = {
            sourceHeader: receivedHeader,
            delaySort: -1, // Force the "no previous or current time" rows to sort before the 0 second rows
            percent: 0
        }

        if (!receivedHeader) { return parsedRow; }
        // Strip linefeeds first
        receivedHeader = receivedHeader.replace(/\r|\n|\r\n/g, ' ')

        var receivedHeaderNames = ["from", "by", "with", "id", "for", "via"];

        // Build array of header locations
        var headerMatches = [];

        // Some bad dates don't wrap UTC in paren - fix that first
        receivedHeader = receivedHeader.replace(/UTC|\(UTC\)/gi, "(UTC)");

        // Read out the date first, then clear it from the string
        var iDate = receivedHeader.lastIndexOf(";");

        // No semicolon means no date - or maybe there's one there?
        // Sendgrid is bad about this
        if (iDate === -1) {
            // First try to find a day of the week
            receivedHeader = receivedHeader.replace(/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)/g, "; $1");
            iDate = receivedHeader.lastIndexOf(";");
        }

        if (iDate === -1) {
            // Next we look for year-month-day, 4-1/2-1/2
            receivedHeader = receivedHeader.replace(/\s*(\d{4}-\d{1,2}-\d{1,2})/g, "; $1");
            iDate = receivedHeader.lastIndexOf(";");
        }

        if (iDate !== -1 && receivedHeader.length !== iDate + 1) {
            var date = receivedHeader.substring(iDate + 1);
            receivedHeader = receivedHeader.substring(0, iDate);

            // Invert any backwards dates: 2018-01-28 -> 01-28-2018
            date = date.replace(/\s*(\d{4})-(\d{1,2})-(\d{1,2})/g, "$2/$3/$1");
            // Replace dashes with slashes
            date = date.replace(/\s*(\d{1,2})-(\d{1,2})-(\d{4})/g, "$1/$2/$3");

            // If we don't have a +xxxx or -xxxx on our date, it will be interpreted in local time
            // This likely isn't the intended timezone, so we add a +0000 to get UTC
            var offset = date.match(/[+|-]\d{4}/);
            if (!offset || offset.length !== 1) {
                date += " +0000";
            }

            // And now we can parse our date
            var time = moment(date);
            parsedRow.dateNum = time.valueOf();
            parsedRow.date = time.format("l LTS");
            parsedRow.dateSort = parsedRow.dateNum;
        }

        // Scan for malformed postFix headers
        // Received: by example.com (Postfix, from userid 1001)
        //   id 1234ABCD; Thu, 21 Aug 2014 12:12:48 +0200 (CEST)
        var postFix = receivedHeader.match(/(.*)by (.*? \(Postfix, from userid .*?\))(.*)/mi);
        if (postFix) {
            parsedRow.by = postFix[2];
            receivedHeader = postFix[1] + postFix[3];
            receivedHeaderNames = removeEntry(receivedHeaderNames, "by");
        }

        // Scan for malformed qmail headers
        // Received: (qmail 10876 invoked from network); 24 Aug 2014 16:13:38 -0000
        var qmail = receivedHeader.match(/(.*)\((qmail .*? invoked from .*?)\)(.*)/mi);
        if (qmail) {
            parsedRow.by = qmail[2];
            receivedHeader = qmail[1] + qmail[3];
            receivedHeaderNames = removeEntry(receivedHeaderNames, "by");
        }

        // Split up the string now so we can look for our headers
        var tokens = receivedHeader.split(/\s+/);

        var iMatch = 0;
        receivedHeaderNames.forEach(function (receivedHeaderName, iHeader) {
            tokens.some(function (token, iToken) {
                if (receivedHeaderName.toUpperCase() === token.toUpperCase()) {
                    headerMatches[iMatch++] = { iHeader: iHeader, iToken: iToken };
                    // We don't return true so we can match any duplicate headers
                    // In doing this, we risk failing to parse a string where a header
                    // keyword appears as the value for another header
                    // Both situations are invalid input
                    // We're just picking which one we'd prefer to handle
                }
            });
        });

        // Next bit assumes headerMatches[x,y] is increasing on y.
        // Sort it so it is.
        headerMatches.sort(function (a, b) { return a.iToken - b.iToken; });

        headerMatches.forEach(function (headerMatch, iMatch) {
            var iNextTokenHeader;
            if (iMatch + 1 < headerMatches.length) {
                iNextTokenHeader = headerMatches[iMatch + 1].iToken;
            } else {
                iNextTokenHeader = tokens.length;
            }

            var headerName = receivedHeaderNames[headerMatch.iHeader];
            if (parsedRow[headerName] === undefined) { parsedRow[headerName] = ""; }
            if (parsedRow[headerName] !== "") { parsedRow[headerName] += "; "; }
            parsedRow[headerName] += tokens.slice(headerMatch.iToken + 1, iNextTokenHeader).join(" ").trim();
        });

        return parsedRow;
    }

    function exists() { return receivedRows.length > 0; }

    function doSort(col) {
        if (sortColumn === col) {
            sortOrder *= -1;
        } else {
            sortColumn = col;
            sortOrder = 1;
        }

        if (sortColumn + "Sort" in receivedRows[0]) {
            col = col + "Sort";
        }

        var that = this;
        receivedRows.sort(function (a, b) {
            return that.sortOrder * (a[col] < b[col] ? -1 : 1);
        });
    }

    function removeEntry(stringArray, entry) {
        var i = stringArray.indexOf(entry);
        if (i >= 0) {
            stringArray.splice(i, 1);
        }

        return stringArray;
    }

    function init(receivedHeader) { receivedRows.push(parseHeader(receivedHeader)); }

    function computeDeltas() {
        // Process received headers in reverse order
        receivedRows.reverse();

        // Parse rows and compute values needed for the "Delay" column
        var iStartTime = 0;
        var iEndTime = 0;
        var iLastTime = NaN;
        var iDelta = 0; // This will be the sum of our positive deltas

        receivedRows.forEach(function (row) {
            if (!isNaN(row.dateNum)) {
                if (!isNaN(iLastTime) && iLastTime < row.dateNum) {
                    iDelta += row.dateNum - iLastTime;
                }

                iStartTime = iStartTime || row.dateNum;
                iEndTime = row.dateNum;
                iLastTime = row.dateNum;
            }
        });

        iLastTime = NaN;

        receivedRows.forEach(function (row, index) {
            row.hop = index + 1;
            row.delay = computeTime(row.dateNum, iLastTime);

            if (!isNaN(row.dateNum) && !isNaN(iLastTime) && iDelta !== 0) {
                row.delaySort = row.dateNum - iLastTime;

                // Only positive delays will get percentage bars. Negative delays will be color coded at render time.
                if (row.delaySort > 0) {
                    row.percent = 100 * row.delaySort / iDelta;
                }
            }

            if (!isNaN(row.dateNum)) {
                iLastTime = row.dateNum;
            }
        });

        // Total time is still last minus first, even if negative.
        return iEndTime !== iStartTime ? computeTime(iEndTime, iStartTime) : "";
    }

    // Computes min/sec from the diff of current and last.
    // Returns nothing if last or current is NaN.
    function computeTime(current, last) {
        var time = [];

        if (isNaN(current) || isNaN(last)) { return ""; }
        var diff = current - last;
        var iDelay;
        var printedMinutes = false;

        if (Math.abs(diff) < 1000) {
            return "0 " + mhaStrings.mha_seconds;
        }

        if (diff < 0) {
            time.push(mhaStrings.mha_negative);
            diff = -diff;
        }

        if (diff >= 1000 * 60) {
            iDelay = Math.floor(diff / 1000 / 60);
            time.push(iDelay, " ");
            if (iDelay === 1) {
                time.push(mhaStrings.mha_minute);
            } else {
                time.push(mhaStrings.mha_minutes);
            }

            diff -= iDelay * 1000 * 60;
            printedMinutes = true;
        }

        if (printedMinutes && diff) {
            time.push(" ");
        }

        if (!printedMinutes || diff) {
            iDelay = Math.floor(diff / 1000);
            time.push(iDelay, " ");
            if (iDelay === 1) {
                time.push(mhaStrings.mha_second);
            } else {
                time.push(mhaStrings.mha_seconds);
            }
        }

        return time.join("");
    }

    return {
        init: init,
        exists: exists,
        doSort: doSort,
        computeDeltas: computeDeltas,
        get receivedRows() { return receivedRows; },
        get sortColumn() { return sortColumn; },
        get sortOrder() { return sortOrder; },
        parseHeader: parseHeader, // For testing only
        computeTime: computeTime // For testing only
    }
});<|endoftext|>


<commit_before>'use strict'

const Promise = require('bluebird')
const _ = require('lodash')
const eventToPromise = require('event-to-promise')
const { asyncLimit } = require('./utils')

const co = Promise.coroutine

// Associations for user class.
class UserAssociations {

  constructor (props) {
    _.defaults(this, props)
  }

  userJoinRoomReport (userName, roomName) {
    this.transport.emitToChannel(roomName, 'roomUserJoined', roomName, userName)
  }

  userLeftRoomReport (userName, roomName, enableUserlistUpdates) {
    if (enableUserlistUpdates) {
      this.transport.emitToChannel(roomName, 'roomUserLeft', roomName, userName)
    }
  }

  userRemovedReport (userName, roomName, enableUserlistUpdates) {
    let cn = this.echoChannel
    this.transport.emitToChannel(cn, 'roomAccessRemoved', roomName)
    this.userLeftRoomReport(userName, roomName, enableUserlistUpdates)
  }

  socketJoinEcho (id, roomName, njoined, isLocalCall) {
    if (isLocalCall) {
      this.transport.emitToChannel(
        this.echoChannel, 'roomJoinedEcho', roomName, id, njoined)
    } else {
      this.transport.sendToChannel(
        id, this.echoChannel, 'roomJoinedEcho', roomName, id, njoined)
    }
  }

  socketLeftEcho (id, roomName, njoined, isLocalCall) {
    if (isLocalCall) {
      this.transport.emitToChannel(
        this.echoChannel, 'roomLeftEcho', roomName, id, njoined)
    } else {
      this.transport.sendToChannel(
        id, this.echoChannel, 'roomLeftEcho', roomName, id, njoined)
    }
  }

  socketConnectEcho (id, nconnected) {
    this.transport.sendToChannel(
      id, this.echoChannel, 'socketConnectEcho', id, nconnected)
  }

  socketDisconnectEcho (id, nconnected) {
    this.transport.sendToChannel(
      id, this.echoChannel, 'socketDisconnectEcho', id, nconnected)
  }

  leaveChannel (id, channel) {
    return this.transport.leaveChannel(id, channel).catch(e => {
      let info = { roomName: channel, id, opType: 'transportChannel' }
      return this.consistencyFailure(e, info)
    })
  }

  socketLeaveChannels (id, channels) {
    return Promise.map(
      channels,
      channel => this.leaveChannel(id, channel),
      { concurrency: asyncLimit })
  }

  leaveChannelMessage (id, channel) {
    let bus = this.clusterBus
    return Promise.try(() => {
      bus.emit('roomLeaveSocket', id, channel)
      return eventToPromise(bus, bus.makeSocketRoomLeftName(id, channel))
    }).timeout(this.busAckTimeout).catchReturn()
  }

  channelLeaveSockets (channel, ids) {
    return Promise.map(
      ids,
      id => this.leaveChannelMessage(id, channel),
      { concurrency: asyncLimit })
  }

  rollbackRoomJoin (error, roomName, id) {
    return this.userState.removeSocketFromRoom(id, roomName).catch(e => {
      this.consistencyFailure(e, { roomName, opType: 'userRooms' })
      return [1]
    }).spread(njoined => {
      if (!njoined) {
        return this.leaveRoom(roomName)
      } else {
        return Promise.resolve()
      }
    }).thenThrow(error)
  }

  leaveRoom (roomName) {
    return Promise
      .try(() => this.state.getRoom(roomName))
      .then(room => room.leave(this.userName)
            .catch(error => this.consistencyFailure(
              error, {roomName, opType: 'roomUserlist'})))
      .catchReturn()
  }

  getNotifySettings (roomName) {
    return Promise
      .try(() => this.state.getRoom(roomName))
      .then(room => room.getNotificationsInfo(null, true))
      .catchReturn({})
  }

  joinSocketToRoom (id, roomName, isLocalCall) {
    let lock = this.userState.lockToRoom(roomName, this.lockTTL)
    return Promise.using(lock, co(function * () {
      let room = yield this.state.getRoom(roomName)
      yield room.join(this.userName)
      let enableUserlistUpdates = yield room.roomState.userlistUpdatesGet()
      return this.userState.addSocketToRoom(id, roomName).spread(
        (njoined, hasChanged) => {
          return this.transport.joinChannel(id, roomName).then(() => {
            if (hasChanged) {
              if (njoined === 1 && enableUserlistUpdates) {
                this.userJoinRoomReport(this.userName, roomName)
              }
              this.socketJoinEcho(id, roomName, njoined, isLocalCall)
            }
          }).return(njoined)
        }).catch(e => this.rollbackRoomJoin(e, roomName, id))
    }).bind(this))
  }

  leaveSocketFromRoom (id, roomName, isLocalCall) {
    let lock = this.userState.lockToRoom(roomName, this.lockTTL)
    return Promise.using(lock, co(function * () {
      let [njoined, hasChanged] =
            yield this.userState.removeSocketFromRoom(id, roomName)
      yield this.leaveChannel(id, roomName)
      if (njoined === 0) {
        yield this.leaveRoom(roomName)
      }
      if (hasChanged) {
        let { enableUserlistUpdates } = yield this.getNotifySettings(roomName)
        this.socketLeftEcho(id, roomName, njoined, isLocalCall)
        this.userLeftRoomReport(this.userName, roomName, enableUserlistUpdates)
      }
      return Promise.resolve(njoined)
    }).bind(this))
  }

  removeUserSocket (id) {
    return this.userState.removeSocket(id)
      .spread((roomsRemoved, joinedSockets, nconnected) => {
        roomsRemoved = roomsRemoved || []
        joinedSockets = joinedSockets || []
        nconnected = nconnected || 0
        return this.socketLeaveChannels(id, roomsRemoved).then(() => {
          return Promise.map(
            roomsRemoved,
            (roomName, idx) => {
              let njoined = joinedSockets[idx]
              this.socketLeftEcho(id, roomName, njoined)
              if (njoined) { return Promise.resolve() }
              return this.leaveRoom(roomName)
                .then(() => this.getNotifySettings(roomName))
                .then(({enableUserlistUpdates}) => this.userLeftRoomReport(
                  this.userName, roomName, enableUserlistUpdates))
            },
            { concurrency: asyncLimit })
            .then(() => this.socketDisconnectEcho(id, nconnected))
        })
      }).then(() => this.state.removeSocket(id))
  }

  removeSocketFromServer (id) {
    return this.removeUserSocket(id).catch(e => {
      let info = { id, opType: 'userSockets' }
      return this.consistencyFailure(e, info)
    })
  }

  removeUserSocketsFromRoom (roomName) {
    return this.userState.removeAllSocketsFromRoom(roomName).catch(e => {
      let info = { roomName, opType: 'roomUserlist' }
      return this.consistencyFailure(e, info)
    })
  }

  removeFromRoom (roomName) {
    let lock = this.userState.lockToRoom(roomName, this.lockTTL)
    return Promise.using(lock, co(function * () {
      let removedSockets = yield this.removeUserSocketsFromRoom(roomName)
      removedSockets = removedSockets || []
      yield this.channelLeaveSockets(roomName, removedSockets)
      if (removedSockets.length) {
        let { enableUserlistUpdates } = yield this.getNotifySettings(roomName)
        this.userRemovedReport(this.userName, roomName, enableUserlistUpdates)
      }
      return this.leaveRoom(roomName)
    }).bind(this))
  }

  removeRoomUsers (roomName, userNames) {
    userNames = userNames || []
    return Promise.map(
      userNames,
      userName => {
        return this.state.getUser(userName)
          .then(user => user.removeFromRoom(roomName))
          .catchReturn()
      },
      { concurrency: asyncLimit })
  }

}

module.exports = UserAssociations
<commit_msg>refactor: use more generators in user associations<commit_after>'use strict'

const Promise = require('bluebird')
const _ = require('lodash')
const eventToPromise = require('event-to-promise')
const { asyncLimit } = require('./utils')
const { run } = require('./utils')

const co = Promise.coroutine

// Associations for user class.
class UserAssociations {

  constructor (props) {
    _.defaults(this, props)
  }

  userJoinRoomReport (userName, roomName) {
    this.transport.emitToChannel(roomName, 'roomUserJoined', roomName, userName)
  }

  userLeftRoomReport (userName, roomName, enableUserlistUpdates) {
    if (enableUserlistUpdates) {
      this.transport.emitToChannel(roomName, 'roomUserLeft', roomName, userName)
    }
  }

  userRemovedReport (userName, roomName, enableUserlistUpdates) {
    let cn = this.echoChannel
    this.transport.emitToChannel(cn, 'roomAccessRemoved', roomName)
    this.userLeftRoomReport(userName, roomName, enableUserlistUpdates)
  }

  socketJoinEcho (id, roomName, njoined, isLocalCall) {
    if (isLocalCall) {
      this.transport.emitToChannel(
        this.echoChannel, 'roomJoinedEcho', roomName, id, njoined)
    } else {
      this.transport.sendToChannel(
        id, this.echoChannel, 'roomJoinedEcho', roomName, id, njoined)
    }
  }

  socketLeftEcho (id, roomName, njoined, isLocalCall) {
    if (isLocalCall) {
      this.transport.emitToChannel(
        this.echoChannel, 'roomLeftEcho', roomName, id, njoined)
    } else {
      this.transport.sendToChannel(
        id, this.echoChannel, 'roomLeftEcho', roomName, id, njoined)
    }
  }

  socketConnectEcho (id, nconnected) {
    this.transport.sendToChannel(
      id, this.echoChannel, 'socketConnectEcho', id, nconnected)
  }

  socketDisconnectEcho (id, nconnected) {
    this.transport.sendToChannel(
      id, this.echoChannel, 'socketDisconnectEcho', id, nconnected)
  }

  leaveChannel (id, channel) {
    return this.transport.leaveChannel(id, channel).catch(e => {
      let info = { roomName: channel, id, opType: 'transportChannel' }
      return this.consistencyFailure(e, info)
    })
  }

  socketLeaveChannels (id, channels) {
    return Promise.map(
      channels,
      channel => this.leaveChannel(id, channel),
      { concurrency: asyncLimit })
  }

  leaveChannelMessage (id, channel) {
    let bus = this.clusterBus
    return Promise.try(() => {
      bus.emit('roomLeaveSocket', id, channel)
      return eventToPromise(bus, bus.makeSocketRoomLeftName(id, channel))
    }).timeout(this.busAckTimeout).catchReturn()
  }

  channelLeaveSockets (channel, ids) {
    return Promise.map(
      ids,
      id => this.leaveChannelMessage(id, channel),
      { concurrency: asyncLimit })
  }

  rollbackRoomJoin (error, roomName, id) {
    return this.userState.removeSocketFromRoom(id, roomName).catch(e => {
      this.consistencyFailure(e, { roomName, opType: 'userRooms' })
      return [1]
    }).spread(njoined => {
      if (!njoined) {
        return this.leaveRoom(roomName)
      } else {
        return Promise.resolve()
      }
    }).thenThrow(error)
  }

  leaveRoom (roomName) {
    return Promise
      .try(() => this.state.getRoom(roomName))
      .then(room => room.leave(this.userName).catch(error => {
        this.consistencyFailure(error, {roomName, opType: 'roomUserlist'})
      }))
      .catchReturn()
  }

  getNotifySettings (roomName) {
    return Promise
      .try(() => this.state.getRoom(roomName))
      .then(room => room.getNotificationsInfo(null, true))
      .catchReturn({})
  }

  joinSocketToRoom (id, roomName, isLocalCall) {
    let lock = this.userState.lockToRoom(roomName, this.lockTTL)
    return Promise.using(lock, co(function * () {
      let room = yield this.state.getRoom(roomName)
      yield room.join(this.userName)
      try {
        let enableUserlistUpdates = yield room.roomState.userlistUpdatesGet()
        let [njoined, hasChanged] =
              yield this.userState.addSocketToRoom(id, roomName)
        yield this.transport.joinChannel(id, roomName)
        if (hasChanged) {
          if (njoined === 1 && enableUserlistUpdates) {
            this.userJoinRoomReport(this.userName, roomName)
          }
          this.socketJoinEcho(id, roomName, njoined, isLocalCall)
        }
        return njoined
      } catch (e) {
        yield this.rollbackRoomJoin(e, roomName, id)
      }
    }).bind(this))
  }

  leaveSocketFromRoom (id, roomName, isLocalCall) {
    let lock = this.userState.lockToRoom(roomName, this.lockTTL)
    return Promise.using(lock, co(function * () {
      let [njoined, hasChanged] =
            yield this.userState.removeSocketFromRoom(id, roomName)
      yield this.leaveChannel(id, roomName)
      if (njoined === 0) {
        yield this.leaveRoom(roomName)
      }
      if (hasChanged) {
        let { enableUserlistUpdates } = yield this.getNotifySettings(roomName)
        this.socketLeftEcho(id, roomName, njoined, isLocalCall)
        this.userLeftRoomReport(this.userName, roomName, enableUserlistUpdates)
      }
      return Promise.resolve(njoined)
    }).bind(this))
  }

  removeUserSocket (id) {
    return run(this, function * () {
      let [roomsRemoved, joinedSockets, nconnected] =
            yield this.userState.removeSocket(id)
      roomsRemoved = roomsRemoved || []
      joinedSockets = joinedSockets || []
      nconnected = nconnected || 0
      yield this.socketLeaveChannels(id, roomsRemoved)
      yield Promise.map(roomsRemoved, (roomName, idx) => {
        let njoined = joinedSockets[idx]
        this.socketLeftEcho(id, roomName, njoined)
        if (njoined) { return Promise.resolve() }
        return this.leaveRoom(roomName)
          .then(() => this.getNotifySettings(roomName))
          .then(({enableUserlistUpdates}) => this.userLeftRoomReport(
            this.userName, roomName, enableUserlistUpdates))
      }, { concurrency: asyncLimit })
      this.socketDisconnectEcho(id, nconnected)
      return this.state.removeSocket(id)
    })
  }

  removeSocketFromServer (id) {
    return this.removeUserSocket(id).catch(e => {
      let info = { id, opType: 'userSockets' }
      return this.consistencyFailure(e, info)
    })
  }

  removeUserSocketsFromRoom (roomName) {
    return this.userState.removeAllSocketsFromRoom(roomName).catch(e => {
      let info = { roomName, opType: 'roomUserlist' }
      return this.consistencyFailure(e, info)
    })
  }

  removeFromRoom (roomName) {
    let lock = this.userState.lockToRoom(roomName, this.lockTTL)
    return Promise.using(lock, co(function * () {
      let removedSockets = yield this.removeUserSocketsFromRoom(roomName)
      removedSockets = removedSockets || []
      yield this.channelLeaveSockets(roomName, removedSockets)
      if (removedSockets.length) {
        let { enableUserlistUpdates } = yield this.getNotifySettings(roomName)
        this.userRemovedReport(this.userName, roomName, enableUserlistUpdates)
      }
      return this.leaveRoom(roomName)
    }).bind(this))
  }

  removeRoomUsers (roomName, userNames) {
    userNames = userNames || []
    return Promise.map(
      userNames,
      userName => {
        return this.state.getUser(userName)
          .then(user => user.removeFromRoom(roomName))
          .catchReturn()
      }, { concurrency: asyncLimit })
  }

}

module.exports = UserAssociations
<|endoftext|>


<commit_before>import React from 'react';
import { Box } from './inc';
import styler from 'stylefire';
import pointer from '../packages/popmotion/lib/input/pointer';
import touch from '../packages/popmotion/lib/input/touch';
import spring from '../packages/popmotion/lib/animations/spring';
import value from '../packages/popmotion/lib/reactions/value';
import { applyOffset } from '../packages/popmotion/lib/transformers';

export class Drag extends React.Component {
  setRef = (dom) => {
    if (!dom) dom;
    this.box = styler(dom);
  };

  startDrag = () => {
    document.addEventListener('mouseup', this.stopDrag);
    document.addEventListener('touchend', this.stopDrag);
    this.drag = pointer()
      .pipe(({x}) => x, applyOffset(this.box.get('x')))
      .start((v) => this.box.set('x', v));
  };

  stopDrag = () => {
    if (this.drag) this.drag.stop();
    document.removeEventListener('mouseup', this.stopDrag);
    document.removeEventListener('touchend', this.stopDrag);
  };

  render() {
    return <Box onMouseDown={this.startDrag} onTouchStart={this.startDrag} innerRef={this.setRef} />
  }
}

export class DragWithDeltaPointer extends React.Component {
  setRef = (dom) => {
    if (!dom) dom;
    this.box = styler(dom);
    this.boxXY = value({ x: 0, y: 0 });
    this.boxXY.subscribe(this.box.set);
  };

  startDrag = () => {
    document.addEventListener('mouseup', this.stopDrag);
    document.addEventListener('touchend', this.stopDrag);
    pointer(this.boxXY.get()).start(this.boxXY);
  };

  stopDrag = () => {
    spring({
      from: this.boxXY.get(),
      to: 0,
      velocity: this.boxXY.getVelocity(),
      stiffness: 100,
    }).start(this.boxXY)
    document.removeEventListener('mouseup', this.stopDrag);
    document.removeEventListener('touchend', this.stopDrag);
  };

  render() {
    return <Box onMouseDown={this.startDrag} onTouchStart={this.startDrag} innerRef={this.setRef} />
  }
}

export class Multitouch extends React.Component {
  componentDidMount() {
    document.addEventListener('touchstart', () => {
      touch()
        .while(({ touches }) => touches.length > 1)
        .start(console.log)
    })
  }

  render() {
    return null;
  }
}
<commit_msg>fix: touch rename multitouch<commit_after>import React from 'react';
import { Box } from './inc';
import styler from 'stylefire';
import pointer from '../packages/popmotion/lib/input/pointer';
import multitouch from '../packages/popmotion/lib/input/multitouch';
import spring from '../packages/popmotion/lib/animations/spring';
import value from '../packages/popmotion/lib/reactions/value';
import { applyOffset } from '../packages/popmotion/lib/transformers';

export class Drag extends React.Component {
  setRef = (dom) => {
    if (!dom) dom;
    this.box = styler(dom);
  };

  startDrag = () => {
    document.addEventListener('mouseup', this.stopDrag);
    document.addEventListener('touchend', this.stopDrag);
    this.drag = pointer()
      .pipe(({x}) => x, applyOffset(this.box.get('x')))
      .start((v) => this.box.set('x', v));
  };

  stopDrag = () => {
    if (this.drag) this.drag.stop();
    document.removeEventListener('mouseup', this.stopDrag);
    document.removeEventListener('touchend', this.stopDrag);
  };

  render() {
    return <Box onMouseDown={this.startDrag} onTouchStart={this.startDrag} innerRef={this.setRef} />
  }
}

export class DragWithDeltaPointer extends React.Component {
  setRef = (dom) => {
    if (!dom) dom;
    this.box = styler(dom);
    this.boxXY = value({ x: 0, y: 0 });
    this.boxXY.subscribe(this.box.set);
  };

  startDrag = () => {
    document.addEventListener('mouseup', this.stopDrag);
    document.addEventListener('touchend', this.stopDrag);
    pointer(this.boxXY.get()).start(this.boxXY);
  };

  stopDrag = () => {
    spring({
      from: this.boxXY.get(),
      to: 0,
      velocity: this.boxXY.getVelocity(),
      stiffness: 100,
    }).start(this.boxXY)
    document.removeEventListener('mouseup', this.stopDrag);
    document.removeEventListener('touchend', this.stopDrag);
  };

  render() {
    return <Box onMouseDown={this.startDrag} onTouchStart={this.startDrag} innerRef={this.setRef} />
  }
}

export class Multitouch extends React.Component {
  componentDidMount() {
    document.addEventListener('touchstart', () => {
      multitouch()
        .while(({ touches }) => touches.length > 1)
        .start(console.log)
    });
  }

  render() {
    return null;
  }
}
<|endoftext|>


<commit_before>// TODO Test whether a Meteor project of only server files (or only client files) ends up doing two passes too (if it does not, this will break)

var fs = Npm.require('fs');
var Fiber = Npm.require('fibers');
var Future = Npm.require('fibers/future');
var ts = Npm.require('ts-compiler');
//var storage = Npm.require('node-persist');

var fsStat = Future.wrap(fs.stat);
//storage.initSync();

// "this." allows for modTimes to survive across changes to compile-tsc.js (useful during tsc devel)
this.modTimesByArch = this.modTimesByArch || {};

var cachedContents = {};
var cachedErrorsByArch = {};

var tsInputPaths = [];
var fullPathsToCompileSteps = {};
var inputPathsToCompileSteps = {};

var tsErrorRegex = /(.*[.]ts)\((\d+),(\d)+\): (.+)/;
var placeholderFileName = "main.tsc_placeholder.ts";

Plugin.registerSourceHandler("ts", function (compileStep) {
  if (compileStep.inputPath !== placeholderFileName) {
    handleSourceFile(compileStep);
    return;
  }

  var hadMod = checkAgainstModTime(compileStep.arch);
  if (!hadMod) tsInputPaths = [];
  compile(compileStep, hadMod);
  compilationFinished(compileStep.arch);
});

// Save the file input path, and return back to Meteor
function handleSourceFile(compileStep) {
  // Ensures the placeholder file exists. If the placeholder file is absent, tsInputPaths would never get cleared.
  if (tsInputPaths.indexOf(compileStep.inputPath) != -1) {
    fs.writeFileSync(placeholderFileName, "");
    compileStep.error({message: "Missing required \"" + placeholderFileName + "\" file; it has been created (make sure to add it to your .gitignore). You may have to touch a .ts file to trigger another compilation."});
    return;
  }

  console.log(compileStep.arch, compileStep.inputPath);
  tsInputPaths.push(compileStep.inputPath);
  fullPathsToCompileSteps[compileStep._fullInputPath] = compileStep;
  inputPathsToCompileSteps[compileStep.inputPath] = compileStep;
}

function checkAgainstModTime(arch) {
  modTimesByArch[arch] = modTimesByArch[arch] || {};

  var hadModifications = false;
  tsInputPaths.forEach(function(path) {
    stats = fsStat(path).wait();
    if (typeof(modTimesByArch[arch][path]) === 'undefined' || modTimesByArch[arch][path].toString() !== stats.mtime.toString()) {
      console.log(path,"had mods");
      hadModifications = true;
    }

    modTimesByArch[arch][path] = stats.mtime;
  });

  return hadModifications;
}

function compile(placeholderCompileStep, hadModifications) {
  var arch = placeholderCompileStep.arch;
  cachedErrorsByArch[arch] = cachedErrorsByArch[arch] || [];

  if (tsInputPaths.length == 0) {
    return;
  }

  if (!hadModifications) {
    // Short-circuit via cache
    tsInputPaths.forEach(function(path) {
      var compileStep = inputPathsToCompileSteps[path];

      compileStep.addJavaScript({
        path: path + ".js",
        sourcePath: path,
        data: cachedContents[path]//storage.getItem(path)
      })
    });

    // Replay errors
    cachedErrorsByArch[arch].forEach(function(args) {
      recordError(args.err, placeholderCompileStep, args.errorNumber, arch, true);
    });

    return;
  }

  cachedErrorsByArch[arch] = [];
  var browser = arch === "browser";
  var errorCount = 0;

  console.log("\nCompiling TypeScript " + (browser ? "client" : "server") + " files...");

  // AFAICT, this is synchronous (and our callback can get called multiple times if there are errors)
  compileOptions = {
    'target': (browser ? 'ES3' : 'ES5'),
    'skipWrite': true,
    'removeComments': true
  };
  ts.compile(tsInputPaths, compileOptions, function(err, results) {
    if (err) {
      recordError(err, placeholderCompileStep, ++errorCount, arch, false);
      return;
    }

    results.forEach(function(res) {
      // res.name is the theoretically-generated js filename
      var tsFullPath = res.name.substr(0, res.name.length-2) + "ts";
      var compileStep = fullPathsToCompileSteps[tsFullPath];

      compileStep.addJavaScript({
        path: compileStep.inputPath + ".js",
        sourcePath: compileStep.inputPath,
        data: res.text
      });

//      storage.setItem(compileStep.inputPath, res.text);
      cachedContents[compileStep.inputPath] = res.text;
    });
  });
}

function recordError(err, placeholderCompileStep, errorNumber, arch, isFromCache) {
  if (!isFromCache) {
    cachedErrorsByArch[arch].push({err: err, errNumber: errorNumber});
  }

  if (match = tsErrorRegex.exec(err.toString())) {
    var compileStep = fullPathsToCompileSteps[match[1]];
    if (compileStep) {
      compileStep.error({
        message: match[4],
        sourcePath: match[1],
        line: match[2],
        column: match[3]
      });
      return;
    }
  }

  placeholderCompileStep.error({
    message: err.toString(),
    sourcePath: placeholderCompileStep.inputPath,
    line: errorNumber,
    column: 1
  });
}

function compilationFinished(arch) {
  tsInputPaths = [];
  fullPathsToCompileSteps = {};
  inputPathsToCompileSteps = {};
}
<commit_msg>CP of cleanup<commit_after>// TODO Test whether a Meteor project of only server files (or only client files) ends up doing two passes too (if it does not, this will break)

var fs = Npm.require('fs');
var Fiber = Npm.require('fibers');
var Future = Npm.require('fibers/future');
var ts = Npm.require('ts-compiler');
//var storage = Npm.require('node-persist');

var fsStat = Future.wrap(fs.stat);
//storage.initSync();

var archs = {};
function initArch(archName) {
  archs[archName] = {name: archName};
  resetCompilationScopedArch(archs[archName]);
}

var cachedPathContents = {};

var tsErrorRegex = /(.*[.]ts)\((\d+),(\d)+\): (.+)/;
var placeholderFileName = "main.tsc_placeholder.ts";

Plugin.registerSourceHandler("ts", function (compileStep) {
  if (typeof(archs[compileStep.archName]) === 'undefined') {
    initArch(compileStep.arch);
  }

  if (compileStep.inputPath !== placeholderFileName) {
    handleSourceFile(compileStep);
    return;
  }

  // Typically, Meteor hands us all of the files pertaining to an arch in clumps (i.e. all files for browser, then all files for os), so most compiles are only two phases (client and server).
  for (var archName in archs) {
    var arch = archs[archName];
    if (arch.compileSteps.length == 0) {
      continue;
    }

    var hadMod = checkAgainstModTime(arch);
    compile(arch, compileStep, hadMod);
    resetCompilationScopedArch(arch);
  }
});

// Save the file input path, and return back to Meteor
function handleSourceFile(compileStep) {
  var arch = archs[compileStep.arch];

  // This ensures the placeholder file exists. If the placeholder file is absent, we would have never triggered compilation, so the inputPaths would never have gotten cleared.
  if (arch.inputPaths.indexOf(compileStep.inputPath) != -1) {
    fs.writeFileSync(placeholderFileName, "");
    compileStep.error({message: "Missing required \"" + placeholderFileName + "\" file; it has been created (make sure to add it to your .gitignore). You may have to touch a .ts file to trigger another compilation."});
    return;
  }

  arch.inputPaths.push(compileStep.inputPath);
  arch.compileSteps.push(compileStep);
  arch.fullPathToCompileSteps[compileStep._fullInputPath] = compileStep;
}

function checkAgainstModTime(arch) {
  var hadModifications = false;
  arch.inputPaths.forEach(function(path) {
    stats = fsStat(path).wait();
    if (typeof(arch.modTimes[path]) === 'undefined' || arch.modTimes[path].toString() !== stats.mtime.toString()) {
      console.log(arch.name, path,"had mods");
      hadModifications = true;
    }

    arch.modTimes[path] = stats.mtime;
  });

  return hadModifications;
}

function compile(arch, placeholderCompileStep, hadModifications) {
  if (!hadModifications) {
    // Short-circuit via cache
    arch.compileSteps.forEach(function(compileStep) {
      compileStep.addJavaScript({
        path: compileStep.inputPath + ".js",
        sourcePath: compileStep.inputPath,
        data: cachedPathContents[path]//storage.getItem(path)
      })
    });

    // Replay errors
    arch.cachedErrorReplays.forEach(function(errReplay) {
      recordError(errReplay.err, placeholderCompileStep, errReplay.errorNumber, arch, true);
    });

    return;
  }

  console.log("\nCompiling TypeScript " + arch.name + " files...");

  // Clear cached errors since we're about to re-compile
  arch.cachedErrorReplays = [];
  var errorCount = 0;
  var compileOptions = {
    'target': (arch.name === 'browser' ? 'ES3' : 'ES5'),
    'skipWrite': true,
    'removeComments': true
  };

  // This is synchronous (and our callback will get called multiple times if there are errors)
  ts.compile(arch.inputPaths, compileOptions, function(err, results) {
    if (err) {
      // TODO
      recordError(err, placeholderCompileStep, ++errorCount, arch, false);
      if (typeof(results) === 'undefined') {
        return;
      }
    }

    results.forEach(function(res) {
      // res.name is the theoretically-generated js filename
      var tsFullPath = res.name.substr(0, res.name.length-2) + "ts";
      var compileStep = arch.fullPathToCompileSteps[tsFullPath];

      compileStep.addJavaScript({
        path: compileStep.inputPath + ".js",
        sourcePath: compileStep.inputPath,
        data: res.text
      });

//      storage.setItem(compileStep.inputPath, res.text);
      cachedPathContents[compileStep.inputPath] = res.text;
    });
  });
}

function recordError(err, placeholderCompileStep, errorNumber, arch, isFromCache) {
  if (!isFromCache) {
    arch.cachedErrorReplays.push({err: err, errNumber: errorNumber});
  }

  if (match = tsErrorRegex.exec(err.toString())) {
    var compileStep = arch.fullPathToCompileSteps[match[1]];
    if (compileStep) {
      compileStep.error({
        message: match[4],
        sourcePath: match[1],
        line: match[2],
        column: match[3]
      });
      return;
    }
  }

  placeholderCompileStep.error({
    message: err.toString(),
    sourcePath: placeholderCompileStep.inputPath,
    line: errorNumber,
    column: 1
  });
}

function resetCompilationScopedArch(arch) {
  arch.modTimes = {};
  arch.cachedErrorReplays = [];
  arch.inputPaths = [];
  arch.compileSteps = [];
  arch.fullPathToCompileSteps = {};
}
<|endoftext|>