<commit_before>import moment from 'moment';
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';
import wait from 'ember-test-helpers/wait';

const { RSVP, Service, Object:EmberObject } = Ember;
const { resolve } = RSVP;

let today = moment();

const mockEvents = [
  {
    name: 'Learn to Learn',
    startDate: today.format(),
    location: 'Room 123',
    sessionTypeTitle: 'Lecture',
    courseExternalId: 'C1',
    sessionDescription: 'Best Session For Sure',
    isBlanked: false,
    isPublished: true,
    isScheduled: false,
    learningMaterials: [
      {
        title: 'Citation LM',
        required: true,
        publicNote: 'This is cool.',
        citation: 'citationtext',
        type: 'citation'
      },
      {
        title: 'Link LM',
        required: false,
        link: 'http://myhost.com/url2',
        type: 'link'
      },
      {
        title: 'File LM',
        required: true,
        absoluteFileUri: 'http://myhost.com/url1',
        type: 'file',
        mimetype: 'pdf'
      },
    ],
  },
  {
    name: 'Finding the Point in Life',
    startDate: today.format(),
    location: 'Room 456',
    sessionTypeTitle: 'Independent Learning',
    isBlanked: false,
    isPublished: true,
    isScheduled: false,
    learningMaterials: [
      {
        title: 'Great Slides',
        required: true,
        absoluteFileUri: 'http://myhost.com/url1',
        type: 'file',
        mimetype: 'keynote'
      },
    ],
    instructors: [
      'Second Person',
      'First Person',
    ],
  },
  {
    name: 'Blank',
    isBlanked: true,
  },
  {
    name: 'Not Published',
    isBlanked: false,
    isPublished: false,
    isScheduled: false,
  },
  {
    name: 'Scheduled',
    isBlanked: false,
    isPublished: true,
    isScheduled: true,
  },
];
const userEventsMock = Service.extend({
  getEvents(){
    return new resolve(mockEvents);
  },
  getSessionForEvent() {
    return EmberObject.create({
      attireRequired: false,
      equipmentRequired: false,
      attendanceRequired: false,
    });
  }
});
let blankEventsMock = Service.extend({
  getEvents(){
    return new resolve([]);
  }
});

moduleForComponent('dashboard-week', 'Integration | Component | dashboard week', {
  integration: true,
});

const getTitle = function(){
  const startOfWeek = today.clone().day(0).hour(0).minute(0).second(0);
  const endOfWeek = today.clone().day(6).hour(23).minute(59).second(59);

  let expectedTitle;
  if (startOfWeek.month() != endOfWeek.month()) {
    const from = startOfWeek.format('MMMM D');
    const to = endOfWeek.format('MMMM D');
    expectedTitle = `${from} - ${to}`;
  } else {
    const from = startOfWeek.format('MMMM D');
    const to = endOfWeek.format('D');
    expectedTitle = `${from}-${to}`;
  }
  expectedTitle += ' Week at a Glance';

  return expectedTitle;
};

test('it renders with events', async function(assert) {
  assert.expect(24);
  this.register('service:user-events', userEventsMock);
  this.inject.service('user-events', { as: 'userEvents' });

  this.render(hbs`{{dashboard-week}}`);
  const title = 'h3';
  const allWeeks = '.weeklylink';
  const events = '.event';
  const firstEvent = `${events}:eq(0)`;
  const secondEvent = `${events}:eq(1)`;

  const firstEventTitle = `${firstEvent} .title`;
  const firstSessionType = `${firstEvent} .sessiontype`;
  const firstLocation = `${firstEvent} .location`;
  const firstDescription = `${firstEvent} .description`;
  const firstLearningMaterials = `${firstEvent} .learning-material`;
  const firstLm1 = `${firstLearningMaterials}:eq(0)`;
  const firstLm1TypeIcon = `${firstLm1} i.fa-paragraph`;
  const firstLm2 = `${firstLearningMaterials}:eq(1)`;
  const firstLm2Link = `${firstLm2} a`;
  const firstLm2TypeIcon = `${firstLm2} i.fa-link`;
  const firstLm3 = `${firstLearningMaterials}:eq(2)`;
  const firstLm3Link = `${firstLm3} a`;
  const firstLm3TypeIcon = `${firstLm3} i.fa-file-pdf-o`;

  const firstInstructors = `${firstEvent} .instructors`;

  const secondEventTitle = `${secondEvent} .title`;
  const secondSessionType = `${secondEvent} .sessiontype`;
  const secondLocation = `${secondEvent} .location`;
  const secondDescription = `${secondEvent} .description`;
  const secondLearningMaterials = `${secondEvent} .learning-material`;
  const secondLm1 = `${secondLearningMaterials}:eq(0)`;
  const secondLm1Link = `${secondLm1} a`;
  const secondLm1TypeIcon = `${secondLm1} i.fa-file-powerpoint-o`;

  const secondInstructors = `${secondEvent} .instructors`;

  await wait();

  const expectedTitle = getTitle();
  assert.equal(this.$(title).text().replace(/[\t\n\s]+/g, ""), expectedTitle.replace(/[\t\n\s]+/g, ""));
  assert.equal(this.$(allWeeks).text().trim(), 'All Weeks');
  assert.equal(this.$(events).length, 2, 'Blank events are not shown');

  assert.equal(this.$(firstEventTitle).text().trim(), 'Learn to Learn');
  assert.equal(this.$(firstSessionType).text().trim(), 'Lecture');
  assert.equal(this.$(firstLocation).text().trim(), '- Room 123');
  assert.equal(this.$(firstDescription).text().trim(), 'Best Session For Sure');
  assert.equal(this.$(firstLm1).text().replace(/[\t\n\s]+/g, ""), 'CitationLMcitationtext');
  assert.equal(this.$(firstLm1TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(firstLm2).text().trim(), 'Link LM');
  assert.equal(this.$(firstLm2Link).attr('href'), 'http://myhost.com/url2');
  assert.equal(this.$(firstLm2TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(firstLm3).text().trim(), 'File LM');
  assert.equal(this.$(firstLm3Link).attr('href'), 'http://myhost.com/url1');
  assert.equal(this.$(firstLm3TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(firstInstructors).length, 0, 'No Instructors leaves and empty spot');

  assert.equal(this.$(secondEventTitle).text().trim(), 'Finding the Point in Life');
  assert.equal(this.$(secondSessionType).text().trim(), 'Independent Learning');
  assert.equal(this.$(secondLocation).text().trim(), '- Room 456');
  assert.equal(this.$(secondDescription).text().trim(), '', 'Emtpy Description is Empty');
  assert.equal(this.$(secondLm1).text().trim(), 'Great Slides');
  assert.equal(this.$(secondLm1Link).attr('href'), 'http://myhost.com/url1');
  assert.equal(this.$(secondLm1TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(secondInstructors).text().replace(/[\t\n\s]+/g, ""), 'Instructors:FirstPerson,SecondPerson', 'Instructors sorted and formated correctly');
});

test('it renders blank', async function(assert) {
  assert.expect(2);
  this.register('service:user-events', blankEventsMock);
  this.inject.service('user-events', { as: 'userEvents' });

  this.render(hbs`{{dashboard-week}}`);
  const title = 'h3';
  const body = 'p';
  const expectedTitle = getTitle();

  await wait();

  assert.equal(this.$(title).text().replace(/[\t\n\s]+/g, ""), expectedTitle.replace(/[\t\n\s]+/g, ""));
  assert.equal(this.$(body).text().trim(), 'None');

});
<commit_msg>updated tests.<commit_after>import moment from 'moment';
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';
import wait from 'ember-test-helpers/wait';

const { RSVP, Service, Object:EmberObject } = Ember;
const { resolve } = RSVP;

let today = moment();

const mockEvents = [
  {
    name: 'Learn to Learn',
    startDate: today.format(),
    location: 'Room 123',
    sessionTypeTitle: 'Lecture',
    courseExternalId: 'C1',
    sessionDescription: 'Best Session For Sure',
    isBlanked: false,
    isPublished: true,
    isScheduled: false,
    learningMaterials: [
      {
        title: 'Citation LM',
        required: true,
        publicNote: 'This is cool.',
        citation: 'citationtext',
        type: 'citation'
      },
      {
        title: 'Link LM',
        required: false,
        link: 'http://myhost.com/url2',
        type: 'link'
      },
      {
        title: 'File LM',
        required: true,
        absoluteFileUri: 'http://myhost.com/url1',
        type: 'file',
        mimetype: 'pdf'
      },
    ],
  },
  {
    name: 'Finding the Point in Life',
    startDate: today.format(),
    location: 'Room 456',
    sessionTypeTitle: 'Independent Learning',
    isBlanked: false,
    isPublished: true,
    isScheduled: false,
    learningMaterials: [
      {
        title: 'Great Slides',
        required: true,
        absoluteFileUri: 'http://myhost.com/url1',
        type: 'file',
        mimetype: 'keynote'
      },
    ],
    instructors: [
      'Second Person',
      'First Person',
    ],
  },
  {
    name: 'Blank',
    isBlanked: true,
  },
  {
    name: 'Not Published',
    isBlanked: false,
    isPublished: false,
    isScheduled: false,
  },
  {
    name: 'Scheduled',
    isBlanked: false,
    isPublished: true,
    isScheduled: true,
  },
];
const userEventsMock = Service.extend({
  getEvents(){
    return new resolve(mockEvents);
  },
  getSessionForEvent() {
    return EmberObject.create({
      attireRequired: false,
      equipmentRequired: false,
      attendanceRequired: false,
    });
  }
});
let blankEventsMock = Service.extend({
  getEvents(){
    return new resolve([]);
  }
});

moduleForComponent('dashboard-week', 'Integration | Component | dashboard week', {
  integration: true,
});

const getTitle = function(){
  const startOfWeek = today.clone().day(0).hour(0).minute(0).second(0);
  const endOfWeek = today.clone().day(6).hour(23).minute(59).second(59);

  let expectedTitle;
  if (startOfWeek.month() != endOfWeek.month()) {
    const from = startOfWeek.format('MMMM D');
    const to = endOfWeek.format('MMMM D');
    expectedTitle = `${from} - ${to}`;
  } else {
    const from = startOfWeek.format('MMMM D');
    const to = endOfWeek.format('D');
    expectedTitle = `${from}-${to}`;
  }
  expectedTitle += ' Week at a Glance';

  return expectedTitle;
};

test('it renders with events', async function(assert) {
  assert.expect(24);
  this.register('service:user-events', userEventsMock);
  this.inject.service('user-events', { as: 'userEvents' });

  this.render(hbs`{{dashboard-week}}`);
  const title = 'h3';
  const allWeeks = '.weeklylink a';
  const events = '.event';
  const firstEvent = `${events}:eq(0)`;
  const secondEvent = `${events}:eq(1)`;

  const firstEventTitle = `${firstEvent} .title`;
  const firstSessionType = `${firstEvent} .sessiontype`;
  const firstLocation = `${firstEvent} .location`;
  const firstDescription = `${firstEvent} .description`;
  const firstLearningMaterials = `${firstEvent} .learning-material`;
  const firstLm1 = `${firstLearningMaterials}:eq(0)`;
  const firstLm1TypeIcon = `${firstLm1} i.fa-paragraph`;
  const firstLm2 = `${firstLearningMaterials}:eq(1)`;
  const firstLm2Link = `${firstLm2} a`;
  const firstLm2TypeIcon = `${firstLm2} i.fa-link`;
  const firstLm3 = `${firstLearningMaterials}:eq(2)`;
  const firstLm3Link = `${firstLm3} a`;
  const firstLm3TypeIcon = `${firstLm3} i.fa-file-pdf-o`;

  const firstInstructors = `${firstEvent} .instructors`;

  const secondEventTitle = `${secondEvent} .title`;
  const secondSessionType = `${secondEvent} .sessiontype`;
  const secondLocation = `${secondEvent} .location`;
  const secondDescription = `${secondEvent} .description`;
  const secondLearningMaterials = `${secondEvent} .learning-material`;
  const secondLm1 = `${secondLearningMaterials}:eq(0)`;
  const secondLm1Link = `${secondLm1} a`;
  const secondLm1TypeIcon = `${secondLm1} i.fa-file-powerpoint-o`;

  const secondInstructors = `${secondEvent} .instructors`;

  await wait();

  const expectedTitle = getTitle();
  assert.equal(this.$(title).text().replace(/[\t\n\s]+/g, ""), expectedTitle.replace(/[\t\n\s]+/g, ""));
  assert.equal(this.$(allWeeks).text().trim(), 'Last Week / Next Week / All Weeks');
  assert.equal(this.$(events).length, 2, 'Blank events are not shown');

  assert.equal(this.$(firstEventTitle).text().trim(), 'Learn to Learn');
  assert.equal(this.$(firstSessionType).text().trim(), 'Lecture');
  assert.equal(this.$(firstLocation).text().trim(), '- Room 123');
  assert.equal(this.$(firstDescription).text().trim(), 'Best Session For Sure');
  assert.equal(this.$(firstLm1).text().replace(/[\t\n\s]+/g, ""), 'CitationLMcitationtext');
  assert.equal(this.$(firstLm1TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(firstLm2).text().trim(), 'Link LM');
  assert.equal(this.$(firstLm2Link).attr('href'), 'http://myhost.com/url2');
  assert.equal(this.$(firstLm2TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(firstLm3).text().trim(), 'File LM');
  assert.equal(this.$(firstLm3Link).attr('href'), 'http://myhost.com/url1');
  assert.equal(this.$(firstLm3TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(firstInstructors).length, 0, 'No Instructors leaves and empty spot');

  assert.equal(this.$(secondEventTitle).text().trim(), 'Finding the Point in Life');
  assert.equal(this.$(secondSessionType).text().trim(), 'Independent Learning');
  assert.equal(this.$(secondLocation).text().trim(), '- Room 456');
  assert.equal(this.$(secondDescription).text().trim(), '', 'Emtpy Description is Empty');
  assert.equal(this.$(secondLm1).text().trim(), 'Great Slides');
  assert.equal(this.$(secondLm1Link).attr('href'), 'http://myhost.com/url1');
  assert.equal(this.$(secondLm1TypeIcon).length, 1, 'LM type icon is present');
  assert.equal(this.$(secondInstructors).text().replace(/[\t\n\s]+/g, ""), 'Instructors:FirstPerson,SecondPerson', 'Instructors sorted and formated correctly');
});

test('it renders blank', async function(assert) {
  assert.expect(2);
  this.register('service:user-events', blankEventsMock);
  this.inject.service('user-events', { as: 'userEvents' });

  this.render(hbs`{{dashboard-week}}`);
  const title = 'h3';
  const body = 'p';
  const expectedTitle = getTitle();

  await wait();

  assert.equal(this.$(title).text().replace(/[\t\n\s]+/g, ""), expectedTitle.replace(/[\t\n\s]+/g, ""));
  assert.equal(this.$(body).text().trim(), 'None');

});
<|endoftext|>


<commit_before>// UCU - The Unending Coil Of Bahamut (Ultimate)
[{
  // TODO: Zone name is empty string for now lol?
  zoneRegex: /(The Unending Coil Of Bahamut \(Ultimate\)|^$)/,
  triggers: [
    { id: 'UCU Twisters',
      regex: /:26AA:Twintania starts using/,
      alertText: function(data) {
        return 'Twisters';
      },
    },
    { id: 'UCU Death Sentence',
      regex: /:Twintania readies Death Sentence/,
      alertText: function(data, matches) {
        if (data.role == 'tank' || data.role == 'healer')
          return 'Death Sentence';
      },
    },
    { id: 'UCU Fireball Marker',
      regex: /1B:........:(\y{Name}):....:....:0075:0000:0000:0000:/,
      infoText: function(data, matches) {
        if (data.me != matches[1])
          return 'fireball on ' + matches[1];
      },
      alertText: function(data, matches) {
        if (data.me == matches[1])
          return 'fireball on YOU';
      },
    },
    { id: 'UCU Hatch Marker',
      regex: /1B:........:(\y{Name}):....:....:0076:0000:0000:0000:/,
      infoText: function(data, matches) {
        if (data.me != matches[1])
          return 'hatch on ' + matches[1];
      },
      alarmText: function(data, matches) {
        if (data.me == matches[1])
          return 'hatch on YOU';
      },
    },
    { id: 'UCU Twintania P2',
      regex: /:Twintania HP at 74%/,
      infoText: function(data, matches) {
        return "Phase 2 Push";
      },
    },
    { id: 'UCU Twintania P3',
      regex: /:Twintania HP at 44%/,
      infoText: function(data, matches) {
        return "Phase 3 Push";
      },
    },
  ]
}]
<commit_msg>Use 'Long' sound for phase changes.<commit_after>// UCU - The Unending Coil Of Bahamut (Ultimate)
[{
  // TODO: Zone name is empty string for now lol?
  zoneRegex: /(The Unending Coil Of Bahamut \(Ultimate\)|^$)/,
  triggers: [
    { id: 'UCU Twisters',
      regex: /:26AA:Twintania starts using/,
      alertText: function(data) {
        return 'Twisters';
      },
    },
    { id: 'UCU Death Sentence',
      regex: /:Twintania readies Death Sentence/,
      alertText: function(data, matches) {
        if (data.role == 'tank' || data.role == 'healer')
          return 'Death Sentence';
      },
    },
    { id: 'UCU Fireball Marker',
      regex: /1B:........:(\y{Name}):....:....:0075:0000:0000:0000:/,
      infoText: function(data, matches) {
        if (data.me != matches[1])
          return 'fireball on ' + matches[1];
      },
      alertText: function(data, matches) {
        if (data.me == matches[1])
          return 'fireball on YOU';
      },
    },
    { id: 'UCU Hatch Marker',
      regex: /1B:........:(\y{Name}):....:....:0076:0000:0000:0000:/,
      infoText: function(data, matches) {
        if (data.me != matches[1])
          return 'hatch on ' + matches[1];
      },
      alarmText: function(data, matches) {
        if (data.me == matches[1])
          return 'hatch on YOU';
      },
    },
    { id: 'UCU Twintania P2',
      regex: /:Twintania HP at 74%/,
      sound: 'Long',
      infoText: function(data, matches) {
        return "Phase 2 Push";
      },
    },
    { id: 'UCU Twintania P3',
      regex: /:Twintania HP at 44%/,
      sound: 'Long',
      infoText: function(data, matches) {
        return "Phase 3 Push";
      },
    },
  ]
}]
<|endoftext|>


<commit_before>/*
 d3.phylogram.js
 Wrapper around a d3-based phylogram (tree where branch lengths are scaled)
 Also includes a radial dendrogram visualization (branch lengths not scaled)
 along with some helper methods for building angled-branch trees.

 Copyright (c) 2013, Ken-ichi Ueda

 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer. Redistributions in binary
 form must reproduce the above copyright notice, this list of conditions and
 the following disclaimer in the documentation and/or other materials
 provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

 DOCUEMENTATION

 d3.phylogram.build(selector, nodes, options)
 Creates a phylogram.
 Arguments:
 selector: selector of an element that will contain the SVG
 nodes: JS object of nodes
 Options:
 width
 Width of the vis, will attempt to set a default based on the width of
 the container.
 height
 Height of the vis, will attempt to set a default based on the height
 of the container.
 vis
 Pre-constructed d3 vis.
 tree
 Pre-constructed d3 tree layout.
 children
 Function for retrieving an array of children given a node. Default is
 to assume each node has an attribute called "branchset"
 diagonal
 Function that creates the d attribute for an svg:path. Defaults to a
 right-angle diagonal.
 skipTicks
 Skip the tick rule.
 skipBranchLengthScaling
 Make a dendrogram instead of a phylogram.

 d3.phylogram.buildRadial(selector, nodes, options)
 Creates a radial dendrogram.
 Options: same as build, but without diagonal, skipTicks, and
 skipBranchLengthScaling

 d3.phylogram.rightAngleDiagonal()
 Similar to d3.diagonal except it create an orthogonal crook instead of a
 smooth Bezier curve.

 d3.phylogram.radialRightAngleDiagonal()
 d3.phylogram.rightAngleDiagonal for radial layouts.
 */

if (!d3) { throw "d3 wasn't included!"};
(function() {
    d3.phylogram = {}
    d3.phylogram.rightAngleDiagonal = function() {
        var projection = function(d) { return [d.y, d.x]; }

        var path = function(pathData) {
            return "M" + pathData[0] + ' ' + pathData[1] + " " + pathData[2];
        }

        function diagonal(diagonalPath, i) {
            var source = diagonalPath.source,
                target = diagonalPath.target,
                midpointX = (source.x + target.x) / 2,
                midpointY = (source.y + target.y) / 2,
                pathData = [source, {x: target.x, y: source.y}, target];
            pathData = pathData.map(projection);
            return path(pathData)
        }

        diagonal.projection = function(x) {
            if (!arguments.length) return projection;
            projection = x;
            return diagonal;
        };

        diagonal.path = function(x) {
            if (!arguments.length) return path;
            path = x;
            return diagonal;
        };

        return diagonal;
    }

    d3.phylogram.styleTreeNodes = function(vis) {
        vis.selectAll('g.leaf.node')
            .append("svg:circle")
            .attr("r", 4.5)
            .attr('stroke',  'yellowGreen')
            .attr('fill', 'greenYellow')
            .attr('stroke-width', '2px');

        //vis.selectAll('g.root.node')
        //    .append('svg:circle')
        //    .attr("r", 4.5)
        //    .attr('fill', 'steelblue')
        //    .attr('stroke', '#369')
        //    .attr('stroke-width', '2px');
    }

    function scaleBranchLengths(nodes, w) {
        // Visit all nodes and adjust y pos width distance metric
        var visitPreOrder = function(root, callback) {
            callback(root)
            if (root.children) {
                for (var i = root.children.length - 1; i >= 0; i--){
                    visitPreOrder(root.children[i], callback)
                }
            }
        }
        visitPreOrder(nodes[0], function(node) {
            node.rootDist = (node.parent ? node.parent.rootDist : 0) + (node.length || 0)
        })
        var rootDists = nodes.map(function(n) { return n.rootDist; });
        var yscale = d3.scale.linear()
            .domain([0, d3.max(rootDists)])
            .range([0, w]);
        visitPreOrder(nodes[0], function(node) {
            node.y = yscale(node.rootDist)
        })
        return yscale
    }

    d3.phylogram.build = function(selector, nodes, options) {
        options = options || {}
        var w = options.width || d3.select(selector).style('width') || d3.select(selector).attr('width'),
            h = options.height || d3.select(selector).style('height') || d3.select(selector).attr('height'),
            w = parseInt(w),
            h = parseInt(h);
        var tree = options.tree || d3.layout.cluster()
                .size([h, w])
                .sort(function(node) { return node.children ? node.children.length : -1; })
                .children(options.children || function(node) {
                    return node.branchset
                });
        var diagonal = options.diagonal || d3.phylogram.rightAngleDiagonal();
        var vis = options.vis || d3.select(selector).append("svg:svg")
                .attr("width", w + 300)
                .attr("height", h + 30)
                .append("svg:g")
                .attr("transform", "translate(20, 20)");
        var nodes = tree(nodes);

        if (options.skipBranchLengthScaling) {
            var yscale = d3.scale.linear()
                .domain([0, w])
                .range([0, w]);
        } else {
            var yscale = scaleBranchLengths(nodes, w)
        }

        if (!options.skipTicks) {
            vis.selectAll('line')
                .data(yscale.ticks(10))
                .enter().append('svg:line')
                .attr('y1', 0)
                .attr('y2', h)
                .attr('x1', yscale)
                .attr('x2', yscale)
                .attr("stroke", "#ddd");

            vis.selectAll("text.rule")
                .data(yscale.ticks(10))
                .enter().append("svg:text")
                .attr("class", "rule")
                .attr("x", yscale)
                .attr("y", 0)
                .attr("dy", -3)
                .attr("text-anchor", "middle")
                .attr('font-size', '8px')
                .attr('fill', '#ccc')
                .text(function(d) { return Math.round(d*100) / 100; });
        }

        var link = vis.selectAll("path.link")
            .data(tree.links(nodes))
            .enter().append("svg:path")
            .attr("class", "link")
            .attr("d", diagonal)
            .attr("fill", "none")
            .attr("stroke", "#aaa")
            .attr("stroke-width", "4px");

        var node = vis.selectAll("g.node")
            .data(nodes)
            .enter().append("svg:g")
            .attr("class", function(n) {
                if (n.children) {
                    if (n.depth == 0) {
                        return "root node"
                    } else {
                        return "inner node"
                    }
                } else {
                    return "leaf node"
                }
            })
            .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

        d3.phylogram.styleTreeNodes(vis)

        if (!options.skipLabels) {
            //vis.selectAll('g.inner.node')
            //    .append("svg:text")
            //    .attr("dx", -6)
            //    .attr("dy", -6)
            //    .attr("text-anchor", 'end')
            //    .attr('font-size', '8px')
            //    .attr('fill', '#ccc')
            //    .text(function(d) { return d.length; });

            vis.selectAll('g.leaf.node')
                .append("svg:text")
                .attr("dx", 8)
                .attr("dy", 3)
                .attr("text-anchor", "start")
                .attr('font-family', 'Helvetica Neue, Helvetica, sans-serif')
                .attr('font-size', '12px')
                .attr('fill', function(d) { return d.color ? d.color: 'black'; })
                .text(function(d) { return d.name /*+ ' ('+d.length+')' */; });

            vis.selectAll('g.node')
                .append('svg:text')
                .attr("dx", -6)
                .attr("dy", -6)
                .attr("text-anchor", 'end')
                .attr('font-size', '12px')
                .attr('fill', '#333')
                .text(function(d) { return d.b; });
        }

        return {tree: tree, vis: vis}
    }
}());<commit_msg>Applied Terry's color changes<commit_after>/*
 d3.phylogram.js
 Wrapper around a d3-based phylogram (tree where branch lengths are scaled)
 Also includes a radial dendrogram visualization (branch lengths not scaled)
 along with some helper methods for building angled-branch trees.

 Copyright (c) 2013, Ken-ichi Ueda

 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer. Redistributions in binary
 form must reproduce the above copyright notice, this list of conditions and
 the following disclaimer in the documentation and/or other materials
 provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

 DOCUEMENTATION

 d3.phylogram.build(selector, nodes, options)
 Creates a phylogram.
 Arguments:
 selector: selector of an element that will contain the SVG
 nodes: JS object of nodes
 Options:
 width
 Width of the vis, will attempt to set a default based on the width of
 the container.
 height
 Height of the vis, will attempt to set a default based on the height
 of the container.
 vis
 Pre-constructed d3 vis.
 tree
 Pre-constructed d3 tree layout.
 children
 Function for retrieving an array of children given a node. Default is
 to assume each node has an attribute called "branchset"
 diagonal
 Function that creates the d attribute for an svg:path. Defaults to a
 right-angle diagonal.
 skipTicks
 Skip the tick rule.
 skipBranchLengthScaling
 Make a dendrogram instead of a phylogram.

 d3.phylogram.buildRadial(selector, nodes, options)
 Creates a radial dendrogram.
 Options: same as build, but without diagonal, skipTicks, and
 skipBranchLengthScaling

 d3.phylogram.rightAngleDiagonal()
 Similar to d3.diagonal except it create an orthogonal crook instead of a
 smooth Bezier curve.

 d3.phylogram.radialRightAngleDiagonal()
 d3.phylogram.rightAngleDiagonal for radial layouts.
 */

if (!d3) { throw "d3 wasn't included!"};
(function() {
    d3.phylogram = {}
    d3.phylogram.rightAngleDiagonal = function() {
        var projection = function(d) { return [d.y, d.x]; }

        var path = function(pathData) {
            return "M" + pathData[0] + ' ' + pathData[1] + " " + pathData[2];
        }

        function diagonal(diagonalPath, i) {
            var source = diagonalPath.source,
                target = diagonalPath.target,
                midpointX = (source.x + target.x) / 2,
                midpointY = (source.y + target.y) / 2,
                pathData = [source, {x: target.x, y: source.y}, target];
            pathData = pathData.map(projection);
            return path(pathData)
        }

        diagonal.projection = function(x) {
            if (!arguments.length) return projection;
            projection = x;
            return diagonal;
        };

        diagonal.path = function(x) {
            if (!arguments.length) return path;
            path = x;
            return diagonal;
        };

        return diagonal;
    }

    d3.phylogram.styleTreeNodes = function(vis) {
        vis.selectAll('g.leaf.node')
            .append("svg:circle")
            .attr("r", 0.1)
            .attr('stroke',  'yellowGreen')
            .attr('fill', 'greenYellow')
            .attr('stroke-width', '2px');

        //vis.selectAll('g.root.node')
        //    .append('svg:circle')
        //    .attr("r", 4.5)
        //    .attr('fill', 'steelblue')
        //    .attr('stroke', '#369')
        //    .attr('stroke-width', '2px');
    }

    function scaleBranchLengths(nodes, w) {
        // Visit all nodes and adjust y pos width distance metric
        var visitPreOrder = function(root, callback) {
            callback(root)
            if (root.children) {
                for (var i = root.children.length - 1; i >= 0; i--){
                    visitPreOrder(root.children[i], callback)
                }
            }
        }
        visitPreOrder(nodes[0], function(node) {
            node.rootDist = (node.parent ? node.parent.rootDist : 0) + (node.length || 0)
        })
        var rootDists = nodes.map(function(n) { return n.rootDist; });
        var yscale = d3.scale.linear()
            .domain([0, d3.max(rootDists)])
            .range([0, w]);
        visitPreOrder(nodes[0], function(node) {
            node.y = yscale(node.rootDist)
        })
        return yscale
    }

    d3.phylogram.build = function(selector, nodes, options) {
        options = options || {}
        var w = options.width || d3.select(selector).style('width') || d3.select(selector).attr('width'),
            h = options.height || d3.select(selector).style('height') || d3.select(selector).attr('height'),
            w = parseInt(w),
            h = parseInt(h);
        var tree = options.tree || d3.layout.cluster()
                .size([h, w])
                .sort(function(node) { return node.children ? node.children.length : -1; })
                .children(options.children || function(node) {
                    return node.branchset
                });
        var diagonal = options.diagonal || d3.phylogram.rightAngleDiagonal();
        var vis = options.vis || d3.select(selector).append("svg:svg")
                .attr("width", w + 300)
                .attr("height", h + 30)
                .append("svg:g")
                .attr("transform", "translate(20, 20)");
        var nodes = tree(nodes);

        if (options.skipBranchLengthScaling) {
            var yscale = d3.scale.linear()
                .domain([0, w])
                .range([0, w]);
        } else {
            var yscale = scaleBranchLengths(nodes, w)
        }

        if (!options.skipTicks) {
            vis.selectAll('line')
                .data(yscale.ticks(10))
                .enter().append('svg:line')
                .attr('y1', 0)
                .attr('y2', h)
                .attr('x1', yscale)
                .attr('x2', yscale)
                .attr("stroke", "#ddd");

            vis.selectAll("text.rule")
                .data(yscale.ticks(10))
                .enter().append("svg:text")
                .attr("class", "rule")
                .attr("x", yscale)
                .attr("y", 0)
                .attr("dy", -3)
                .attr("text-anchor", "middle")
                .attr('font-size', '8px')
                .attr('fill', '#ccc')
                .text(function(d) { return Math.round(d*100) / 100; });
        }

        var link = vis.selectAll("path.link")
            .data(tree.links(nodes))
            .enter().append("svg:path")
            .attr("class", "link")
            .attr("d", diagonal)
            .attr("fill", "none")
            .attr("stroke", "#000")
            .attr("stroke-width", "4px");

        var node = vis.selectAll("g.node")
            .data(nodes)
            .enter().append("svg:g")
            .attr("class", function(n) {
                if (n.children) {
                    if (n.depth == 0) {
                        return "root node"
                    } else {
                        return "inner node"
                    }
                } else {
                    return "leaf node"
                }
            })
            .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

        d3.phylogram.styleTreeNodes(vis)

        if (!options.skipLabels) {
            //vis.selectAll('g.inner.node')
            //    .append("svg:text")
            //    .attr("dx", -6)
            //    .attr("dy", -6)
            //    .attr("text-anchor", 'end')
            //    .attr('font-size', '8px')
            //    .attr('fill', '#ccc')
            //    .text(function(d) { return d.length; });

            vis.selectAll('g.leaf.node')
                .append("svg:text")
                .attr("dx", 8)
                .attr("dy", 3)
                .attr("text-anchor", "start")
                .attr('font-family', 'Helvetica Neue, Helvetica, sans-serif')
                .attr('font-size', '12px')
                .attr('fill', function(d) { return d.color ? d.color: 'black'; })
                .text(function(d) { return d.name /*+ ' ('+d.length+')' */; });

            vis.selectAll('g.node')
                .append('svg:text')
                .attr("dx", -6)
                .attr("dy", -6)
                .attr("text-anchor", 'end')
                .attr('font-size', '12px')
                .attr('fill', '#333')
                .text(function(d) { return d.b; });
        }

        return {tree: tree, vis: vis}
    }
}());<|endoftext|>


<commit_before>'use strict';

const ROUND_DURATION = 8 * 1000; //8 seconds

class Ambush {
	constructor(room, seconds) {
		this.players = new Map();
		this.round = 0;
		this.room = room;
		if (this.room.ambushCount) {
			this.room.ambushCount++;
		} else {
			this.room.ambushCount = 1;
		}
		this.timeLeft = Date.now() + seconds * 1000;

		this.room.add('|uhtml|ambush' + this.room.ambushCount + this.round + '|
A game of Ambush has been started!
' +
			'The game will begin in ' + seconds + ' seconds!
' +
			'<button name = "send" value = "/ambush join">Join!</button>
'
		);
		this.timer = setTimeout(() => {
			if (this.players.size < 3) {
				this.room.add('|uhtmlchange|ambush' + this.room.ambushCount + this.round + '|
This game of ambush has been ended due to the lack of players.
').update();
				return this.end();
			}
			this.nextRound();
		}, seconds * 1000);
	}
	updateJoins() {
		let msg = 'ambush' + this.room.ambushCount + this.round + '|
A game of Ambush has been started!
' +
			'The game will begin in ' + Math.round((this.timeLeft - Date.now()) / 1000) + ' seconds
' +
			'<button name = "send" value = "/ambush join">Join!</button>';
		if (this.players.size > 0) {
			msg += '' + this.players.size + ' ' + (this.players.size === 1 ? 'user has' : 'users have') + ' joined: ' + Array.from(this.players).map(player => Tools.escapeHTML(player[0].name)).join(', ') + '';
		}
		this.room.add('|uhtmlchange|' + msg + '
');
	}
	join(user, self) {
		if (!user.named) return self.errorReply("You must choose a name before joining a game of ambush.");
		if (this.players.has(user)) return self.sendReply('You have already joined this game of ambush.');
		if (this.round > 0) return self.sendReply('You cannot join a game of ambush after it has started.');

		this.players.set(user, {status:'alive', rounds:0});
		this.updateJoins();
	}
	leave(user, self) {
		if (!this.players.has(user)) return self.sendReply('You haven\'t joined this game of ambush yet.');

		this.players.delete(user);
		if (!this.round) {
			this.updateJoins();
		} else {
			this.room.add('|html|' + Tools.escapeHTML(user.name) + ' has left the game!');
		}
	}
	getSurvivors() {
		return Array.from(this.players).filter(player => player[1].status === 'alive');
	}
	nextRound() {
		clearTimeout(this.timer);
		this.canShoot = false;
		if (this.checkWinner()) return this.getWinner();
		let survivors = this.getSurvivors();
		if (this.lastRoundSurvivors === survivors.length) {
			this.room.add('|html|
This game of ambush has ended due to inactivity, with ' + survivors.length + ' survivors.
');
			return this.end();
		}
		this.lastRoundSurvivors = survivors.length;

		this.round++;
		this.loadGuns();
		let msg = 'ambush' + this.room.ambushCount + this.round + '|
Round ' + this.round + '
' +
			'Players: ' + survivors.map(player => Tools.escapeHTML(player[0].name)).join(', ') +
			'
Use /fire [player] to shoot another player!';
		this.room.add('|uhtml|' + msg + '
Wait for it...
').update();

		this.release = setTimeout(() => {
			this.room.add('|uhtmlchange|' + msg + '
FIRE!
').update();
			this.canShoot = true;
			this.resetTimer();
		}, (Math.floor(Math.random() * 12) + 3) * 1000);
	}
	fire(user, target, self) {
		let getUser = this.players.get(user);
		if (!getUser) return self.sendReply("You aren't a player in this game of Ambush.");
		this.madeMove = false;

		if (!this.canShoot) return self.sendReply("You're not allowed to open fire yet!");

		if (getUser.status === 'dead') return self.sendReply("You can't fire after you've been killed!");
		if (!getUser.rounds) return self.sendReply("You're out of rounds! You can't shoot anyone else!");

		let targetUser = Users(target);
		if (!targetUser) return self.sendReply('User ' + target + ' not found.');
		if (!this.players.has(targetUser)) return self.sendReply(targetUser.name + ' is not a player!');
		if (this.players.get(targetUser).status === 'dead') return self.sendReply(targetUser.name + ' has already been shot!');

		this.players.get(user).rounds--;
		this.madeMove = true;
		if (targetUser === user) {
			this.room.add('|html|' + user.name + ' shot themself!');
		} else if (!this.players.get(targetUser).rounds) {
			this.room.add('|html|' + Tools.escapeHTML(user.name) + ' fired at ' + Tools.escapeHTML(targetUser.name) + ', but ' + Tools.escapeHTML(targetUser.name) + ' has an active shield!');
			return;
		} else {
			this.room.add('|html|' + Tools.escapeHTML(user.name) + ' fired at ' + Tools.escapeHTML(targetUser.name) + '!');
		}
		this.players.get(targetUser).status = 'dead';

		if (this.checkWinner()) this.getWinner();
	}
	loadGuns() {
		this.players.forEach((details, user) => {
			if (this.players.get(user).status === 'alive') this.players.get(user).rounds = 1;
		});
	}
	resetTimer() {
		this.timer = setTimeout(() => {
			this.nextRound();
			this.room.update();
		}, ROUND_DURATION);
	}
	dq(target, self) {
		if (!this.round) return self.sendReply('You can only disqualify a player after the first round has begun.');
		let targetUser = Users(target);
		if (!targetUser) return self.sendReply('User ' + target + ' not found.');

		let getUser = this.players.get(targetUser);
		if (!getUser) return self.sendReply(targetUser.name + ' is not a player!');
		if (getUser.status === 'dead') return self.sendReply(targetUser.name + ' has already been killed!');

		this.players.delete(targetUser);
		this.room.add('|html|' + Tools.escapeHTML(targetUser.name) + ' has been disqualified from the game.');
		if (this.checkWinner()) this.getWinner();
	}
	checkWinner() {
		if (this.getSurvivors().length === 1) return true;
	}
	getWinner() {
		let winner = this.getSurvivors()[0][0].name;
		let msg = '|html|
The winner of this game of ambush is ' + Tools.escapeHTML(winner) + '! Congratulations!';
		{
			this.room.add(msg).update();
		}
		this.end();
	}
	end(user) {
		if (user) {
			let msg = '
This game of ambush has been forcibly ended by ' + Tools.escapeHTML(user.name) + '
';
			if (!this.madeMove) {
				this.room.add('|uhtmlchange|ambush' + this.room.ambushCount + this.round + '|' + msg).update();
			} else {
				this.room.add('|html|' + msg).update();
			}
		}
		if (this.release) clearTimeout(this.release);
		clearTimeout(this.timer);
		delete this.room.ambush;
	}
}
let commands = {
	'': 'new',
	'start': 'new',
	'begin': 'new',
	'new': function (target, room, user) {
		if (room.ambush) return this.sendReply("There is already a game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");
		if (!user.can('broadcast', null, room)) return this.sendReply("You must be ranked + or higher in this room to start a game of ambush.");

		if (!target || !target.trim()) target = '60';
		if (isNaN(target)) return this.sendReply('\'' + target + '\' is not a valid number.');
		if (target.includes('.') || target > 180 || target < 10) return this.sendReply('The number of seconds needs to be a non-decimal number between 10 and 180.');

		room.ambush = new Ambush(room, Number(target));
	},
	join: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");

		room.ambush.join(user, this);
	},
	leave: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");

		room.ambush.leave(user, this);
	},
	proceed: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");
		if (!user.can('broadcast', null, room)) return this.sendReply("You must be ranked + or higher in this room to forcibly begin the first round of a game of ambush.");

		if (room.ambush.round) return this.sendReply('This game of ambush has already begun!');
		if (room.ambush.players.size < 3) return this.sendReply('There aren\'t enough players yet. Wait for more to join!');
		room.add('(' + user.name + ' forcibly started round 1)');
		room.ambush.nextRound();
	},
	disqualify: 'dq',
	dq: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");
		if (!user.can('mute', null, room)) return this.sendReply("You must be ranked % or higher in this room to disqualify a user from a game of ambush.");

		room.ambush.dq(target, this);
	},
	shoot: 'fire',
	fire: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");

		room.ambush.fire(user, target, this);
	},
	cancel: 'end',
	end: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (!user.can('mute', null, room)) return this.sendReply("You must be ranked % or higher in this room to end a game of ambush.");

		room.ambush.end(user);
	},
	help: function () {
		this.parse('/help ambush');
	},
};
exports.commands = {
	ambush: commands,
	shoot: commands.fire,
	ambushhelp: [
		'/ambush start [seconds] - Starts a game of ambush in the room. The first round will begin after the mentioned number of seconds (1 minute by default). Requires + or higher to use.',
		'/ambush join/leave - Joins/Leaves a game of ambush.',
		'/ambush proceed - Forcibly starts the first round of the game. Requires + or higher to use',
		'/ambush dq [user] - Disqualifies a player from a game of ambush. Requires % or higher to use',
		'/ambush shoot [user] - Shoots another player (you can shoot yourself too)',
		'/ambush end - Forcibly ends a game of ambush. Requires % or higher to use.',
		'/ambush rules - Displays the rules of the game.',
	],
};
<commit_msg>Update ambush.js<commit_after>'use strict';

const ROUND_DURATION = 8 * 1000; //8 seconds

class Ambush {
	constructor(room, seconds) {
		this.players = new Map();
		this.round = 0;
		this.room = room;
		if (this.room.ambushCount) {
			this.room.ambushCount++;
		} else {
			this.room.ambushCount = 1;
		}
		this.timeLeft = Date.now() + seconds * 1000;

		this.room.add('|uhtml|ambush' + this.room.ambushCount + this.round + '|
A game of Ambush has been started!
' +
			'The game will begin in ' + seconds + ' seconds!
' +
			'<button name = "send" value = "/ambush join">Join!</button>
'
		);
		this.timer = setTimeout(() => {
			if (this.players.size < 3) {
				this.room.add('|uhtmlchange|ambush' + this.room.ambushCount + this.round + '|
This game of ambush has been ended due to the lack of players.
').update();
				return this.end();
			}
			this.nextRound();
		}, seconds * 1000);
	}
	updateJoins() {
		let msg = 'ambush' + this.room.ambushCount + this.round + '|
A game of Ambush has been started!
' +
			'The game will begin in ' + Math.round((this.timeLeft - Date.now()) / 1000) + ' seconds
' +
			'<button name = "send" value = "/ambush join">Join!</button>';
		if (this.players.size > 0) {
			msg += '' + this.players.size + ' ' + (this.players.size === 1 ? 'user has' : 'users have') + ' joined: ' + Array.from(this.players).map(player => Tools.escapeHTML(player[0].name)).join(', ') + '';
		}
		this.room.add('|uhtmlchange|' + msg + '
');
	}
	join(user, self) {
		if (!user.named) return self.errorReply("You must choose a name before joining a game of ambush.");
		if (this.players.has(user)) return self.sendReply('You have already joined this game of ambush.');
		if (this.round > 0) return self.sendReply('You cannot join a game of ambush after it has started.');

		this.players.set(user, {status:'alive', rounds:0});
		this.updateJoins();
	}
	leave(user, self) {
		if (!this.players.has(user)) return self.sendReply('You haven\'t joined this game of ambush yet.');

		this.players.delete(user);
		if (!this.round) {
			this.updateJoins();
		} else {
			this.room.add('|html|' + Tools.escapeHTML(user.name) + ' has left the game!');
		}
	}
	getSurvivors() {
		return Array.from(this.players).filter(player => player[1].status === 'alive');
	}
	nextRound() {
		clearTimeout(this.timer);
		this.canShoot = false;
		if (this.checkWinner()) return this.getWinner();
		let survivors = this.getSurvivors();
		if (this.lastRoundSurvivors === survivors.length) {
			this.room.add('|html|
This game of ambush has ended due to inactivity, with ' + survivors.length + ' survivors.
');
			return this.end();
		}
		this.lastRoundSurvivors = survivors.length;

		this.round++;
		this.loadGuns();
		let msg = 'ambush' + this.room.ambushCount + this.round + '|
Round ' + this.round + '
' +
			'Players: ' + survivors.map(player => Tools.escapeHTML(player[0].name)).join(', ') +
			'
Use /fire [player] to shoot another player!';
		this.room.add('|uhtml|' + msg + '
Wait for it...
').update();

		this.release = setTimeout(() => {
			this.room.add('|uhtmlchange|' + msg + '
FIRE!
').update();
			this.canShoot = true;
			this.resetTimer();
		}, (Math.floor(Math.random() * 12) + 3) * 1000);
	}
	fire(user, target, self) {
		let getUser = this.players.get(user);
		if (!getUser) return self.sendReply("You aren't a player in this game of Ambush.");
		this.madeMove = false;

		if (!this.canShoot) return self.sendReply("You're not allowed to open fire yet!");

		if (getUser.status === 'dead') return self.sendReply("You can't fire after you've been killed!");
		if (!getUser.rounds) return self.sendReply("You're out of rounds! You can't shoot anyone else!");

		let targetUser = Users(target);
		if (!targetUser) return self.sendReply('User ' + target + ' not found.');
		if (!this.players.has(targetUser)) return self.sendReply(targetUser.name + ' is not a player!');
		if (this.players.get(targetUser).status === 'dead') return self.sendReply(targetUser.name + ' has already been shot!');

		this.players.get(user).rounds--;
		this.madeMove = true;
		if (targetUser === user) {
			this.room.add('|html|' + user.name + ' shot themself!');
		} else if (!this.players.get(targetUser).rounds) {
			this.room.add('|html|' + Tools.escapeHTML(user.name) + ' fired at ' + Tools.escapeHTML(targetUser.name) + ', but ' + Tools.escapeHTML(targetUser.name) + ' has an active shield!');
			return;
		} else {
			this.room.add('|html|' + Tools.escapeHTML(user.name) + ' fired at ' + Tools.escapeHTML(targetUser.name) + '!');
		}
		this.players.get(targetUser).status = 'dead';

		if (this.checkWinner()) this.getWinner();
	}
	loadGuns() {
		this.players.forEach((details, user) => {
			if (this.players.get(user).status === 'alive') this.players.get(user).rounds = 1;
		});
	}
	resetTimer() {
		this.timer = setTimeout(() => {
			this.nextRound();
			this.room.update();
		}, ROUND_DURATION);
	}
	dq(target, self) {
		if (!this.round) return self.sendReply('You can only disqualify a player after the first round has begun.');
		let targetUser = Users(target);
		if (!targetUser) return self.sendReply('User ' + target + ' not found.');

		let getUser = this.players.get(targetUser);
		if (!getUser) return self.sendReply(targetUser.name + ' is not a player!');
		if (getUser.status === 'dead') return self.sendReply(targetUser.name + ' has already been killed!');

		this.players.delete(targetUser);
		this.room.add('|html|' + Tools.escapeHTML(targetUser.name) + ' has been disqualified from the game.');
		if (this.checkWinner()) this.getWinner();
	}
	checkWinner() {
		if (this.getSurvivors().length === 1) return true;
	}
	getWinner() {
		let winner = this.getSurvivors()[0][0].name;
		room.addRaw('|html|
The winner of this game of ambush is ' + Tools.escapeHTML(winner) + '! Congratulations!');
		if (this.room.id === 'lobby') {
			room.addRaw('' + Tools.escapeHTML(winner) + ' has also won 5 bucks for winning!');
			Db('money').set(user.userid, winner + 2).get(user.userid);
		this.end();
	}
	end(user) {
		if (user) {
			let msg = '
This game of ambush has been forcibly ended by ' + Tools.escapeHTML(user.name) + '
';
			if (!this.madeMove) {
				this.room.add('|uhtmlchange|ambush' + this.room.ambushCount + this.round + '|' + msg).update();
			} else {
				this.room.add('|html|' + msg).update();
			}
		}
		if (this.release) clearTimeout(this.release);
		clearTimeout(this.timer);
		delete this.room.ambush;
	}
}
let commands = {
	'': 'new',
	'start': 'new',
	'begin': 'new',
	'new': function (target, room, user) {
		if (room.ambush) return this.sendReply("There is already a game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");
		if (!user.can('broadcast', null, room)) return this.sendReply("You must be ranked + or higher in this room to start a game of ambush.");

		if (!target || !target.trim()) target = '60';
		if (isNaN(target)) return this.sendReply('\'' + target + '\' is not a valid number.');
		if (target.includes('.') || target > 180 || target < 10) return this.sendReply('The number of seconds needs to be a non-decimal number between 10 and 180.');

		room.ambush = new Ambush(room, Number(target));
	},
	join: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");

		room.ambush.join(user, this);
	},
	leave: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");

		room.ambush.leave(user, this);
	},
	proceed: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");
		if (!user.can('broadcast', null, room)) return this.sendReply("You must be ranked + or higher in this room to forcibly begin the first round of a game of ambush.");

		if (room.ambush.round) return this.sendReply('This game of ambush has already begun!');
		if (room.ambush.players.size < 3) return this.sendReply('There aren\'t enough players yet. Wait for more to join!');
		room.add('(' + user.name + ' forcibly started round 1)');
		room.ambush.nextRound();
	},
	disqualify: 'dq',
	dq: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");
		if (!user.can('mute', null, room)) return this.sendReply("You must be ranked % or higher in this room to disqualify a user from a game of ambush.");

		room.ambush.dq(target, this);
	},
	shoot: 'fire',
	fire: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (room.isMuted(user) || user.locked) return this.errorReply("You cannot use this while unable to speak.");

		room.ambush.fire(user, target, this);
	},
	cancel: 'end',
	end: function (target, room, user) {
		if (!room.ambush) return this.sendReply("There is no game of ambush going on in this room.");
		if (!user.can('mute', null, room)) return this.sendReply("You must be ranked % or higher in this room to end a game of ambush.");

		room.ambush.end(user);
	},
	help: function () {
		this.parse('/help ambush');
	},
};
exports.commands = {
	ambush: commands,
	fire: 'shoot',
	shoot: commands.fire,
	ambushhelp: [
		'/ambush start [seconds] - Starts a game of ambush in the room. The first round will begin after the mentioned number of seconds (1 minute by default). Requires + or higher to use.',
		'/ambush join/leave - Joins/Leaves a game of ambush.',
		'/ambush proceed - Forcibly starts the first round of the game. Requires + or higher to use',
		'/ambush dq [user] - Disqualifies a player from a game of ambush. Requires % or higher to use',
		'/ambush shoot/fire [user] - Shoots another player (you can shoot yourself too)',
		'/ambush end - Forcibly ends a game of ambush. Requires % or higher to use.',
		'/ambush rules - Displays the rules of the game.',
	],
};
<|endoftext|>


<commit_before>/**
 * API wrapper for Messenger chatbot
 */

'use strict';

const request = require('request');
/**
 * @type {{Promise:object}}
 */
const es6Promise = require('es6-promise');
const Promise = es6Promise.Promise;

function setTyping(token, idSender, action) {
	return new Promise(function(resolve, reject) {
		request({
			url: 'https://graph.facebook.com/v2.6/me/messages',
			qs: {access_token: token},
			method: 'POST',
			json: {
				recipient: {id: idSender},
				sender_action: action,
			}
		},
		function (error, response) {
			if (error) {
				console.log('Error setting typing: ', error);
				reject(error);
			}
			else {
				if (response.body.error) {
					console.error(__filename + ' - send Error: ', response.body.error);
					console.trace();
				}
				resolve(response);
			}
		})
	});
}

module.exports = function(token) {
	return {
		send: function(idSender, messageData) {
			return new Promise(function(resolve, reject) {
				console.log('Sending message ' + JSON.stringify(messageData));
				request({
					url: 'https://graph.facebook.com/v2.6/me/messages',
					qs: {access_token:token},
					method: 'POST',
					json: {
						recipient: {id:idSender},
						message: messageData,
					}
				},
				function(error, response) {
					if (error) {
						console.log('Error sending message: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.error(__filename + ' - send Error: ', response.body.error);
							console.trace();
						}
						console.log('Message sent ' + JSON.stringify(messageData));
						resolve(response);
					}
				})
			});
		},

		setTyping: function(idSender) {
			return setTyping(token, idSender, "typing_on");
		},
		unsetTyping: function(idSender) {
			return setTyping(token, idSender, "typing_off");
		},

		sendTextMessage: function sendTextMessage(idSender, text) {
			let messageData = { text:text };
			return this.send(idSender, messageData);
		},

		sendPicMessage: function sendPicMessage(idSender, url) {
			return this.send(idSender, {
				"attachment":{
					"type":"image",
					"payload":{
						"url":url
					}
				}
			});
		},

		setMenu: function setMenu(data) {
			return new Promise(function(resolve,reject) {
				request({
					url: 'https://graph.facebook.com/v2.6/me/thread_settings',
					qs: {access_token:token},
					method: 'POST',
					json: data
				},
				function(error, response) {
					if (error) {
						console.log('Error setting menu: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.log(__filename + ' - setMenu Error: ', response.body.error)
						}
						resolve(response);
					}
				})
			});
		},

		setStartButton: function setMenu(data) {
			return new Promise(function(resolve,reject) {
				request({
					url: 'https://graph.facebook.com/v2.6/me/thread_settings',
					qs: {access_token:token},
					method: 'POST',
					json: data
				},
				function(error, response) {
					if (error) {
						console.log('Error setting menu: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.log(__filename + ' - setStartButton Error: ', response.body.error)
						}
						resolve(response);
					}
				})
			});
		},

		//SET GREETING MESSAGE RAPH

		setGreetingMessage: function setGreetingMessage(data) {
			return new Promise(function (resolve, reject) {
				request({
					url: 'https://graph.facebook.com/v2.6/me/thread_settings',
					qs: {access_token: token},
					method: 'POST',
					json: {
						"setting_type": "greeting",
						"greeting": {
							"text": data
						}
					}
				},
				function (error, response) {
					if (error) {
						console.log('Error setting menu: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.log( __filename + ' - setGreetingMessage Error: ', response.body.error)
						}
						resolve(response);
					}
				})
			});
		},

		//END OF GREETING MESSAGE

	}
};

<commit_msg>Error from FB are now sent as a catch<commit_after>/**
 * API wrapper for Messenger chatbot
 */

'use strict';

const request = require('request');
/**
 * @type {{Promise:object}}
 */
const es6Promise = require('es6-promise');
const Promise = es6Promise.Promise;

function setTyping(token, idSender, action) {
	return new Promise(function(resolve, reject) {
		request({
			url: 'https://graph.facebook.com/v2.6/me/messages',
			qs: {access_token: token},
			method: 'POST',
			json: {
				recipient: {id: idSender},
				sender_action: action,
			}
		},
		function (error, response) {
			if (error) {
				console.log('Error setting typing: ', error);
				reject(error);
			}
			else {
				if (response.body.error) {
					console.error(__filename + ' - send Error: ', response.body.error);
					console.trace();
				}
				resolve(response);
			}
		})
	});
}

module.exports = function(token) {
	return {
		send: function(idSender, messageData) {
			return new Promise(function(resolve, reject) {
				console.log('Sending message ' + JSON.stringify(messageData));
				request({
					url: 'https://graph.facebook.com/v2.6/me/messages',
					qs: {access_token:token},
					method: 'POST',
					json: {
						recipient: {id:idSender},
						message: messageData,
					}
				},
				function(error, response) {
					if (error) {
						console.log('Error sending message: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.error(__filename + ' - send Error: ', response.body.error);
							reject(new Error(response.body.error));
						}
						console.log('Message sent ' + JSON.stringify(messageData));
						resolve(response);
					}
				})
			});
		},

		setTyping: function(idSender) {
			return setTyping(token, idSender, "typing_on");
		},
		unsetTyping: function(idSender) {
			return setTyping(token, idSender, "typing_off");
		},

		sendTextMessage: function sendTextMessage(idSender, text) {
			let messageData = { text:text };
			return this.send(idSender, messageData);
		},

		sendPicMessage: function sendPicMessage(idSender, url) {
			return this.send(idSender, {
				"attachment":{
					"type":"image",
					"payload":{
						"url":url
					}
				}
			});
		},

		setMenu: function setMenu(data) {
			return new Promise(function(resolve,reject) {
				request({
					url: 'https://graph.facebook.com/v2.6/me/thread_settings',
					qs: {access_token:token},
					method: 'POST',
					json: data
				},
				function(error, response) {
					if (error) {
						console.log('Error setting menu: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.log(__filename + ' - setMenu Error: ', response.body.error)
						}
						resolve(response);
					}
				})
			});
		},

		setStartButton: function setMenu(data) {
			return new Promise(function(resolve,reject) {
				request({
					url: 'https://graph.facebook.com/v2.6/me/thread_settings',
					qs: {access_token:token},
					method: 'POST',
					json: data
				},
				function(error, response) {
					if (error) {
						console.log('Error setting menu: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.log(__filename + ' - setStartButton Error: ', response.body.error)
						}
						resolve(response);
					}
				})
			});
		},

		//SET GREETING MESSAGE RAPH

		setGreetingMessage: function setGreetingMessage(data) {
			return new Promise(function (resolve, reject) {
				request({
					url: 'https://graph.facebook.com/v2.6/me/thread_settings',
					qs: {access_token: token},
					method: 'POST',
					json: {
						"setting_type": "greeting",
						"greeting": {
							"text": data
						}
					}
				},
				function (error, response) {
					if (error) {
						console.log('Error setting menu: ', error);
						reject(error);
					}
					else {
						if (response.body.error) {
							console.log( __filename + ' - setGreetingMessage Error: ', response.body.error)
						}
						resolve(response);
					}
				})
			});
		},

		//END OF GREETING MESSAGE

	}
};

<|endoftext|>


<commit_before>(function(self) {
  'use strict';
  var $ = jQuery;

  var page = $('.page');
  var thread = $('#collapseobj_threadreview');
  var links = $('td.alt1 a');
  var muteRemainingUnmuted = function() {};

  self.port.on('prefs', showVideos);

  function showVideos(prefs) {
    var videos = $([]);

    $(window).resize(resize);
    setTimeout(resize, 50);

    links.each(function() {
      var url = this.href;

      if (/\/redirect\/index.php/.test(url)) {
        url = unescape(url.split('link=')[1]);
      }

      if (!/\.webm$/.test(url)) return;

      var nsfw = false;
      if (!prefs.nsfw) {
        if (this.nextSibling && this.nextSibling.data) {
          nsfw = /NSFW/i.test(this.nextSibling.data);
          this.nextSibling.data = this.nextSibling.data.replace(/NSFW/i, '');
        }

        if (!nsfw && this.previousSibling && this.previousSibling.data) {
          nsfw = /NSFW/i.test(this.previousSibling.data);
          this.previousSibling.data =
            this.previousSibling.data.replace(/NSFW/i, '');
        }

        if (!nsfw && this.parentElement.childNodes[1]) {
          nsfw = this.parentElement.childNodes[1].nodeName === 'BR' &&
            /NSFW/i.test(this.parentElement.firstChild.data);
        }
      }

      var video = $('<video>');

      if (nsfw) {
        video.css('background', [
          'url(' + prefs.nsfwImageURL + ')',
          'no-repeat',
          'center',
          'center / cover',
          'transparent'
        ].join(' '));
      }

      video.attr({
        src: url,
        preload: 'metadata',
        poster: nsfw ? prefs.transparentImageURL : null
      });

      video.prop({
        controls: prefs.controls,
        loop: prefs.loop,
        muted: prefs.muted || null
      });

      if (!nsfw && (prefs.mode === 0 || prefs.mode === 3)) {
        video.on('mouseenter', function() {
          this.play();
        });

        if (prefs.mode === 0) {
          video.on('mouseleave', function() {
            this.pause();
          });
        }
      }

      if (!video.controls) {
        video.on('click', function() {
          if (this.paused) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      if (prefs.mode === 1 && !nsfw) {
        video.on('inview', function(event, inview) {
          if (inview) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      $(this).replaceWith(video);
      videos.push(video);

      if (prefs.muted) {
        var button = $('<img>');
        button.attr('src', prefs.unmuteImageURL);
        button.attr('title', 'Enable audio');

        button.css({
          display: 'none',
          marginBottom: '2px'
        });

        button.click(function toggleMute() {
          if (video.prop('muted')) {
            muteRemainingUnmuted();
            unmute();
            muteRemainingUnmuted = mute;
          } else {
            mute();
          }
        });

        video.before(button);

        video.on('loadeddata', function() {
          if (this.mozHasAudio) {
            button.css('display', 'block');
          }
        });
      }

      function unmute() {
        video.prop('muted', null);
        button.attr('src', prefs.muteImageURL);
        button.attr('title', 'Disable audio');
      }

      function mute() {
        video.prop('muted', true);
        button.attr('src', prefs.unmuteImageURL);
        button.attr('title', 'Enable audio');
      }
    });

    function resize() {
      videos.each(function() {
        var vpH = $(window).width() - 30;

        if (page.get(0)) {
          this.css('max-width', Math.min(vpH, page.width() - 30) + 'px');
        } else if (thread.get(0)) {
          this.css('max-width', Math.min(vpH, thread.width() - 125) + 'px');
        } else {
          this.css('max-width', vpH + 'px');
        }

        if (prefs.muted) {
          var button = this.prev();

          button.css({
            marginLeft: this.width() - button.width()
          });
        }

        this.css('max-height', window.innerHeight + 'px');
      });
    }
  }
})(self);
<commit_msg>add red borders to NSFW videos<commit_after>(function(self) {
  'use strict';
  var $ = jQuery;

  var page = $('.page');
  var thread = $('#collapseobj_threadreview');
  var links = $('td.alt1 a');
  var muteRemainingUnmuted = function() {};

  self.port.on('prefs', showVideos);

  function showVideos(prefs) {
    var videos = $([]);

    $(window).resize(resize);
    setTimeout(resize, 50);

    links.each(function() {
      var url = this.href;

      if (/\/redirect\/index.php/.test(url)) {
        url = unescape(url.split('link=')[1]);
      }

      if (!/\.webm$/.test(url)) return;

      var nsfw = false;
      if (!prefs.nsfw) {
        if (this.nextSibling && this.nextSibling.data) {
          nsfw = /NSFW/i.test(this.nextSibling.data);
          this.nextSibling.data = this.nextSibling.data.replace(/NSFW/i, '');
        }

        if (!nsfw && this.previousSibling && this.previousSibling.data) {
          nsfw = /NSFW/i.test(this.previousSibling.data);
          this.previousSibling.data =
            this.previousSibling.data.replace(/NSFW/i, '');
        }

        if (!nsfw && this.parentElement.childNodes[1]) {
          nsfw = this.parentElement.childNodes[1].nodeName === 'BR' &&
            /NSFW/i.test(this.parentElement.firstChild.data);
        }
      }

      var video = $('<video>');

      if (nsfw) {
        video.css('background', [
          'url(' + prefs.nsfwImageURL + ')',
          'no-repeat',
          'center',
          'center / cover',
          'transparent'
        ].join(' '));

        video.css('border', '1px solid red');
      }

      video.attr({
        src: url,
        preload: 'metadata',
        poster: nsfw ? prefs.transparentImageURL : null
      });

      video.prop({
        controls: prefs.controls,
        loop: prefs.loop,
        muted: prefs.muted || null
      });

      if (!nsfw && (prefs.mode === 0 || prefs.mode === 3)) {
        video.on('mouseenter', function() {
          this.play();
        });

        if (prefs.mode === 0) {
          video.on('mouseleave', function() {
            this.pause();
          });
        }
      }

      if (!video.controls) {
        video.on('click', function() {
          if (this.paused) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      if (prefs.mode === 1 && !nsfw) {
        video.on('inview', function(event, inview) {
          if (inview) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      $(this).replaceWith(video);
      videos.push(video);

      if (prefs.muted) {
        var button = $('<img>');
        button.attr('src', prefs.unmuteImageURL);
        button.attr('title', 'Enable audio');

        button.css({
          display: 'none',
          marginBottom: '2px'
        });

        button.click(function toggleMute() {
          if (video.prop('muted')) {
            muteRemainingUnmuted();
            unmute();
            muteRemainingUnmuted = mute;
          } else {
            mute();
          }
        });

        video.before(button);

        video.on('loadeddata', function() {
          if (this.mozHasAudio) {
            button.css('display', 'block');
          }
        });
      }

      function unmute() {
        video.prop('muted', null);
        button.attr('src', prefs.muteImageURL);
        button.attr('title', 'Disable audio');
      }

      function mute() {
        video.prop('muted', true);
        button.attr('src', prefs.unmuteImageURL);
        button.attr('title', 'Enable audio');
      }
    });

    function resize() {
      videos.each(function() {
        var vpH = $(window).width() - 30;

        if (page.get(0)) {
          this.css('max-width', Math.min(vpH, page.width() - 30) + 'px');
        } else if (thread.get(0)) {
          this.css('max-width', Math.min(vpH, thread.width() - 125) + 'px');
        } else {
          this.css('max-width', vpH + 'px');
        }

        if (prefs.muted) {
          var button = this.prev();

          button.css({
            marginLeft: this.width() - button.width()
          });
        }

        this.css('max-height', window.innerHeight + 'px');
      });
    }
  }
})(self);
<|endoftext|>


<commit_before>(function(self) {
  'use strict';
  var $ = jQuery;

  var page = $('.page');
  var thread = $('#collapseobj_threadreview');
  var links = $('td.alt1 a');

  self.port.on('prefs', showVideos);

  function showVideos(prefs) {
    var videos = $([]);

    $(window).resize(resize);
    setTimeout(resize, 50);

    links.each(function() {
      var url = this.href;

      if (/\/redirect\/index.php/.test(url)) {
        url = unescape(url.split('link=')[1]);
      }

      if (!/\.webm$/.test(url)) return;

      if (!prefs.nsfw && this.parentElement) {
        var nsfw = /NSFW/i.test(this.nextSibling.data);
      }

      var video = $('<video>');

      if (nsfw) {
        video.css('background', [
          'url(' + prefs.nsfwImageURL + ')',
          'no-repeat',
          'center',
          'center / cover',
          'transparent'
        ].join(' '));
      }

      video.attr({
        src: url,
        preload: 'metadata',
        poster: nsfw ? prefs.transparentImageURL : null
      });

      video.prop({
        controls: prefs.controls,
        loop: prefs.loop,
        muted: prefs.muted || null
      });

      if (!nsfw && (prefs.mode === 0 || prefs.mode === 3)) {
        video.on('mouseenter', function() {
          this.play();
        });

        if (prefs.mode === 0) {
          video.on('mouseleave', function() {
            this.pause();
          });
        }
      }

      if (!video.controls) {
        video.on('click', function() {
          if (this.paused) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      if (prefs.mode === 1 && !nsfw) {
        video.on('inview', function(event, inview) {
          if (inview) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      $(this).replaceWith(video);
      videos.push(video);

      if (prefs.muted) {
        var button = $('<img>');
        button.attr('src', prefs.unmuteImageURL);
        button.attr('title', 'Enable audio');

        button.css({
          display: 'none',
          marginBottom: '2px'
        });

        button.click(function() {
          if (video.prop('muted')) {
            video.prop('muted', null);
            button.attr('src', prefs.muteImageURL);
            button.attr('title', 'Disable audio');
          } else {
            video.prop('muted', true);
            button.attr('src', prefs.unmuteImageURL);
            button.attr('title', 'Enable audio');
          }
        });

        video.before(button);

        video.on('loadeddata', function() {
          if (this.mozHasAudio) {
            button.css('display', 'block');
          }
        });
      }
    });

    function resize() {
      videos.each(function() {
        var vpH = $(window).width() - 30;

        if (page.get(0)) {
          this.css('max-width', Math.min(vpH, page.width() - 30) + 'px');
        } else if (thread.get(0)) {
          this.css('max-width', Math.min(vpH, thread.width() - 125) + 'px');
        } else {
          this.css('max-width', vpH + 'px');
        }

        if (prefs.muted) {
          var button = this.prev();

          button.css({
            marginLeft: this.width() - button.width()
          });
        }

        this.css('max-height', window.innerHeight + 'px');
      });
    }
  }
})(self);
<commit_msg>clear the 'NSFW' phrase from the element on loading<commit_after>(function(self) {
  'use strict';
  var $ = jQuery;

  var page = $('.page');
  var thread = $('#collapseobj_threadreview');
  var links = $('td.alt1 a');

  self.port.on('prefs', showVideos);

  function showVideos(prefs) {
    var videos = $([]);

    $(window).resize(resize);
    setTimeout(resize, 50);

    links.each(function() {
      var url = this.href;

      if (/\/redirect\/index.php/.test(url)) {
        url = unescape(url.split('link=')[1]);
      }

      if (!/\.webm$/.test(url)) return;

      if (!prefs.nsfw && this.nextSibling) {
        var nsfw = /NSFW/i.test(this.nextSibling.data);
        this.nextSibling.data = this.nextSibling.data.replace(/NSFW/i, '');
      }

      var video = $('<video>');

      if (nsfw) {
        video.css('background', [
          'url(' + prefs.nsfwImageURL + ')',
          'no-repeat',
          'center',
          'center / cover',
          'transparent'
        ].join(' '));
      }

      video.attr({
        src: url,
        preload: 'metadata',
        poster: nsfw ? prefs.transparentImageURL : null
      });

      video.prop({
        controls: prefs.controls,
        loop: prefs.loop,
        muted: prefs.muted || null
      });

      if (!nsfw && (prefs.mode === 0 || prefs.mode === 3)) {
        video.on('mouseenter', function() {
          this.play();
        });

        if (prefs.mode === 0) {
          video.on('mouseleave', function() {
            this.pause();
          });
        }
      }

      if (!video.controls) {
        video.on('click', function() {
          if (this.paused) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      if (prefs.mode === 1 && !nsfw) {
        video.on('inview', function(event, inview) {
          if (inview) {
            this.play();
          } else {
            this.pause();
          }
        });
      }

      $(this).replaceWith(video);
      videos.push(video);

      if (prefs.muted) {
        var button = $('<img>');
        button.attr('src', prefs.unmuteImageURL);
        button.attr('title', 'Enable audio');

        button.css({
          display: 'none',
          marginBottom: '2px'
        });

        button.click(function() {
          if (video.prop('muted')) {
            video.prop('muted', null);
            button.attr('src', prefs.muteImageURL);
            button.attr('title', 'Disable audio');
          } else {
            video.prop('muted', true);
            button.attr('src', prefs.unmuteImageURL);
            button.attr('title', 'Enable audio');
          }
        });

        video.before(button);

        video.on('loadeddata', function() {
          if (this.mozHasAudio) {
            button.css('display', 'block');
          }
        });
      }
    });

    function resize() {
      videos.each(function() {
        var vpH = $(window).width() - 30;

        if (page.get(0)) {
          this.css('max-width', Math.min(vpH, page.width() - 30) + 'px');
        } else if (thread.get(0)) {
          this.css('max-width', Math.min(vpH, thread.width() - 125) + 'px');
        } else {
          this.css('max-width', vpH + 'px');
        }

        if (prefs.muted) {
          var button = this.prev();

          button.css({
            marginLeft: this.width() - button.width()
          });
        }

        this.css('max-height', window.innerHeight + 'px');
      });
    }
  }
})(self);
<|endoftext|>


<commit_before>import Ember from 'ember';

const { computed, inject, observer, on } = Ember;

const { service } = inject;
const { alias } = computed;

export default Ember.Component.extend({
  to: null,
  classNames: ['liquid-wormhole-container'],

  liquidTarget: alias('to'),
  liquidTargetService: service('liquid-target'),

  nodes: computed(function() {
    return this.$().children();
  }),

  liquidTargetDidChange: observer('liquidTarget', function() {
    this.removeFromTarget();
    this.appendToTarget();
  }),

  didInsertElement() {
    this._target = this.get('liquidTarget');

    this.get('liquidTargetService').appendItem(this._target, this);

    this._super.apply(this, arguments);
  },

  willDestroyElement() {
    this.get('liquidTargetService').removeItem(this._target, this);

    this._super.apply(this, arguments);
  }
});
<commit_msg>quick jshint<commit_after>import Ember from 'ember';

const { computed, inject, observer } = Ember;

const { service } = inject;
const { alias } = computed;

export default Ember.Component.extend({
  to: null,
  classNames: ['liquid-wormhole-container'],

  liquidTarget: alias('to'),
  liquidTargetService: service('liquid-target'),

  nodes: computed(function() {
    return this.$().children();
  }),

  liquidTargetDidChange: observer('liquidTarget', function() {
    this.removeFromTarget();
    this.appendToTarget();
  }),

  didInsertElement() {
    this._target = this.get('liquidTarget');

    this.get('liquidTargetService').appendItem(this._target, this);

    this._super.apply(this, arguments);
  },

  willDestroyElement() {
    this.get('liquidTargetService').removeItem(this._target, this);

    this._super.apply(this, arguments);
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import jQuery from 'jquery';

export default Ember.Component.extend({
  classNames: 'pull-to-refresh-parent',
  classNameBindings: ['refreshing', 'pulling'],
  scrollable: undefined,
  disableMouseEvents: false,
  threshold: 50,
  refreshing: false,
  _startY: undefined,
  _lastY: undefined,

  touchStart(e) {
    const y = e.originalEvent.targetTouches[0].pageY;
    this._start(y);
  },

  mouseDown(e) {
    if (this.get('disableMouseEvents')) {
      return;
    }

    const y = e.pageY;
    this._start(y);
  },

  _start(y) {
    if (this.get('refreshing') || !this._canPullDown()) {
      return;
    }

    this.setProperties({
      _startY: y,
      _lastY: y
    });
  },

  touchMove(e) {
    const y = e.originalEvent.targetTouches[0].pageY;
    this._move(y);
  },

  mouseMove(e) {
    const y = e.pageY;
    this._move(y);
  },

  _move(y) {
    if (this.get('refreshing') || !this.get('_startY')) {
      return;
    }

    this.set('_lastY', y);

    if (!this.get('pulling')) {
      return;
    }

    const dy = Math.min(
      this.get('_dy'),
      (this.get('threshold') * 2)
    );

    this._setTop(dy);
  },

  touchEnd() {
    this._end();
  },

  mouseUp() {
    this._end();
  },

  mouseLeave() {
    this._end();
  },

  _end() {
    if (!this.get('_startY')) {
      return;
    }

    const threshold = this.get('threshold');
    const refreshing = this.get('_dy') >= threshold;

    this.setProperties({
      _startY: undefined,
      _lastY: undefined,
      refreshing: refreshing
    });

    if (refreshing) {
      this.sendAction('refresh');
    }
  },

  _reset: Ember.observer('refreshing', function () {
    let top = 0;
    if (this.get('refreshing')) {
      top = this.get('threshold');
    }

    this._setTop(top);
  }),

  _setTop(y) {
    this.$('.pull-to-refresh-child')
      .css('transform', `translate3d(0, ${y}px, 0)`);
  },

  _dy: Ember.computed('_lastY', '_startY', function () {
    return this.get('_lastY') - this.get('_startY');
  }),

  _canPullDown() {
    let scrollable = this.get('_scrollableEl');

    if (!scrollable) {
      scrollable = jQuery(this.get('scrollable'));

      if (scrollable.length > 0) {
        this.set('_scrollableEl', scrollable);
      }
    }

    return (scrollable.length === 0 ||
      scrollable.scrollTop() === 0);
  },

  pulling: Ember.computed('_startY', '_dy', function () {
    return this.get('_startY') && this._canPullDown() && this.get('_dy') > 0;
  })
});
<commit_msg>fix for newer Embers<commit_after>import Ember from 'ember';
import jQuery from 'jquery';

export default Ember.Component.extend({
  classNames: 'pull-to-refresh-parent',
  classNameBindings: ['refreshing', 'pulling'],
  scrollable: undefined,
  disableMouseEvents: false,
  threshold: 50,
  refreshing: false,
  _startY: undefined,
  _lastY: undefined,

  touchStart(e) {
    const y = e.originalEvent.targetTouches[0].pageY;
    this._start(y);
  },

  mouseDown(e) {
    if (this.get('disableMouseEvents')) {
      return;
    }

    const y = e.pageY;
    this._start(y);
  },

  _start(y) {
    if (this.get('refreshing') || !this._canPullDown()) {
      return;
    }

    this.setProperties({
      _startY: y,
      _lastY: y
    });
  },

  touchMove(e) {
    const y = e.originalEvent.targetTouches[0].pageY;
    this._move(y);
  },

  mouseMove(e) {
    const y = e.pageY;
    this._move(y);
  },

  _move(y) {
    if (this.get('refreshing') || !this.get('_startY')) {
      return;
    }

    this.set('_lastY', y);

    if (!this.get('pulling')) {
      return;
    }

    const dy = Math.min(
      this.get('_dy'),
      (this.get('threshold') * 2)
    );

    this._setTop(dy);
  },

  touchEnd() {
    this._end();
  },

  mouseUp() {
    this._end();
  },

  mouseLeave() {
    this._end();
  },

  _end() {
    if (!this.get('_startY')) {
      return;
    }

    const threshold = this.get('threshold');
    const refreshing = this.get('_dy') >= threshold;

    Ember.run.once(() => {
      this.setProperties({
        _startY: undefined,
        _lastY: undefined,
        refreshing: refreshing
      });

      this._reset();
    });

    if (refreshing) {
      this.sendAction('refresh');
    }
  },

  _reset: Ember.observer('refreshing', function () {
    let top = 0;
    if (this.get('refreshing')) {
      top = this.get('threshold');
    }

    this._setTop(top);
  }),

  _setTop(y) {
    this.$('.pull-to-refresh-child')
      .css('transform', `translate3d(0, ${y}px, 0)`);
  },

  _dy: Ember.computed('_lastY', '_startY', function () {
    return this.get('_lastY') - this.get('_startY');
  }),

  _canPullDown() {
    let scrollable = this.get('_scrollableEl');

    if (!scrollable) {
      scrollable = jQuery(this.get('scrollable'));

      if (scrollable.length > 0) {
        this.set('_scrollableEl', scrollable);
      }
    }

    return (scrollable.length === 0 ||
      scrollable.scrollTop() === 0);
  },

  pulling: Ember.computed('_startY', '_dy', function () {
    return this.get('_startY') && this._canPullDown() && this.get('_dy') > 0;
  })
});
<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  Component,
  computed,
  observer,
  run
} = Ember;

export default Component.extend({
  classNames: ['upf-table__container'],

  hasSelection: false,
  hasPagination: false,
  hasSearch: false,
  hasToggleableColumns: true,

  allRowsSelected: false,
  displayedColumnsPanel: false,
  isLoading: false,

  _searchQuery: null,
  searchInputPlaceholder: 'Search...',

  currentPage: 1,
  totalPages: 1,
  perPage: 1,
  itemTotal: 0,
  itemCount: 0,
  itemName: '',

  init() {
    this._super();

    this.set('_searchQuery', this.get('searchQuery'));
  },

  _columns: computed('columns', function() {
    return this.get('columns').map((column) => {
      column.visible = column.visible !== false;
      column.sorted = column.sorted === true;
      column.editable = column.editable !== false;
      column.unhideable = column.unhideable === true;

      return Ember.Object.create(column);
    });
  }),

  _initiallyDisplayedColumns: computed.filterBy('_columns', 'visible'),
  _fullSizeColumnColspan: computed('_initiallyDisplayedColumns', function() {
    if (this.get('hasSelection')) {
      return this.get('_initiallyDisplayedColumns').length + 1;
    }

    return this.get('_initiallyDisplayedColumns').length;
  }),

  _selectAllObserver: observer('allRowsSelected', function() {
    this.get('collection').map((item) => {
      if (this.get('allRowsSelected')) {
        item.set('selected', true);
      } else {
        item.set('selected', false);
      }
    });
  }),

  _bubbleSearch: function() {
    this.get('_targetObject').send('performSearch', this.get('_searchQuery'));
  },

  _searchQueryObserver: observer('_searchQuery', function() {
    run.debounce(this, this._bubbleSearch, 100);
  }),

  actions: {
    toggleDisplayedColumnVisibilityPanel() {
      this.toggleProperty('displayedColumnsPanel');
    },

    callOnRowClickCallback: function(action, record) {
      this.get('_targetObject').send(
        action,
        record
      );
    },

    didPageChange(page) {
      this.sendAction('didPageChange', page);
    },

    onClickHeader(column) {
      if (!column.get('sortKey')) {
        return;
      }

      if (!column.get('sorted')) {
        this.get('_columns').forEach((c) => c.set('sorted', false));
        // default direction
        column.set('sorted', true);
        column.set('direction', 'desc');
      } else {
        let direction = column.get('direction') === 'desc' ? 'asc' : 'desc';
        column.set('direction', direction);
      }

      this.sendAction('didSortColumn', column);
    }
  }
});
<commit_msg>added an action to be called when people want to create something from the table<commit_after>import Ember from 'ember';

const {
  Component,
  computed,
  observer,
  run
} = Ember;

export default Component.extend({
  classNames: ['upf-table__container'],

  hasSelection: false,
  hasPagination: false,
  hasSearch: false,
  hasToggleableColumns: true,

  allRowsSelected: false,
  displayedColumnsPanel: false,
  isLoading: false,

  _searchQuery: null,
  searchInputPlaceholder: 'Search...',

  currentPage: 1,
  totalPages: 1,
  perPage: 1,
  itemTotal: 0,
  itemCount: 0,
  itemName: '',

  init() {
    this._super();

    this.set('_searchQuery', this.get('searchQuery'));
  },

  _columns: computed('columns', function() {
    return this.get('columns').map((column) => {
      column.visible = column.visible !== false;
      column.sorted = column.sorted === true;
      column.editable = column.editable !== false;
      column.unhideable = column.unhideable === true;

      return Ember.Object.create(column);
    });
  }),

  _initiallyDisplayedColumns: computed.filterBy('_columns', 'visible'),
  _fullSizeColumnColspan: computed('_initiallyDisplayedColumns', function() {
    if (this.get('hasSelection')) {
      return this.get('_initiallyDisplayedColumns').length + 1;
    }

    return this.get('_initiallyDisplayedColumns').length;
  }),

  _selectAllObserver: observer('allRowsSelected', function() {
    this.get('collection').map((item) => {
      if (this.get('allRowsSelected')) {
        item.set('selected', true);
      } else {
        item.set('selected', false);
      }
    });
  }),

  _bubbleSearch: function() {
    this.get('_targetObject').send('performSearch', this.get('_searchQuery'));
  },

  _searchQueryObserver: observer('_searchQuery', function() {
    run.debounce(this, this._bubbleSearch, 100);
  }),

  actions: {
    toggleDisplayedColumnVisibilityPanel() {
      this.toggleProperty('displayedColumnsPanel');
    },

    callOnRowClickCallback: function(action, record) {
      this.get('_targetObject').send(
        action,
        record
      );
    },

    didPageChange(page) {
      this.sendAction('didPageChange', page);
    },

    onClickHeader(column) {
      if (!column.get('sortKey')) {
        return;
      }

      if (!column.get('sorted')) {
        this.get('_columns').forEach((c) => c.set('sorted', false));
        // default direction
        column.set('sorted', true);
        column.set('direction', 'desc');
      } else {
        let direction = column.get('direction') === 'desc' ? 'asc' : 'desc';
        column.set('direction', direction);
      }

      this.sendAction('didSortColumn', column);
    },

    triggerObjectCreation() {
      this.triggerAction({ action: 'handleObjectCreation'});
    }
  }
});
<|endoftext|>


<commit_before>import addons from '@storybook/addons';
import { EVENT_ID } from '../shared';

// init function will be executed once when the storybook loads for the
// first time. This is a good place to add global listeners on channel.
export function init() {
  // NOTE nothing to do here
}

function regExpStringify(exp) {
  if (typeof exp === 'string') return exp;
  if (Object.prototype.toString.call(exp) === '[object RegExp]') return exp.source;
  return null;
}

// setOptions function will send Storybook UI options when the channel is
// ready. If called before, options will be cached until it can be sent.
export function setOptions(newOptions) {
  const channel = addons.getChannel();
  if (!channel) {
    throw new Error(
      'Failed to find addon channel. This may be due to https://github.com/storybooks/storybook/issues/1192.'
    );
  }

  let options = newOptions;

  // since 'undefined' and 'null' are the valid values we don't want to
  // override the hierarchySeparator if the prop is missing
  if (Object.prototype.hasOwnProperty.call(newOptions, 'hierarchySeparator')) {
    options = {
      ...newOptions,
      hierarchySeparator: regExpStringify(newOptions.hierarchySeparator),
    };
  }

  channel.emit(EVENT_ID, { options });
}
<commit_msg>extend options addon to enable rootSeparator<commit_after>import addons from '@storybook/addons';
import { EVENT_ID } from '../shared';

// init function will be executed once when the storybook loads for the
// first time. This is a good place to add global listeners on channel.
export function init() {
  // NOTE nothing to do here
}

function hasOwnProp(object, propName) {
  return Object.prototype.hasOwnProperty.call(object, propName);
}

function regExpStringify(exp) {
  if (typeof exp === 'string') return exp;
  if (Object.prototype.toString.call(exp) === '[object RegExp]') return exp.source;
  return null;
}

// setOptions function will send Storybook UI options when the channel is
// ready. If called before, options will be cached until it can be sent.
export function setOptions(newOptions) {
  const channel = addons.getChannel();
  if (!channel) {
    throw new Error(
      'Failed to find addon channel. This may be due to https://github.com/storybooks/storybook/issues/1192.'
    );
  }

  // since 'undefined' and 'null' are the valid values we don't want to
  // override the hierarchySeparator or rootSeparator if the prop is missing
  const options = {
    ...newOptions,
    ...(hasOwnProp(newOptions, 'hierarchySeparator')
      ? { hierarchySeparator: regExpStringify(newOptions.hierarchySeparator) }
      : {}),
    ...(hasOwnProp(newOptions, 'rootSeparator')
      ? { rootSeparator: regExpStringify(newOptions.rootSeparator) }
      : {}),
  };

  channel.emit(EVENT_ID, { options });
}
<|endoftext|>