<commit_before>/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */

define([
    'underscore',
    'backbone'
], function (_, Backbone) {
    var WizardView = Backbone.View.extend({
        render: function () {
            var wizardTempl = contrail.getTemplate4Id("sm-wizard-view-template"),
                viewConfig = this.attributes.viewConfig,
                elId = this.attributes.elementId,
                validation = this.attributes.validation,
                lockEditingByDefault = this.attributes.lockEditingByDefault,
                self = this,
                childViewObj, childElId, steps;

            this.$el.html(wizardTempl({viewConfig: viewConfig, elementId: elId}));
            steps = viewConfig['steps'];

            this.$el.find("#" + elId).contrailWizard({
                headerTag: "h2",
                bodyTag: "section",
                transitionEffect: "slideLeft",
                titleTemplate: '#index# #title#',
                steps: steps,
                onInit: function (event, currentIndex) {
                    for (var i = 0; i < steps.length; i++) {
                        childViewObj = steps[i];
                        childElId = childViewObj['elementId'];
                        smUtils.renderView4Config($("#" + childElId), self.model, childViewObj, validation, lockEditingByDefault);
                    }
                }
            });

            this.$el.parents('.modal-body').css({'padding': '0'});

            this.$el.find('.actions').find('a').addClass('btn btn-mini')
            this.$el.find('.actions').find('a[href="#next"]').addClass('btn-primary');
            this.$el.find('.actions').find('a[href="#finish"]').addClass('btn-mini');

            $('.wizard > .steps > ul > li').css({
                'max-width': (100/steps.length) + '%'
            });

            $('.wizard > .steps ul li').each(function(key, value){
                if(steps[key].stepType == 'sub-step'){
                    $(this).addClass('subStep');
                    $(this).find('.number').text('');
                    $(this).find('.title').text('');

                }
            });
        }
    });

    return WizardView;
});<commit_msg>Bug fixes - Fixed numbering of steps for Add Cluster<commit_after>/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */

define([
    'underscore',
    'backbone'
], function (_, Backbone) {
    var WizardView = Backbone.View.extend({
        render: function () {
            var wizardTempl = contrail.getTemplate4Id("sm-wizard-view-template"),
                viewConfig = this.attributes.viewConfig,
                elId = this.attributes.elementId,
                validation = this.attributes.validation,
                lockEditingByDefault = this.attributes.lockEditingByDefault,
                self = this,
                childViewObj, childElId, steps;

            this.$el.html(wizardTempl({viewConfig: viewConfig, elementId: elId}));
            steps = viewConfig['steps'];

            this.$el.find("#" + elId).contrailWizard({
                headerTag: "h2",
                bodyTag: "section",
                transitionEffect: "slideLeft",
                titleTemplate: '#index# #title#',
                steps: steps,
                onInit: function (event, currentIndex) {
                    for (var i = 0; i < steps.length; i++) {
                        childViewObj = steps[i];
                        childElId = childViewObj['elementId'];
                        smUtils.renderView4Config($("#" + childElId), self.model, childViewObj, validation, lockEditingByDefault);
                    }
                }
            });

            this.$el.parents('.modal-body').css({'padding': '0'});

            this.$el.find('.actions').find('a').addClass('btn btn-mini')
            this.$el.find('.actions').find('a[href="#next"]').addClass('btn-primary');
            this.$el.find('.actions').find('a[href="#finish"]').addClass('btn-mini');

            $('.wizard > .steps > ul > li').css({
                'max-width': (100/steps.length) + '%'
            });

            var stepIndex = 0;
            $('.wizard > .steps ul li').each(function(key, value){
                if(steps[key].stepType == 'sub-step'){
                    $(this).addClass('subStep');
                    $(this).find('.number').text('');
                    $(this).find('.title').text('');

                }
                else {
                    $(this).find('.number').text(++stepIndex);
                }
            });
        }
    });

    return WizardView;
});<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var config = require('./config');

mongoose.connect(config.db, function(err) {
  if(err) {
  	console.error("Having trouble connecting to %s, the problem seems to be ", config.db, err.message);
  }
});

//models
require('./user');
require('./note');
require('./topic');
require('./append');
require('./reply');
require('./messsage');
require('./topic_collect');

exports.User          = mongoose.model('User');
exports.Note          = mongoose.model('Note');
exports.Topic         = mongoose.model('Topic');
exports.append        = mongoose.model('Append');
exports.Reply         = mongoose.model('Reply');
exports.Message       = mongoose.model('Message');
exports.topic_collect = mongoose.model('TopicCollect');<commit_msg>Added other models to export.<commit_after>var mongoose = require('mongoose');
var config = require('./config');

mongoose.connect(config.db, function(err) {
  if(err) {
  	console.error("Having trouble connecting to %s, the problem seems to be ", config.db, err.message);
  }
});

//models
require('./user');
require('./note');
require('./section');
require('./node');
require('./topic');
require('./append');
require('./reply');
require('./messsage');
require('./topic_collect');

exports.User          = mongoose.model('User');
exports.Note          = mongoose.model('Note');
exports.Section       = mongoose.model('Section');
exports.Node          = mongoose.model('Node');
exports.Topic         = mongoose.model('Topic');
exports.append        = mongoose.model('Append');
exports.Reply         = mongoose.model('Reply');
exports.Message       = mongoose.model('Message');
exports.topic_collect = mongoose.model('TopicCollect');<|endoftext|>


<commit_before>/**
 * Created by faisal on 3/16/15.
 */

import Ember from 'ember';
import BaseMod from 'tealeaves/components/vizmods/base-viz';
import tools from 'tealeaves/library/toolkit';
/* global d3 */

function generateChart(target_elem, legend_elem, topic_set, message_data) {
  var margin = {
      top: 40,
      right: 30,
      bottom: 30,
      left: 50
    },
    width = Ember.$(target_elem).width() - margin.left - margin.right,
    height = Ember.$(target_elem).height() - margin.top - margin.bottom;

  var parseDate = d3.time.format("%Y-%m-%dT%I:%M:%S").parse;
  var formatDate = d3.time.format("%m/%d");
  var formatCount = d3.format(",.0f");

  var x = d3.time.scale().range([0, width]);
  var y = d3.scale.linear().range([height, 0]);
  var color = d3.scale.category20();

  // fix the colors to the topic IDs so they're consistent between vis components
  color.domain(Object.keys(topic_set));

  var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").tickFormat(formatDate);

  var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(12);

  var stack = d3.layout.stack()
    .values(function(d) {
      return d.values;
    });

  // Create the SVG drawing area
  var svg = d3.select(target_elem)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // Parse the date strings into javascript dates
  message_data.forEach(function(d) {
    d.received_on = parseDate(d.received_on);
  });

  // Determine the first and list dates in the data set
  var timeExtent = d3.extent(message_data, function(d) {
    return d.received_on;
  });

  // Create one bin per day, use an offset to include the first and last days(?)
  var dayBins = d3.time.days(d3.time.day.offset(timeExtent[0], -1),
    d3.time.day.offset(timeExtent[1], 0));

  // Use the histogram layout to create a function that will bin the data
  var binByDay = d3.layout.histogram()
    .value(function(d) {
      return d.received_on;
    })
    .bins(dayBins);

  // Use D3's nest function to group the data by borough
  var dataGroupedByBorough = d3.nest()
    .key(function(d) {
      return d.topics[0].topic_id;
    })
    .map(message_data, d3.map);

  // Apply the histogram binning function to the data and convert from
  // a map to an array so that the stack layout can consume it
  var histDataByBorough = [];
  dataGroupedByBorough.entries().map(function(o) {
    var key = o.key;
    var value = o.value;

    // Bin the data for each borough by day
    var histData = binByDay(value);
    histDataByBorough.push({
      topic_id: key,
      values: histData
    });
  });

  var stackedHistData = stack(histDataByBorough);

  // Scale the range of the data by setting the domain
  x.domain(d3.extent(dayBins));
  y.domain([0, d3.max(stackedHistData[stackedHistData.length - 1].values, function(d) {
    return d.y + d.y0;
  })]);

  // Define the div for the tooltip
  var div = d3.select("body").append("div")
    .attr("class", "topicstacked-tooltip")
    .style("opacity", 0);

  // Set up one group for each borough
  // Note that color doesn't have a domain set, so colors are assigned to boroughs
  // below on a first come, first serve basis
  var borough = svg.selectAll(".borough")
    .data(stackedHistData)
    .enter().append("g")
    .attr("class", "borough")
    .style("fill", function(d, i) {
      return color(d.topic_id);
    })
    .style("stroke", function(d, i) {
      return d3.rgb(color(d.topic_id)).darker();
    })
    .on("mouseover", function(d) {
      div.transition()
        .duration(200)
        .style("opacity", 0.9);
      div	.html(topic_set[d.topic_id])
        .style("left", (d3.event.pageX) + "px")
        .style("top", (d3.event.pageY - 28) + "px");
    })
    .on("mouseout", function(d) {
      div.transition()
        .duration(500)
        .style("opacity", 0);
    });

  // Months have slightly different lengths so calculate the width for each bin
  // Draw the rectangles, starting from the upper left corner and going down
  borough.selectAll(".bar")
    .data(function(d) {
      return d.values;
    })
    .enter().append("rect")
    .attr("class", "bar")
    .attr("x", function(d) {
      return x(d.x);
    })
    .attr("width", function(d) {
      return x(new Date(d.x.getTime() + d.dx)) - x(d.x) - 2;
    })
    .attr("y", function(d) {
      return y(d.y0 + d.y);
    })
    .attr("height", function(d) {
      return y(d.y0) - y(d.y0 + d.y);
    });

  // Add the X Axis
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  // Add the Y Axis and label
  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);
    /*
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Number of Rat Sightings");
    */

  // Add the legend to the second box
  Ember.$.each(topic_set, function(k, v) {
    // colored box + label container
    var $topic_set = Ember.$("<div />")
      .css('margin', '3px')
      .css('margin-left', '50px')
      .css('display', 'flex');

    // colored box
    Ember.$("<div />")
      .css('width', '15px')
      .css('height', '15px')
      .css('margin-right', '3px')
      .css('background-color', color(k))
      .appendTo($topic_set);

    // label
    Ember.$("<div />")
      .text(v)
      .appendTo($topic_set);

    $topic_set.appendTo(legend_elem);
  });
}

export default BaseMod.extend({
  classNames: ['topic-stacked'],

  bind: function(start_date, end_date, filters) {
    var _this = this;

    // display the spinner
    this.$(".loader").show();

    var $boxShell = this.$(".topicstacked-box");
    var $target = this.$(".topicstacked-box svg");
    var $legend_target = this.$(".topic-legend");

    // remove any elements in the SVG
    $target.empty();
    $legend_target.empty();

    $target.attr('viewBox', `0 0 ${$boxShell.width()} ${$boxShell.height()}`);
    $target.attr('width', $boxShell.width());
    $target.attr('height', $boxShell.height());

    // we have to query for two things:
    // 1) the set of topics for this user
    // 2) the mail messages with associated 'topics' field keyed to set of topics

    _this.get('eaf_api').query('topic', {})
      .then(function(data) {
        var topic_set = data.objects.reduce(function(acc, v) { acc[v.id] = v.shortname; return acc; }, {});

        var params = {
          min_date: tools.apiTZDateTime(start_date),
          max_date: tools.apiTZDateTime(end_date),
          topics: 1
        };

        _this.applyAlterFilter(filters, params);

        // once we have the topics, we can query for the messages and build the rest of the graph
        _this.get('eaf_api').query('mail_message', params)
          .then(function(message_data) {
            var msg_topics = message_data.objects.map(function(v) {
              return {'received_on': v.received_on, 'topics': v.topics};
            }).filter(function(v) {
              return v.topics.length > 0;
            });

            generateChart($target.get(0), $legend_target.get(0), topic_set, msg_topics);

            // now we can generate the chart
            _this.$(".loader").hide();
          });
      });
  }
});
<commit_msg>Added label filtering to topic stack, removed stroke on rectangles to make them look slightly nicer.<commit_after>/**
 * Created by faisal on 3/16/15.
 */

import Ember from 'ember';
import BaseMod from 'tealeaves/components/vizmods/base-viz';
import tools from 'tealeaves/library/toolkit';
/* global d3 */

function generateChart(target_elem, legend_elem, topic_set, message_data) {
  var margin = {
      top: 40,
      right: 30,
      bottom: 30,
      left: 50
    },
    width = Ember.$(target_elem).width() - margin.left - margin.right,
    height = Ember.$(target_elem).height() - margin.top - margin.bottom;

  var parseDate = d3.time.format("%Y-%m-%dT%I:%M:%S").parse;
  var formatDate = d3.time.format("%m/%d");
  var formatCount = d3.format(",.0f");

  var x = d3.time.scale().range([0, width]);
  var y = d3.scale.linear().range([height, 0]);
  var color = d3.scale.category20();

  // fix the colors to the topic IDs so they're consistent between vis components
  color.domain(Object.keys(topic_set));

  var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").tickFormat(formatDate);

  var yAxis = d3.svg.axis().scale(y)
    .orient("left");

  var stack = d3.layout.stack()
    .values(function(d) {
      return d.values;
    });

  // Create the SVG drawing area
  var svg = d3.select(target_elem)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  // Parse the date strings into javascript dates
  message_data.forEach(function(d) {
    d.received_on = parseDate(d.received_on);
  });

  // Determine the first and list dates in the data set
  var timeExtent = d3.extent(message_data, function(d) {
    return d.received_on;
  });

  // Create one bin per day, use an offset to include the first and last days(?)
  var dayBins = d3.time.days(d3.time.day.offset(timeExtent[0], -1),
    d3.time.day.offset(timeExtent[1], 0));

  // Use the histogram layout to create a function that will bin the data
  var binByDay = d3.layout.histogram()
    .value(function(d) {
      return d.received_on;
    })
    .bins(dayBins);

  // Use D3's nest function to group the data by borough
  var dataGroupedByBorough = d3.nest()
    .key(function(d) {
      return d.topics[0].topic_id;
    })
    .map(message_data, d3.map);

  // Apply the histogram binning function to the data and convert from
  // a map to an array so that the stack layout can consume it
  var histDataByBorough = [];
  dataGroupedByBorough.entries().map(function(o) {
    var key = o.key;
    var value = o.value;

    // Bin the data for each borough by day
    var histData = binByDay(value);
    histDataByBorough.push({
      topic_id: key,
      values: histData
    });
  });

  var stackedHistData = stack(histDataByBorough);

  // Scale the range of the data by setting the domain
  x.domain(d3.extent(dayBins));
  y.domain([0, d3.max(stackedHistData[stackedHistData.length - 1].values, function(d) {
    return d.y + d.y0;
  })]);

  // Define the div for the tooltip
  var div = d3.select("body").append("div")
    .attr("class", "topicstacked-tooltip")
    .style("opacity", 0);

  // Set up one group for each borough
  // Note that color doesn't have a domain set, so colors are assigned to boroughs
  // below on a first come, first serve basis
  var borough = svg.selectAll(".borough")
    .data(stackedHistData)
    .enter().append("g")
    .attr("class", "borough")
    .style("fill", function(d, i) {
      return color(d.topic_id);
    })
    // .style("stroke", function(d, i) {
    //   return d3.rgb(color(d.topic_id)).darker();
    // })
    .on("mouseover", function(d) {
      div.transition()
        .duration(200)
        .style("opacity", 0.9);
      div	.html(topic_set[d.topic_id])
        .style("left", (d3.event.pageX) + "px")
        .style("top", (d3.event.pageY - 28) + "px");
    })
    .on("mouseout", function(d) {
      div.transition()
        .duration(500)
        .style("opacity", 0);
    });

  // Months have slightly different lengths so calculate the width for each bin
  // Draw the rectangles, starting from the upper left corner and going down
  borough.selectAll(".bar")
    .data(function(d) {
      return d.values;
    })
    .enter().append("rect")
    .attr("class", "bar")
    .attr("x", function(d) {
      return x(d.x);
    })
    .attr("width", function(d) {
      return x(new Date(d.x.getTime() + d.dx)) - x(d.x) - 2;
    })
    .attr("y", function(d) {
      return y(d.y0 + d.y);
    })
    .attr("height", function(d) {
      return y(d.y0) - y(d.y0 + d.y);
    });

  // Add the X Axis
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  // Add the Y Axis and label
  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);
    /*
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Number of Rat Sightings");
    */

  // Add the legend to the second box
  Ember.$.each(topic_set, function(k, v) {
    // colored box + label container
    var $topic_set = Ember.$("<div />")
      .css('margin', '3px')
      .css('margin-left', '50px')
      .css('display', 'flex');

    // colored box
    Ember.$("<div />")
      .css('width', '15px')
      .css('height', '15px')
      .css('margin-right', '3px')
      .css('background-color', color(k))
      .appendTo($topic_set);

    // label
    Ember.$("<div />")
      .text(v)
      .appendTo($topic_set);

    $topic_set.appendTo(legend_elem);
  });
}

export default BaseMod.extend({
  classNames: ['topic-stacked'],

  bind: function(start_date, end_date, filters) {
    var _this = this;

    // display the spinner
    this.$(".loader").show();

    var $boxShell = this.$(".topicstacked-box");
    var $target = this.$(".topicstacked-box svg");
    var $legend_target = this.$(".topic-legend");

    // remove any elements in the SVG
    $target.empty();
    $legend_target.empty();

    $target.attr('viewBox', `0 0 ${$boxShell.width()} ${$boxShell.height()}`);
    $target.attr('width', $boxShell.width());
    $target.attr('height', $boxShell.height());

    // we have to query for two things:
    // 1) the set of topics for this user
    // 2) the mail messages with associated 'topics' field keyed to set of topics

    _this.get('eaf_api').query('topic', {})
      .then(function(data) {
        var topic_set = data.objects.reduce(function(acc, v) { acc[v.id] = v.shortname; return acc; }, {});

        var params = {
          min_date: tools.apiTZDateTime(start_date),
          max_date: tools.apiTZDateTime(end_date),
          topics: 1
        };

        _this.applyAlterFilter(filters, params);
        _this.applyLabelFilter(filters, params);

        // once we have the topics, we can query for the messages and build the rest of the graph
        _this.get('eaf_api').query('mail_message', params)
          .then(function(message_data) {
            var msg_topics = message_data.objects.map(function(v) {
              return {'received_on': v.received_on, 'topics': v.topics};
            }).filter(function(v) {
              return v.topics.length > 0;
            });

            generateChart($target.get(0), $legend_target.get(0), topic_set, msg_topics);

            // now we can generate the chart
            _this.$(".loader").hide();
          });
      });
  }
});
<|endoftext|>


<commit_before>// remap jQuery to $
(function($) {

  $(function() {

    // Resizable
	  var mouseMoveHandler = function(e) {
	    var initial = e.data.initial,
          u = Math.min(Math.max(initial.uHeight + (e.pageY - initial.offTop), initial.min), initial.max),
          b = Math.min(Math.max(initial.bHeight - (e.pageY - initial.offTop), initial.min), initial.max);
      initial.up.height(u);
      initial.below.height(b);

      $('#iframe-cover').height(initial.up.height()); // iframe fix
    };
    var mouseUpHandler = function(e) {
      $(document)
        .unbind('mousemove', mouseMoveHandler)
        .unbind('mouseup', mouseUpHandler);

      $('#iframe-cover').hide(); // iframe fix
      if (e.data.initial.below.height() === 0) {
        $('#main').removeClass('opened');
        Pontoon.common.postMessage("MODE", "Advanced");
      } else {
        $('#main').addClass('opened');
        Pontoon.common.postMessage("MODE", "Basic");
      }
    };
	  $('#logo, #drag').bind('mousedown', function(e) {
      e.preventDefault();

      var up = $('#source'),
          below = $('#entitylist'),
          data = {
            up: up,
            below: below,
            uHeight: up.height(),
            bHeight: below.height(),
            offTop: e.pageY,
            min: 0,
            max: $(document).height()
          };

      // iframe fix: Prevent iframes from capturing the mousemove events during a drag
      $('#iframe-cover').show().height(up.height());

      $(document)
        .bind('mousemove', { initial: data }, mouseMoveHandler)
        .bind('mouseup', { initial: data }, mouseUpHandler);
    });

    // Resize iframe with window
    $(window).resize(function () {
      $('#source').height($(document).height() - $('#main').height());
    });

    // Show page load Error
    function showError(callback, message) {
      var error = message || 'Oops, website is either not supported by Pontoon or could not be found.';
      clearInterval(callback);
      $('#project-load').hide();
      $("#install").css('visibility', 'visible');
      $('#intro')
        .find('.notification')
          .html(error)
          .addClass('error').removeClass('message')
          .css('visibility', 'visible').end()
        .css('display', 'table').hide().fadeIn();
    }

    // Validate URL
    function checkURL() {
      // Initialize Pontoon only if project code supports it
      function receiveMessage(e) {
        // TODO: Check origin - hardcode Pontoon domain name
        if (JSON.parse(e.data).type === "SUPPORTED") {
          // Show header and iframe with appropriate height
          $('#main').slideDown(function() {
            $('#source').fadeIn().height($(document).height() - $(this).height());
          });
          Pontoon.init(window, $('#source').get(0).contentWindow, locale);
          window.removeEventListener("message", receiveMessage, false);
        }
      }
      window.addEventListener("message", receiveMessage, false);

      // Load project page into iframe
      $('#source').attr('src', url);

      // Show error message if no callback for 5 seconds: Pontoon/iframe not supported, 404…
      var i = 0,
          callback = setInterval(function() {
            if (i < 50) {
              i++;
              if (Pontoon.app) { // Set in Pontoon.init() which is called on "supported"
                clearInterval(callback);
              }
            } else {
              showError(callback, 'Oops, website is not supported by Pontoon. Check instructions below.');
            }
          }, 100);

      // If loading page fails, don't wait 5 seconds to trigger error (ignore localhost)
      if (url.split('://')[1].indexOf('localhost') !== 0) {
        $.ajax({
          url: base + 'checkurl/',
          data: {url: url},
          timeout: 4500,
          success: function(data) {
            if (data === "invalid") {
              showError(callback, 'Oops, website could not be found.');
            }
          }
        });
      }
    }

    // Update locale selector
    function updateLocale(locale) {
      var menu = $('.locale .menu');
      if (menu.find('.language.' + locale).length === 0) {
        locale = menu.find('.language:first').attr('class').split(' ')[1];
      }
      $('.locale .button .language').addClass(locale).html(menu.find('.language.' + locale).html());
    }

    // MAIN CODE

    // Empty iframe if cached
    $('#source').removeAttr("src");

    // Check for params
    var locale = $('#server').data('locale'),
        url = $('#server').data('url'),
        acceptLanguage = $('#server').data('accept-language'),
        base = $('base').attr('href');

    // Set include script URL
    $("#install code").html('<script src="' + base + 'media/js/project/pontoon.js"></script>');

    // Translate
    if (locale && url) {
      $('.url').val(url);
      // Set locale
      if ($('.locale .menu .language.' + locale).length) { // Locale on the list?
        updateLocale(locale);
      } else {
        // TODO: show warning about switching locales
        updateLocale(acceptLanguage);
      }
      checkURL();

    // Intro
    } else {
      updateLocale(acceptLanguage);
      $('#install').css('visibility', 'visible');
      $('#project-load').hide();
      $('#intro').css('display', 'table').hide().fadeIn(function() {});
    }

  });

}(this.jQuery));

// usage: log('inside coolFunc',this,arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function() {
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if (this.console) {
    console.log(Array.prototype.slice.call(arguments));
  }
};

// catch all document.write() calls
(function(doc) {
  var write = doc.write;
  doc.write = function(q) {
    log('document.write(): ', arguments);
    if (/docwriteregexwhitelist/.test(q)) {
      write.apply(doc,arguments);
    }
  };
}(document));
<commit_msg>Also break on invalid urls<commit_after>// remap jQuery to $
(function($) {

  $(function() {

    // Resizable
	  var mouseMoveHandler = function(e) {
	    var initial = e.data.initial,
          u = Math.min(Math.max(initial.uHeight + (e.pageY - initial.offTop), initial.min), initial.max),
          b = Math.min(Math.max(initial.bHeight - (e.pageY - initial.offTop), initial.min), initial.max);
      initial.up.height(u);
      initial.below.height(b);

      $('#iframe-cover').height(initial.up.height()); // iframe fix
    };
    var mouseUpHandler = function(e) {
      $(document)
        .unbind('mousemove', mouseMoveHandler)
        .unbind('mouseup', mouseUpHandler);

      $('#iframe-cover').hide(); // iframe fix
      if (e.data.initial.below.height() === 0) {
        $('#main').removeClass('opened');
        Pontoon.common.postMessage("MODE", "Advanced");
      } else {
        $('#main').addClass('opened');
        Pontoon.common.postMessage("MODE", "Basic");
      }
    };
	  $('#logo, #drag').bind('mousedown', function(e) {
      e.preventDefault();

      var up = $('#source'),
          below = $('#entitylist'),
          data = {
            up: up,
            below: below,
            uHeight: up.height(),
            bHeight: below.height(),
            offTop: e.pageY,
            min: 0,
            max: $(document).height()
          };

      // iframe fix: Prevent iframes from capturing the mousemove events during a drag
      $('#iframe-cover').show().height(up.height());

      $(document)
        .bind('mousemove', { initial: data }, mouseMoveHandler)
        .bind('mouseup', { initial: data }, mouseUpHandler);
    });

    // Resize iframe with window
    $(window).resize(function () {
      $('#source').height($(document).height() - $('#main').height());
    });

    // Show page load Error
    function showError(callback, message) {
      var error = message || 'Oops, website is either not supported by Pontoon or could not be found.';
      clearInterval(callback);
      $('#project-load').hide();
      $("#install").css('visibility', 'visible');
      $('#intro')
        .find('.notification')
          .html(error)
          .addClass('error').removeClass('message')
          .css('visibility', 'visible').end()
        .css('display', 'table').hide().fadeIn();
    }

    // Validate URL
    function checkURL() {
      // Initialize Pontoon only if project code supports it
      function receiveMessage(e) {
        // TODO: Check origin - hardcode Pontoon domain name
        if (JSON.parse(e.data).type === "SUPPORTED") {
          // Show header and iframe with appropriate height
          $('#main').slideDown(function() {
            $('#source').fadeIn().height($(document).height() - $(this).height());
          });
          Pontoon.init(window, $('#source').get(0).contentWindow, locale);
          window.removeEventListener("message", receiveMessage, false);
        }
      }
      window.addEventListener("message", receiveMessage, false);

      // Load project page into iframe
      $('#source').attr('src', url);

      // Show error message if no callback for 5 seconds: Pontoon/iframe not supported, 404…
      var i = 0,
          callback = setInterval(function() {
            if (i < 50) {
              i++;
              if (Pontoon.app) { // Set in Pontoon.init() which is called on "supported"
                clearInterval(callback);
              }
            } else {
              showError(callback, 'Oops, website is not supported by Pontoon. Check instructions below.');
            }
          }, 100);

      // If loading page fails, don't wait 5 seconds to trigger error (ignore localhost)
      if (!url.split('://')[1] || url.split('://')[1].indexOf('localhost') !== 0) {
        $.ajax({
          url: base + 'checkurl/',
          data: {url: url},
          timeout: 4500,
          success: function(data) {
            if (data === "invalid") {
              showError(callback, 'Oops, website could not be found.');
            }
          }
        });
      }
    }

    // Update locale selector
    function updateLocale(locale) {
      var menu = $('.locale .menu');
      if (menu.find('.language.' + locale).length === 0) {
        locale = menu.find('.language:first').attr('class').split(' ')[1];
      }
      $('.locale .button .language').addClass(locale).html(menu.find('.language.' + locale).html());
    }

    // MAIN CODE

    // Empty iframe if cached
    $('#source').removeAttr("src");

    // Check for params
    var locale = $('#server').data('locale'),
        url = $('#server').data('url'),
        acceptLanguage = $('#server').data('accept-language'),
        base = $('base').attr('href');

    // Set include script URL
    $("#install code").html('<script src="' + base + 'media/js/project/pontoon.js"></script>');

    // Translate
    if (locale && url) {
      $('.url').val(url);
      // Set locale
      if ($('.locale .menu .language.' + locale).length) { // Locale on the list?
        updateLocale(locale);
      } else {
        // TODO: show warning about switching locales
        updateLocale(acceptLanguage);
      }
      checkURL();

    // Intro
    } else {
      updateLocale(acceptLanguage);
      $('#install').css('visibility', 'visible');
      $('#project-load').hide();
      $('#intro').css('display', 'table').hide().fadeIn(function() {});
    }

  });

}(this.jQuery));

// usage: log('inside coolFunc',this,arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function() {
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if (this.console) {
    console.log(Array.prototype.slice.call(arguments));
  }
};

// catch all document.write() calls
(function(doc) {
  var write = doc.write;
  doc.write = function(q) {
    log('document.write(): ', arguments);
    if (/docwriteregexwhitelist/.test(q)) {
      write.apply(doc,arguments);
    }
  };
}(document));
<|endoftext|>


<commit_before>'use strict'

const t = require('../lib/translator')
const Logger = require('../lib/logger')
const languages = require('../lib/lang')
const mixpanel = require('../lib/mixpanel')
const db = require('../db/redis')
const _ = require('lodash')

const responseType = {
  getStarted: '!getstarted',
  help: '#help',
  change: '#change',
  switch: '#switch',
  list: '#list'
}

class MessageHandler {

  // handleMessage is our main handler
  static handleMessage(bot, payload, reply) {

    const { sender, message, postback } = payload
    bot.setTyping(sender.id, true)
    bot.getProfile(sender.id, (err, profile) => {

      if (err) return Logger.log(err)

      db.getAsync(sender.id).then((context) => {
        if (postback && postback.payload) {
          return MessageHandler.handlePostBack(context, postback, profile, sender, reply)
        }
        if (_.includes(message.text, responseType.change)) {
          return MessageHandler.handleChange(sender, reply)
        }
        if (_.includes(message.text, responseType.switch)) {
          return MessageHandler.handleSwitch(context, sender, message, reply)
        }
        if (_.includes(message.text, responseType.help) || context === null && message.text === 'help') {
          return MessageHandler.handleHelp(context, sender, reply)
        }
        if (_.includes(message.text, responseType.list)) {
          return MessageHandler.handleShowAllLanguages(sender, reply)
        }
        if (context === null) {
          return MessageHandler.handleNoContext(sender, profile, message, reply)
        }
        // we made it! translate the message
        MessageHandler.handleTranslation(context, sender, message, reply)
      })
    })
  }

  // handlePostBack
  static handlePostBack(context, postback, profile, sender, reply) {
    if (postback.payload === responseType.getStarted) {
      return MessageHandler.handleGetStarted(sender, profile, reply)
    }
    if (postback.payload === responseType.change) {
      return MessageHandler.handleChange(sender, reply)
    }
    if (postback.payload === responseType.switch) {
      return MessageHandler.handleSwitch(context, sender, reply)
    }
    if (postback.payload === responseType.list) {
      return MessageHandler.handleShowAllLanguages(sender, reply)
    }
    return null
  }

  // handleGetStarted
  static handleGetStarted(sender, profile, reply) {
    reply({
      text: `Hola ${profile.first_name}, let's get started!\n\nI speak lots of different languages, so go ahead and tell me what to translate for you.\n\nExample: "English to Spanish" or "Greek to Japanese"\n\nAsk for #help at any time`
    }, () => {
      mixpanel.setPerson(sender, profile)
      mixpanel.track('I click to get started', sender)
    })
  }

  // handleHelp
  static handleHelp(context, sender, reply) {
    let options = [
      {
        'type': 'postback',
        'title': 'Show all languages',
        'payload': responseType.list
      }
    ]

    if (context !== null) {
      const switchedContext = switchContext(getContextFromCode(context))
      options.push(
        {
          'type': 'postback',
          'title': `${switchedContext.from} to ${switchedContext.to}`,
          'payload': responseType.switch
        },
        {
          'type': 'postback',
          'title': 'Change languages',
          'payload': responseType.change
        }
      )
    }

    return reply({
      'attachment': {
        'type': 'template',
        'payload': {
          'template_type': 'button',
          'text': `Hey there! Here are some helpful shortcuts:\n\n"${responseType.change}" to change languages\n"${responseType.switch}" to switch languages\n\n Or choose a command:\n\n`,
          'buttons': options
        }
      }
    }, () => {
      mixpanel.track('I ask for help', sender)
    })
  }

  // handleShowAllLanguages
  // due to FBs payload size limit we need to chunk the list into seprate bits
  static handleShowAllLanguages(sender, reply) {
    const list = getAllLanguageNames()
    const first = list.splice(0, list.length / 3)
    const last = list.splice(0, list.length / 2)
    reply({text: `OK, here goes...\n\n${first.toString().replace(/,/g, ', ')}`}, () => {
      reply({text: `${last.toString().replace(/,/g, ', ')}`}, () => {
        reply({text: `${list.toString().replace(/,/g, ', ')}\n\n *GULP*`})
      })
    })
  }

  // handleChange
  static handleChange(sender, reply) {
    db.delAsync(sender.id).then((err) => {
      if (err) Logger.log(err)
      return reply({
        text: 'Sure thing. What language would you like me to translate for you now?\n\n Example: "English to German"'
      }, () => {
        mixpanel.track('I change context', sender)
      })
    })
  }

  // handleSwitch
  static handleSwitch(context, sender, reply) {
    if (context === null) {
      return reply({
        text: `Hmmm... I don't know what language I'm supposed to be translating for you.\n\nExample: "Thai to French" or "Russian to Dutch"`
      })
    }
    context = switchContext(getContextFromCode(context))
    return MessageHandler.handleSetContext(context.code, context.from, context.to, sender, null, reply)
  }

  // handleNoContext
  static handleNoContext(sender, profile, message, reply) {
    const context = getContextFromMessage(message.text)
    if (context.hasTwo) {
      return MessageHandler.handleSetContext(context.code, context.from, context.to, sender, message, reply)
    }
    if (MessageHandler.handleGeneralResponse(sender, profile, message, reply) !== false) {
      return
    }

    let text = `Oops, I didn't quite catch that.\n\nSay something like "English to Spanish" or "Korean to Portugese"`
    if (context.hasOne) text = `I only caught ${context.from} there. Please try again`
    return reply({
      text: text
    }, () => {
      mixpanel.track('I incorrectly set context', sender, message)
    })
  }

  // handleGeneralResponse such as hello, goodbye etc
  static handleGeneralResponse(sender, profile, message, reply) {
    if (_.includes(['goodbye', 'bye', 'adios', 'ciao', 'seeya', 'see you', 'later'], message.text.toLowerCase())) {
      return reply({
        text: `Adiós ${profile.gender === 'male' ? 'muchacho' : 'muchacha'}`
      }, () => {
        mixpanel.track('I say goodbye without context', sender, message)
      })
    }
    if (_.includes(['hello', 'hi', 'howdy', 'hallo', 'yo', 'hey', 'sup', 'hiya'], message.text.toLowerCase())) {
      return reply({
        text: getRandom([`Hi ${profile.first_name}!`, 'Hello', '¡Hola!', `Hey ${profile.first_name}!`, 'Oh hey there!', 'Hallo', 'Howdy', 'Oh Hiiiiii'])
      }, () => {
        mixpanel.track('I say hello without context', sender, message)
      })
    }
    return false
  }

  // handleSetContext
  static handleSetContext(code, from, to, sender, message, reply) {
    db.setAsync(sender.id, code).then((err) => {
      if (err) Logger.log(err)
      return reply({
        text: `${from} to ${to}. Got it! Now go ahead and tell me what to say.\n\nAsk for "${responseType.help}" at any time`
      }, () => {
        mixpanel.track('I set context', sender, message)
      })
    })
  }

  // handleTranslation
  static handleTranslation(context, sender, message, reply) {
    t.translate(message.text, context).
      then((result) => {
        reply({ text: result }, (err) => {
          if (err) Logger.log(err)
          mixpanel.track('I send a message to be translated', sender, message)
        })
      }).
      catch((err) => {
        if (err) Logger.log(err)
        reply({ text: 'Oh no, something has gone wrong. Please try that again' })
      })
  }
}

// private methods
// getContextFromMessage
function getContextFromMessage(message) {
  const ctxMatches = getContextMatches(message)
  const { hasTwo, hasOne, hasNone } = ctxMatches
  let code = null
  let from = null
  let to = null

  if (hasOne || hasTwo) {
    from = _.capitalize(ctxMatches.matches[0].name)
  }
  if (hasTwo) {
    code = `${ctxMatches.matches[0].code}:${ctxMatches.matches[1].code}`
    to = _.capitalize(ctxMatches.matches[1].name)
  }
  return { code, from, to, hasTwo, hasOne, hasNone }
}

// getContextFromCode
function getContextFromCode(code) {
  const codes = code.split(':')
  return {
    code,
    from: _.capitalize(getLanguageName(codes[0])),
    to: _.capitalize(getLanguageName(codes[1])),
    hasTwo: true
  }
}

// getContext
function getContextMatches(message) {
  let matches = []
  const words = message.split(' ')
  words.forEach((word) => {
    languages.filter(item => item.name === _.lowerCase(word)).map((lang) => {
      if (matches.length === 2) return
      matches.push(lang)
    })
  })
  return {
    hasTwo: matches.length === 2,
    hasOne: matches.length === 1,
    hasNone: matches.length === 0,
    matches: matches
  }
}

// switchContext
function switchContext(context) {
  const values = context.code.split(':')
  const from = context.to
  const to = context.from

  context.code = `${values[1]}:${values[0]}`
  context.from = from
  context.to = to

  return context
}

// getRandom
function getRandom(responses) {
  return responses[Math.floor(Math.random()*responses.length)]
}

// getLanguageName
function getLanguageName(code) {
  return languages.filter(item => item.code === code).map((lang) => {
    return lang.name
  })
}

// listAllLanguages
function getAllLanguageNames() {
  let list = []
  languages.forEach(function(lang) {
    list.push(_.capitalize(lang.name))
  })
  return list
}

module.exports = MessageHandler<commit_msg>handleSwitch arg fix<commit_after>'use strict'

const t = require('../lib/translator')
const Logger = require('../lib/logger')
const languages = require('../lib/lang')
const mixpanel = require('../lib/mixpanel')
const db = require('../db/redis')
const _ = require('lodash')

const responseType = {
  getStarted: '!getstarted',
  help: '#help',
  change: '#change',
  switch: '#switch',
  list: '#list'
}

class MessageHandler {

  // handleMessage is our main handler
  static handleMessage(bot, payload, reply) {

    const { sender, message, postback } = payload
    bot.setTyping(sender.id, true)
    bot.getProfile(sender.id, (err, profile) => {

      if (err) return Logger.log(err)

      db.getAsync(sender.id).then((context) => {
        if (postback && postback.payload) {
          return MessageHandler.handlePostBack(context, postback, profile, sender, reply)
        }
        if (_.includes(message.text, responseType.change)) {
          return MessageHandler.handleChange(sender, reply)
        }
        if (_.includes(message.text, responseType.switch)) {
          return MessageHandler.handleSwitch(context, sender, reply)
        }
        if (_.includes(message.text, responseType.help) || context === null && message.text.toLowerCase() === 'help') {
          return MessageHandler.handleHelp(context, sender, reply)
        }
        if (_.includes(message.text, responseType.list)) {
          return MessageHandler.handleShowAllLanguages(sender, reply)
        }
        if (context === null) {
          return MessageHandler.handleNoContext(sender, profile, message, reply)
        }
        // we made it! translate the message
        MessageHandler.handleTranslation(context, sender, message, reply)
      })
    })
  }

  // handlePostBack
  static handlePostBack(context, postback, profile, sender, reply) {
    if (postback.payload === responseType.getStarted) {
      return MessageHandler.handleGetStarted(sender, profile, reply)
    }
    if (postback.payload === responseType.change) {
      return MessageHandler.handleChange(sender, reply)
    }
    if (postback.payload === responseType.switch) {
      return MessageHandler.handleSwitch(context, sender, reply)
    }
    if (postback.payload === responseType.list) {
      return MessageHandler.handleShowAllLanguages(sender, reply)
    }
    return null
  }

  // handleGetStarted
  static handleGetStarted(sender, profile, reply) {
    reply({
      text: `Hola ${profile.first_name}, let's get started!\n\nI speak lots of different languages, so go ahead and tell me what to translate for you.\n\nExample: "English to Spanish" or "Greek to Japanese"\n\nAsk for #help at any time`
    }, () => {
      mixpanel.setPerson(sender, profile)
      mixpanel.track('I click to get started', sender)
    })
  }

  // handleHelp
  static handleHelp(context, sender, reply) {
    let options = [
      {
        'type': 'postback',
        'title': 'Show all languages',
        'payload': responseType.list
      }
    ]

    if (context !== null) {
      const switchedContext = switchContext(getContextFromCode(context))
      options.push(
        {
          'type': 'postback',
          'title': `${switchedContext.from} to ${switchedContext.to}`,
          'payload': responseType.switch
        },
        {
          'type': 'postback',
          'title': 'Change languages',
          'payload': responseType.change
        }
      )
    }

    return reply({
      'attachment': {
        'type': 'template',
        'payload': {
          'template_type': 'button',
          'text': `Hey there! Here are some helpful shortcuts:\n\n"${responseType.change}" to change languages\n"${responseType.switch}" to switch languages\n\n Or choose a command:\n\n`,
          'buttons': options
        }
      }
    }, () => {
      mixpanel.track('I ask for help', sender)
    })
  }

  // handleShowAllLanguages
  // due to FBs payload size limit we need to chunk the list into seprate bits
  static handleShowAllLanguages(sender, reply) {
    const list = getAllLanguageNames()
    const first = list.splice(0, list.length / 3)
    const last = list.splice(0, list.length / 2)
    reply({text: `OK, here goes...\n\n${first.toString().replace(/,/g, ', ')}`}, () => {
      reply({text: `${last.toString().replace(/,/g, ', ')}`}, () => {
        reply({text: `${list.toString().replace(/,/g, ', ')}\n\n *GULP*`})
      })
    })
  }

  // handleChange
  static handleChange(sender, reply) {
    db.delAsync(sender.id).then((err) => {
      if (err) Logger.log(err)
      return reply({
        text: 'Sure thing. What language would you like me to translate for you now?\n\n Example: "English to German"'
      }, () => {
        mixpanel.track('I change context', sender)
      })
    })
  }

  // handleSwitch
  static handleSwitch(context, sender, reply) {
    if (context === null) {
      return reply({
        text: `Hmmm... I don't know what language I'm supposed to be translating for you.\n\nExample: "Thai to French" or "Russian to Dutch"`
      })
    }
    context = switchContext(getContextFromCode(context))
    return MessageHandler.handleSetContext(context.code, context.from, context.to, sender, null, reply)
  }

  // handleNoContext
  static handleNoContext(sender, profile, message, reply) {
    const context = getContextFromMessage(message.text)
    if (context.hasTwo) {
      return MessageHandler.handleSetContext(context.code, context.from, context.to, sender, message, reply)
    }
    if (MessageHandler.handleGeneralResponse(sender, profile, message, reply) !== false) {
      return
    }

    let text = `Oops, I didn't quite catch that.\n\nSay something like "English to Spanish" or "Korean to Portugese"`
    if (context.hasOne) text = `I only caught ${context.from} there. Please try again`
    return reply({
      text: text
    }, () => {
      mixpanel.track('I incorrectly set context', sender, message)
    })
  }

  // handleGeneralResponse such as hello, goodbye etc
  static handleGeneralResponse(sender, profile, message, reply) {
    if (_.includes(['goodbye', 'bye', 'adios', 'ciao', 'seeya', 'see you', 'later'], message.text.toLowerCase())) {
      return reply({
        text: `Adiós ${profile.gender === 'male' ? 'muchacho' : 'muchacha'}`
      }, () => {
        mixpanel.track('I say goodbye without context', sender, message)
      })
    }
    if (_.includes(['hello', 'hi', 'howdy', 'hallo', 'yo', 'hey', 'sup', 'hiya'], message.text.toLowerCase())) {
      return reply({
        text: getRandom([`Hi ${profile.first_name}!`, 'Hello', '¡Hola!', `Hey ${profile.first_name}!`, 'Oh hey there!', 'Hallo', 'Howdy', 'Oh Hiiiiii'])
      }, () => {
        mixpanel.track('I say hello without context', sender, message)
      })
    }
    return false
  }

  // handleSetContext
  static handleSetContext(code, from, to, sender, message, reply) {
    db.setAsync(sender.id, code).then((err) => {
      if (err) Logger.log(err)
      return reply({
        text: `${from} to ${to}. Got it! Now go ahead and tell me what to say.\n\nAsk for "${responseType.help}" at any time`
      }, () => {
        mixpanel.track('I set context', sender, message)
      })
    })
  }

  // handleTranslation
  static handleTranslation(context, sender, message, reply) {
    t.translate(message.text, context).
      then((result) => {
        reply({ text: result }, (err) => {
          if (err) Logger.log(err)
          mixpanel.track('I send a message to be translated', sender, message)
        })
      }).
      catch((err) => {
        if (err) Logger.log(err)
        reply({ text: 'Oh no, something has gone wrong. Please try that again' })
      })
  }
}

// private methods
// getContextFromMessage
function getContextFromMessage(message) {
  const ctxMatches = getContextMatches(message)
  const { hasTwo, hasOne, hasNone } = ctxMatches
  let code = null
  let from = null
  let to = null

  if (hasOne || hasTwo) {
    from = _.capitalize(ctxMatches.matches[0].name)
  }
  if (hasTwo) {
    code = `${ctxMatches.matches[0].code}:${ctxMatches.matches[1].code}`
    to = _.capitalize(ctxMatches.matches[1].name)
  }
  return { code, from, to, hasTwo, hasOne, hasNone }
}

// getContextFromCode
function getContextFromCode(code) {
  const codes = code.split(':')
  return {
    code,
    from: _.capitalize(getLanguageName(codes[0])),
    to: _.capitalize(getLanguageName(codes[1])),
    hasTwo: true
  }
}

// getContext
function getContextMatches(message) {
  let matches = []
  const words = message.split(' ')
  words.forEach((word) => {
    languages.filter(item => item.name === _.lowerCase(word)).map((lang) => {
      if (matches.length === 2) return
      matches.push(lang)
    })
  })
  return {
    hasTwo: matches.length === 2,
    hasOne: matches.length === 1,
    hasNone: matches.length === 0,
    matches: matches
  }
}

// switchContext
function switchContext(context) {
  const values = context.code.split(':')
  const from = context.to
  const to = context.from

  context.code = `${values[1]}:${values[0]}`
  context.from = from
  context.to = to

  return context
}

// getRandom
function getRandom(responses) {
  return responses[Math.floor(Math.random()*responses.length)]
}

// getLanguageName
function getLanguageName(code) {
  return languages.filter(item => item.code === code).map((lang) => {
    return lang.name
  })
}

// listAllLanguages
function getAllLanguageNames() {
  let list = []
  languages.forEach(function(lang) {
    list.push(_.capitalize(lang.name))
  })
  return list
}

module.exports = MessageHandler<|endoftext|>


<commit_before>var child_process = require('child_process');
var process = require('process');

var AccountManager = require('./account-manager.js');
var StateController = require('./state-controller.js');
var utils = require('./utils.js');

var Installer = class {
  static get INTERVAL() {
    return 2000;
  }

  constructor(paths) {
    this.flow = null;
    this.path = paths.length ? paths[0] : process.env.HOME;
  }

  init(flow) {
    this.flow = flow;
    this.flow.onInstall(this.install.bind(this));
    this.flow.onCreateAccount(this.createAccount.bind(this));
    this.flow.onLogin(this.login.bind(this));
    this.flow.onResetPassword(this.resetPassword.bind(this));
    this.flow.onLoginBack(() => this.flow.showCreateAccount());
    this.flow.onWhitelist(this.whitelist.bind(this));
    this.flow.onSkipWhitelist(this.skipWhitelist.bind(this));

    this.kiteCanRun = true;
    this.authTimerID = null;
    this.whitelistTimerID = null;

    this.installOpts = {
      deferWrite: true,
      onDownload: () => {
        this.flow.installStep.progress.addMarker(20, 4000, 10);
      },
      onWrite: () => {
        this.flow.installStep.progress.addMarker(30, 1000, 10);
      },
      onMount: () => {
        this.flow.installStep.progress.addMarker(40, 500, 10);
      },
      onCopy: () => {
        this.flow.installStep.progress.addMarker(60, 500, 10);
      },
      onUnmount: () => {
        this.flow.installStep.progress.addMarker(80, 500, 10);
      },
      onRemove: () => {
        this.flow.installStep.progress.addMarker(90, 500, 10);
      },
    };
  }

  get projectName() {
    if (!this.path) {
      return null;
    }
    var parts = this.path.split('/');
    return parts[parts.length - 1];
  }

  install() {
    this.flow.showCreateAccount(this.getUserEmail());
    StateController.installKiteRelease(this.installOpts).then(() => {
      console.log("waiting to install...");
    }, (err) => {
      console.error("error installing kite:", err);
    });
  }

  writeAndRun(callback) {
    StateController.writeDownloadedKite(this.installOpts).then(() => {
      StateController.runKiteAndWait(30, Installer.INTERVAL).then(() => {
        console.log("Kite running!");
        this.flow.installStep.progress.addMarker(95, 100, 10);
        typeof(callback) === 'function' && callback();
      }, (err) => {
        console.error("can't run Kite", err);
        this.kiteCanRun = false;
        this.flow.installStep.progress.addMarker(95, 100, 10);
      });
    }, (err) => {
      console.error("error writing kite:", err);
    });
  }

  createAccount() {
    try {
      var data = this.flow.createAccountStep.data;
      var req = AccountManager.createAccount(data, (resp) => {
        switch (resp.statusCode) {
        case 200:
          this.flow.showWhitelist({
            email: this.flow.createAccountStep.data,
            path: this.projectName,
          });
          this.writeAndRun();
          break;
        case 409:
          this.flow.showLogin();
          break;
        default:
          this.flow.createAccountStep.showError("An error occurred");
          break;
        }
      });
    } catch (err) {
      console.error(`error creating account: ${ err.message }`);
    }
  }

  login() {
    var handle = (resp) => {
      switch (resp.statusCode) {
      case 200:
        this.flow.showWhitelist({ path: this.projectName });
        this.writeAndRun(() => this.attemptAuthenticate());
        break;
      case 401:
        utils.handleResponseData(resp, (raw) => {
          try {
            var data = JSON.parse(raw);
            if (data.code === 9) {
              this.flow.loginStep.showError(
                "To login, set your password first");
            } else {
              this.flow.loginStep.showError(
                "Wrong email/password combination");
            }
          } catch (e) {
            this.flow.loginStep.showError("An error occurred");
          }
        });
        break;
      default:
        this.flow.loginStep.showError("An error occurred");
        break;
      }
    };

    try {
      var data = this.flow.loginStep.data;
      var req = AccountManager.login(data, handle);
    } catch (err) {
      console.error(`error logging in: ${ err.message }`);
    }
  }

  resetPassword() {
    var handle = (resp) => {
      switch (resp.statusCode) {
      case 200:
        var msg = "We've sent you an email with instructions " +
          "to reset your password.";
        this.flow.loginStep.showStatus(msg);
        break;
      default:
        this.flow.loginStep.showError("An error occurred");
        console.error(resp);
        break;
      }
    };

    try {
      var data = this.flow.loginStep.data;
      var req = AccountManager.resetPassword(data, handle);
    } catch (err) {
      console.error(`error logging in: ${ err.message }`);
    }
  }

  attemptAuthenticate() {
    var auth = () => {
      if (!this.kiteCanRun) {
        console.log("kite can't run - aborting authenticate");
        return;
      }
      var sessionKey = AccountManager.client.cookies['kite-session'];
      console.log("session key:", sessionKey.Value);
      var data = this.flow.loginStep.data;
      StateController.authenticateUser(data.email, data.password).then(() => {
        console.log("successfully authenticated!");
      }, (err) => {
        console.error("authenticate error:", err);
        this.attemptAuthenticate();
      });
    };

    console.log("attempting to authenticate...");
    this.authTimerID = setTimeout(() => {
      auth();
    }, Installer.INTERVAL);
  }

  whitelist() {
    this.flow.finishWhitelist();
    this.attemptWhitelist();
  }

  attemptWhitelist() {
    var finish = () => {
      this.flow.installStep.progress.addMarker(100, 100, 10);
      this.flow.finishFlow();
      console.log("successfully whitelisted!");
    };

    var whitelist = () => {
      if (!this.kiteCanRun) {
        this.flow.installStep.progress.addMarker(100, 100, 10);
        console.log("kite can't run - aborting whitelist");
        return;
      }
      if (!this.path) {
        this.flow.installStep.progress.addMarker(100, 100, 10);
        console.log("no project paths - aborting whitelist");
        return;
      }
      StateController.whitelistPath(this.path).then(() => {
        finish();
      }, (err) => {
        if (err.type === 'bad_state' &&
            err.data === StateController.STATES.WHITELISTED) {
          finish();
          return;
        }
        console.error("whitelist error:", err);
        this.attemptWhitelist();
      });
    };

    console.log("attempting to whitelist...");
    this.whitelistTimerID = setTimeout(() => {
      whitelist();
    }, Installer.INTERVAL);
  }

  skipWhitelist() {
    this.flow.finishWhitelist();
    StateController.waitForKite(30, Installer.INTERVAL).then(() => {
      this.flow.installStep.progress.addMarker(100, 100, 10, () => {
        this.flow.finishFlow();
      });
    }, (err) => {
      console.error("error waiting for kite:", err);
      this.flow.installStep.progress.addMarker(100, 100, 10, () => {
        this.flow.finishFlow();
      });
    });
  }

  getUserEmail() {
    var proc = child_process.spawnSync('git', ['config', 'user.email'], {
      encoding: 'utf8',
    });
    return proc.stdout.trim();
  }
};

module.exports = Installer;
<commit_msg>cleanup logging<commit_after>var child_process = require('child_process');
var process = require('process');

var AccountManager = require('./account-manager.js');
var StateController = require('./state-controller.js');
var utils = require('./utils.js');

var Installer = class {
  static get INTERVAL() {
    return 2500;
  }

  constructor(paths) {
    this.flow = null;
    this.path = paths.length ? paths[0] : process.env.HOME;
  }

  init(flow) {
    this.flow = flow;
    this.flow.onInstall(this.install.bind(this));
    this.flow.onCreateAccount(this.createAccount.bind(this));
    this.flow.onLogin(this.login.bind(this));
    this.flow.onResetPassword(this.resetPassword.bind(this));
    this.flow.onLoginBack(() => this.flow.showCreateAccount());
    this.flow.onWhitelist(this.whitelist.bind(this));
    this.flow.onSkipWhitelist(this.skipWhitelist.bind(this));

    this.kiteCanRun = true;
    this.authTimerID = null;
    this.whitelistTimerID = null;

    this.installOpts = {
      deferWrite: true,
      onDownload: () => {
        this.flow.installStep.progress.addMarker(20, 4000, 10);
      },
      onWrite: () => {
        this.flow.installStep.progress.addMarker(30, 1000, 10);
      },
      onMount: () => {
        this.flow.installStep.progress.addMarker(40, 500, 10);
      },
      onCopy: () => {
        this.flow.installStep.progress.addMarker(60, 500, 10);
      },
      onUnmount: () => {
        this.flow.installStep.progress.addMarker(80, 500, 10);
      },
      onRemove: () => {
        this.flow.installStep.progress.addMarker(90, 500, 10);
      },
    };
  }

  get projectName() {
    if (!this.path) {
      return null;
    }
    var parts = this.path.split('/');
    return parts[parts.length - 1];
  }

  install() {
    this.flow.showCreateAccount(this.getUserEmail());
    StateController.installKiteRelease(this.installOpts).catch((err) => {
      console.error("error installing Kite:", err);
    });
  }

  writeAndRun(callback) {
    StateController.writeDownloadedKite(this.installOpts).then(() => {
      StateController.runKiteAndWait(30, Installer.INTERVAL).then(() => {
        this.flow.installStep.progress.addMarker(95, 100, 10);
        typeof(callback) === 'function' && callback();
      }, (err) => {
        console.error("error running Kite:", err);
        this.kiteCanRun = false;
        this.flow.installStep.progress.addMarker(95, 100, 10);
      });
    }, (err) => {
      console.error("error saving Kite:", err);
    });
  }

  createAccount() {
    try {
      var data = this.flow.createAccountStep.data;
      var req = AccountManager.createAccount(data, (resp) => {
        switch (resp.statusCode) {
        case 200:
          this.flow.showWhitelist({
            email: this.flow.createAccountStep.data,
            path: this.projectName,
          });
          this.writeAndRun(() => this.attemptAuthenticate());
          break;
        case 409:
          this.flow.showLogin();
          break;
        default:
          this.flow.createAccountStep.showError("An error occurred");
          break;
        }
      });
    } catch (err) {
      console.error("error creating account:", err);
    }
  }

  login() {
    var handleUnauthorized = (resp) => {
      try {
        var data = JSON.parse(resp);
        if (data.code === 9) {
          this.flow.loginStep.showError("To login, set your password first");
        } else {
          this.flow.loginStep.showError("Wrong email/password combination");
        }
      } catch (e) {
        this.flow.loginStep.showError("An error occurred");
      }
    };
    try {
      var data = this.flow.loginStep.data;
      var req = AccountManager.login(data, (resp) => {
        switch (resp.statusCode) {
        case 200:
          this.flow.showWhitelist({ path: this.projectName });
          this.writeAndRun(() => this.attemptAuthenticate());
          break;
        case 400:
        case 401:
          utils.handleResponseData(resp, (raw) => handleUnauthorized(raw));
          break;
        default:
          this.flow.loginStep.showError("An error occurred");
          break;
        }
        });
    } catch (err) {
      console.error("error logging in:", err);
    }
  }

  resetPassword() {
    try {
      var data = this.flow.loginStep.data;
      var req = AccountManager.resetPassword(data, (resp) => {
        switch (resp.statusCode) {
        case 200:
          var msg = "We've sent you an email with instructions " +
            "to reset your password.";
          this.flow.loginStep.showStatus(msg);
          break;
        default:
          this.flow.loginStep.showError("An error occurred");
          break;
        }
      });
    } catch (err) {
      console.error("error resetting password:", err);
    }
  }

  attemptAuthenticate() {
    var auth = () => {
      if (!this.kiteCanRun) {
        console.error("Kite can't run - aborting authenticate");
        return;
      }
      var sessionKey = AccountManager.client.cookies['kite-session'];
      StateController.authenticateSessionID(sessionKey.Value).catch((err) => {
        console.error("error authenticating:", err);
        this.attemptAuthenticate();
      });
    };
    this.authTimerID = setTimeout(() => {
      auth();
    }, Installer.INTERVAL);
  }

  whitelist() {
    this.flow.finishWhitelist();
    this.attemptWhitelist();
  }

  attemptWhitelist() {
    var finish = () => {
      this.flow.installStep.progress.addMarker(100, 100, 10);
      this.flow.finishFlow();
    };

    var whitelist = () => {
      if (!this.kiteCanRun) {
        this.flow.installStep.progress.addMarker(100, 100, 10);
        console.error("Kite can't run - aborting whitelist");
        return;
      }
      if (!this.path) {
        this.flow.installStep.progress.addMarker(100, 100, 10);
        return;
      }
      StateController.whitelistPath(this.path).then(() => {
        finish();
      }, (err) => {
        if (err.type === 'bad_state' &&
            err.data === StateController.STATES.WHITELISTED) {
          finish();
          return;
        }
        console.error("error whitelisting:", err);
        this.attemptWhitelist();
      });
    };
    this.whitelistTimerID = setTimeout(() => {
      whitelist();
    }, Installer.INTERVAL);
  }

  skipWhitelist() {
    this.flow.finishWhitelist();
    StateController.waitForKite(30, Installer.INTERVAL).then(() => {
      this.flow.installStep.progress.addMarker(100, 100, 10, () => {
        this.flow.finishFlow();
      });
    }, (err) => {
      console.error("error waiting to whitelist:", err);
      this.flow.installStep.progress.addMarker(100, 100, 10, () => {
        this.flow.finishFlow();
      });
    });
  }

  getUserEmail() {
    var proc = child_process.spawnSync('git', ['config', 'user.email'], {
      encoding: 'utf8',
    });
    return proc.stdout.trim();
  }
};

module.exports = Installer;
<|endoftext|>


<commit_before>var mongoose = require("mongoose");
var Schema = mongoose.Schema;

var LocationSchema = new Schema({
	name: String,
	active: { type: Boolean, default: true, index: true },
	city: String,
	address: String,
	img: String,
	description: String,
	email: String,
	bank_account: Number,
	bank_code: String,
	community_manager_name: String,
	community_manager_email: String,
	community_manager_tel: String,
	mail_template: String,
	xero_paypal_id: String,
	xero_creditcard_id: String,
	xero_eft_id: String,
	xero_tax_type: String,
	xero_tracking_name: String,
	xero_branding_theme: String,
	clay_id: String,
	nas_ip: String,
	_deleted: { type: Boolean, default: false, index: true }
});

LocationSchema.set("_perms", {
	admin: "r",
	user: "r",
	all: "r",
	setup: "crud",
});

module.exports = mongoose.model("Location", LocationSchema);
<commit_msg>Friendly names for locations<commit_after>var mongoose = require("mongoose");
var Schema = mongoose.Schema;

var friendly = require("mongoose-friendly");

var LocationSchema = new Schema({
	name: String,
	urlid: { type: String, index: { unique: true, partialFilterExpression: { urlid: { $type: 'string' } } } },
	active: { type: Boolean, default: true, index: true },
	city: String,
	address: String,
	img: String,
	description: String,
	email: String,
	bank_account: Number,
	bank_code: String,
	community_manager_name: String,
	community_manager_email: String,
	community_manager_tel: String,
	mail_template: String,
	xero_paypal_id: String,
	xero_creditcard_id: String,
	xero_eft_id: String,
	xero_tax_type: String,
	xero_tracking_name: String,
	xero_branding_theme: String,
	clay_id: String,
	nas_ip: String,
	_deleted: { type: Boolean, default: false, index: true }
});

LocationSchema.set("_perms", {
	admin: "r",
	user: "r",
	all: "r",
	setup: "crud",
});

LocationSchema.plugin(friendly, {
	source: 'name',
	friendly: 'urlid',
	addIndex: false,
	findById: true
});

module.exports = mongoose.model("Location", LocationSchema);
<|endoftext|>


<commit_before>"use strict";

// TODO - move to lib/ dir?

var AppConfig = require('../config/AppConfig');

// var Bonfires = require('../lib/app/Bonfires');

// from the webapp
// users enter the rooms with a topic=XXX url
// we find a matching room here with that topic
// and redirect them

var RoomData;

// TODO - read this from the JSON file
var bonfireTopics = [
    "bonfires",
    "Pair Program on Bonfires",
    "Meet Bonfire",
    "Reverse a String",
    "Factorialize a Number",
    "Check for Palindromes",
    "Find the Longest Word in a String",
    "Title Case a Sentence",
    "Return Largest Numbers in Arrays",
    "Confirm the Ending",
    "Repeat a string repeat a string",
    "Truncate a string",
    "Chunky Monkey",
    "Slasher Flick",
    "Mutations",
    "Falsey Bouncer",
    "Where art thou",
    "Seek and Destroy",
    "Where do I belong",
    "Sum All Numbers in a Range",
    "Diff Two Arrays",
    "Roman Numeral Converter",
    "Search and Replace",
    "Pig Latin",
    "DNA Pairing",
    "Missing letters",
    "Boo who",
    "Sorted Union",
    "Convert HTML Entities",
    "Spinal Tap Case",
    "Sum All Odd Fibonacci Numbers",
    "Sum All Primes",
    "Smallest Common Multiple",
    "Finders Keepers",
    "Drop it",
    "Steamroller",
    "Binary Agents",
    "Everything Be True",
    "Arguments Optional"
];

var BotRoomData = {

    // developer bot
    bothelp: [
        {
            title: "bothelp",
            name: "bothelp/testing",
            icon: "question",
            topics: ["chitchat", "bots", "bot-development", "camperbot"]
        },

        {
            title: "HelpBonfires",
            icon: "fire",
            name: "bothelp/HelpBonfires",
            topics: bonfireTopics
        },

        {
            title: "dev2",
            name: "bothelp/dev2",
            topics: ["dev2"]
        },

        {
            title: "bothelpDM",
            name: "bothelp",
            topics: ["chitchat", "dm"]
        },

        {
            title: "GeneralChat",
            name: "bothelp/GeneralChat",
            topics: ["general", "intros"]
        },

        {
            title: "PrivateRoomTest",
            private: true,
            name: "bothelp/PrivateRoomTest",
            topics: ["general", "intros"]
        }

    ],

    camperbot: [

        {
            title: "Botdiscussion",
            name: "dcsan/botzy",
            topics: ['bots', 'fcc', 'teaching']
        },

        {
            title: "Help Bonfires",
            name: "camperbot/testing",
            topics: bonfireTopics
        },

        {
            title: "Help Bonfires",
            name: "camperbot/HelpBonfires",
            topics: bonfireTopics
        },

        {
            title: "Help ZipLines",
            name: "camperbot/HelpZiplines",
            topics: ["ziplines"]
        },

        {
            title: "Botdiscussion",
            name: "dcsan/botzy",
            topics: ['bots', 'fcc', 'teaching']
        },

        {
            title: "CoreTeam",
            name: "FreeCodeCamp/CoreTeam",
            private: true,
            topics: ['bots', 'fcc']
        }

    ]

};

var botname = null;

RoomData = {
    rooms: function() {
        botname = botname || AppConfig.getBotName();
        return BotRoomData[botname];
    },

    defaultRoom: function() {
        return RoomData.rooms().rooms[0];
    }

};

module.exports = RoomData;

<commit_msg>roomdata for bonfires<commit_after>"use strict";

// TODO - move to lib/ dir?

var AppConfig = require('../config/AppConfig');

// var Bonfires = require('../lib/app/Bonfires');

// from the webapp
// users enter the rooms with a topic=XXX url
// we find a matching room here with that topic
// and redirect them

var RoomData;

// TODO - read this from the JSON file
var bonfireTopics = [
    "bonfires",
    "Pair Program on Bonfires",
    "Meet Bonfire",
    "Reverse a String",
    "Factorialize a Number",
    "Check for Palindromes",
    "Find the Longest Word in a String",
    "Title Case a Sentence",
    "Return Largest Numbers in Arrays",
    "Confirm the Ending",
    "Repeat a string repeat a string",
    "Truncate a string",
    "Chunky Monkey",
    "Slasher Flick",
    "Mutations",
    "Falsey Bouncer",
    "Where art thou",
    "Seek and Destroy",
    "Where do I belong",
    "Sum All Numbers in a Range",
    "Diff Two Arrays",
    "Roman Numeral Converter",
    "Search and Replace",
    "Pig Latin",
    "DNA Pairing",
    "Missing letters",
    "Boo who",
    "Sorted Union",
    "Convert HTML Entities",
    "Spinal Tap Case",
    "Sum All Odd Fibonacci Numbers",
    "Sum All Primes",
    "Smallest Common Multiple",
    "Finders Keepers",
    "Drop it",
    "Steamroller",
    "Binary Agents",
    "Everything Be True",
    "Arguments Optional"
];

var BotRoomData = {

    // developer bot
    bothelp: [
        {
            title: "bothelp",
            name: "bothelp/testing",
            icon: "question",
            topics: ["chitchat", "bots", "bot-development", "camperbot"]
        },

        {
            title: "HelpBonfires",
            icon: "fire",
            name: "bothelp/HelpBonfires",
            topics: bonfireTopics
        },

        {
            title: "dev2",
            name: "bothelp/dev2",
            topics: ["dev2"]
        },

        {
            title: "bothelpDM",
            name: "bothelp",
            topics: ["chitchat", "dm"]
        },

        {
            title: "GeneralChat",
            name: "bothelp/GeneralChat",
            topics: ["general", "intros"]
        },

        {
            title: "PrivateRoomTest",
            private: true,
            name: "bothelp/PrivateRoomTest",
            topics: ["general", "intros"]
        }

    ],

    camperbot: [

        {
            title: "Botdiscussion",
            name: "dcsan/botzy",
            topics: ['bots', 'fcc', 'teaching']
        },

        {
            title: "Help Bonfires",
            name: "camperbot/testing",
            topics: ["testing", "bots"]
        },

        {
            title: "Help Bonfires",
            name: "camperbot/HelpBonfires",
            topics: bonfireTopics
        },

        {
            title: "Help ZipLines",
            name: "camperbot/HelpZiplines",
            topics: ["ziplines"]
        },

        {
            title: "Botdiscussion",
            name: "dcsan/botzy",
            topics: ['bots', 'fcc', 'teaching']
        },

        {
            title: "CoreTeam",
            name: "FreeCodeCamp/CoreTeam",
            private: true,
            topics: ['bots', 'fcc']
        }

    ]

};

var botname = null;

RoomData = {
    rooms: function() {
        botname = botname || AppConfig.getBotName();
        return BotRoomData[botname];
    },

    defaultRoom: function() {
        return RoomData.rooms().rooms[0];
    }

};

module.exports = RoomData;

<|endoftext|>


<commit_before>/* eslint-env node, jest */

const ahokusa = require('./index.js');
const Slack = require('../lib/slackMock.js');

let slack = null;

beforeEach(() => {
	slack = new Slack();
	process.env.CHANNEL_SANDBOX = slack.fakeChannel;
	ahokusa(slack);
});

describe('ahokusa', () => {
	it('responds to あほくさスライドパズル', async () => {
		const {text, username} = await slack.getResponseTo('あほくさスライドパズル');

		expect(username).toBe('ahokusa');
		expect(text).toContain(':void:');
		expect(text).toMatch(/^(:[a-z-]+:\n?){6}$/);
	});

	it('accepts valid board initialization by emojis', async () => {
		const board = [
			':void::ahokusa-bottom-center::ahokusa-top-center:',
			':ahokusa-bottom-left::ahokusa-top-left::ahokusa-top-right:',
		].join('\n');

		const {text, username} = await slack.getResponseTo(`@ahokusa ${board}`);

		expect(username).toBe('ahokusa');
		expect(text).toBe(board);
	});

	it('accepts valid board initialization by letters', async () => {
		const {text, username} = await slack.getResponseTo('@ahokusa .#_さくあ');

		expect(username).toBe('ahokusa');
		expect(text).toBe([
			':void::ahokusa-bottom-center::ahokusa-top-center:',
			':ahokusa-bottom-left::ahokusa-top-left::ahokusa-top-right:',
		].join('\n'));
	});

	it('rejects invalid board initialization', async () => {
		const {text, username} = await slack.getResponseTo('@ahokusa ああああああ');

		expect(username).toBe('ahokusa');
		expect(text).toBe(':ha:');
	});

	it('rejects invalid board initialization', async () => {
		const {text, username} = await slack.getResponseTo('@ahokusa .#_さくああああああああああああああああああああ');

		expect(username).toBe('ahokusa');
		expect(text).toBe(':ha:');
	});

	it('responds to 寿司スライドパズル', async () => {
		const {text, username} = await slack.getResponseTo('寿司スライドパズル');

		expect(username).toBe('sushi-puzzle');
		expect(text).toContain(':void:');
		expect(text).toContain('sushi');
		expect(text).toMatch(/^(:[a-z-]+:\n?)+$/);
	});
});
<commit_msg>Fix sushi-puzzle test<commit_after>/* eslint-env node, jest */

const ahokusa = require('./index.js');
const Slack = require('../lib/slackMock.js');

let slack = null;

beforeEach(() => {
	slack = new Slack();
	process.env.CHANNEL_SANDBOX = slack.fakeChannel;
	ahokusa(slack);
});

describe('ahokusa', () => {
	it('responds to あほくさスライドパズル', async () => {
		const {text, username} = await slack.getResponseTo('あほくさスライドパズル');

		expect(username).toBe('ahokusa');
		expect(text).toContain(':void:');
		expect(text).toMatch(/^(:[a-z-]+:\n?){6}$/);
	});

	it('accepts valid board initialization by emojis', async () => {
		const board = [
			':void::ahokusa-bottom-center::ahokusa-top-center:',
			':ahokusa-bottom-left::ahokusa-top-left::ahokusa-top-right:',
		].join('\n');

		const {text, username} = await slack.getResponseTo(`@ahokusa ${board}`);

		expect(username).toBe('ahokusa');
		expect(text).toBe(board);
	});

	it('accepts valid board initialization by letters', async () => {
		const {text, username} = await slack.getResponseTo('@ahokusa .#_さくあ');

		expect(username).toBe('ahokusa');
		expect(text).toBe([
			':void::ahokusa-bottom-center::ahokusa-top-center:',
			':ahokusa-bottom-left::ahokusa-top-left::ahokusa-top-right:',
		].join('\n'));
	});

	it('rejects invalid board initialization', async () => {
		const {text, username} = await slack.getResponseTo('@ahokusa ああああああ');

		expect(username).toBe('ahokusa');
		expect(text).toBe(':ha:');
	});

	it('rejects invalid board initialization', async () => {
		const {text, username} = await slack.getResponseTo('@ahokusa .#_さくああああああああああああああああああああ');

		expect(username).toBe('ahokusa');
		expect(text).toBe(':ha:');
	});

	it('responds to 寿司スライドパズル', async () => {
		const {text, username} = await slack.getResponseTo('寿司スライドパズル');

		expect(username).toBe('sushi-puzzle');
		expect(text).toContain(':void:');
		expect(text).toContain('sushi');
		expect(text).toMatch(/^(:[a-z_\d-]+:\n?)+$/);
	});
});
<|endoftext|>


<commit_before>/*!
 * Socialite v2.0 - Plain FB Share Extension
 * Copyright (c) 2013 Dan Drinkard
 * Dual-licensed under the BSD or MIT licenses: http://socialitejs.com/license.txt
 */
(function(window, document, Socialite, undefined)
{

    /**
     * FB Share is no longer supported, but params are:
     * u | data-url    | URL to share
     * t | data-title  | Title to share
     *
     * Others may work, but that will come later. For now just set OG tags.
     *
     */

    function addEvent(obj, evt, fn, capture) {
        if (window.attachEvent) {
            obj.attachEvent("on" + evt, fn);
        }
        else {
            if (!capture) capture = false; // capture
            obj.addEventListener(evt, fn, capture);
        }
    }

    Socialite.widget('facebook', 'share', {
        init: function(instance) {
            var el = document.createElement('a'),
                href = "//www.facebook.com/share.php?",
                attrs = Socialite.getDataAttributes(instance.el, true, true);

            el.className = instance.widget.name;
            Socialite.copyDataAttributes(instance.el, el);
            if(attrs.url) href += 'u=' + encodeURIComponent(attrs.url);
            if(attrs['title']) href += '&t=' + encodeURIComponent(attrs['title']);
            href += '&' + Socialite.getDataAttributes(el, true);
            el.setAttribute('href', href);
            el.setAttribute('data-lang', instance.el.getAttribute('data-lang') || Socialite.settings.facebook.lang);
            if (instance.el.getAttribute('data-image')) {
                imgTag = document.createElement('img');
                imgTag.src = instance.el.getAttribute('data-image');
                el.appendChild(imgTag);
            }
            addEvent(el, 'click', function(e){
                var t = e? e.target : window.event.srcElement;
                e.preventDefault();
                window.open(el.getAttribute('href'), 'fb-share', 'left=' + (screen.availWidth/2 - 350) + ',top=' + (screen.availHeight/2 - 163) + ',height=325,width=700,menubar=0,resizable=0,status=0,titlebar=0');
            });
            instance.el.appendChild(el);
        },
        activate: function(){}
    });

})(window, window.document, window.Socialite);

<commit_msg>Add share counts for facebook<commit_after>/*!
 * Socialite v2.0 - Plain FB Share Extension
 * Copyright (c) 2013 Dan Drinkard
 * Dual-licensed under the BSD or MIT licenses: http://socialitejs.com/license.txt
 */
(function(window, document, Socialite, undefined)
{

    /**
     * FB Share is no longer supported, but params are:
     * u | data-url    | URL to share
     * t | data-title  | Title to share
     *
     * Others may work, but that will come later. For now just set OG tags.
     *
     */

    var addEvent = function(obj, evt, fn, capture) {
        if (window.attachEvent) {
            obj.attachEvent("on" + evt, fn);
        }
        else {
            if (!capture) capture = false; // capture
            obj.addEventListener(evt, fn, capture);
        }
    },

    facebookGetCount = function(instance, options) {
        var params = getParams(instance),
            counturl = params['counturl'] || params['url'],
            callback = 'socialiteFacebookShare' + instance.uid,
            endpoint = "https://graph.facebook.com/fql?q=" + encodeURIComponent("select total_count from link_stat where url = '") + counturl + "'&callback=" + callback,
            options = options || {},
            scriptTag = document.createElement('script');
            scriptTag.src = endpoint;
            document.getElementsByTagName('script')[0].insertBefore(scriptTag);
            window[callback] = function(data){
                instance.el.querySelectorAll('.counter')[0].innerHTML = data.data[0].total_count;
                delete window[callback];
                scriptTag.parentNode.removeChild(scriptTag);
            }
    },

    getParams = function(instance) {
        return parseQS(Socialite.getDataAttributes(instance.el, true));
    },

    parseQS = function(qs) {
        var hsh = {},
            parts = qs.split(/[&=]/);
        for(var i in parts) {

            if  (i%2) {
                hsh[parts[i-1]] = decodeURIComponent(parts[i]);
            };
        };
        return hsh;
    },

    appendStyleSheet = function() {
        (document.querySelectorAll('style.custom-count-widgets').length || (function(){
            var styleTag = document.createElement('style');
            styleTag.className = 'custom-count-widgets';
            styleTag.innerHTML = '.socialite .counter {position:relative; margin-left: 5px; vertical-align: middle; display:inline-block; min-width:6px; border:1px solid #e6e5e3; padding:6px 7px; font-family:"helvetica neue", helvetica, arial, sans-serif; font-size:13px; line-height:1.2; color:#222; text-decoration: none; /*border-radius: 2px;*/ }' +
                                 '.socialite .counter:before { content:""; position:absolute; left: -5px; top: 10px; background:#fff; border:1px solid #e6e5e3; border-right:none; border-top:none; display:block; width:7px; height:6px; -webkit-transform: rotate(61deg) skewX(35deg); -moz-transform: rotate(61deg) skewX(35deg); -o-transform: rotate(61deg) skewX(35deg); -ms-transform: rotate(61deg) skewX(35deg); transform: rotate(61deg) skewX(35deg); }' +
                                 '.socialite img { vertical-align: middle; }';
            document.getElementsByTagName('head')[0].appendChild(styleTag);
        })());
    }

    Socialite.widget('facebook', 'share', {
        init: function(instance) {
            var el = document.createElement('a'),
                href = "//www.facebook.com/share.php?",
                attrs = Socialite.getDataAttributes(instance.el, true, true);

            el.className = instance.widget.name;
            Socialite.copyDataAttributes(instance.el, el);
            if(attrs.url) href += 'u=' + encodeURIComponent(attrs.url);
            if(attrs['title']) href += '&t=' + encodeURIComponent(attrs['title']);
            href += '&' + Socialite.getDataAttributes(el, true);
            el.setAttribute('href', href);
            el.setAttribute('data-lang', instance.el.getAttribute('data-lang') || Socialite.settings.facebook.lang);
            if (instance.el.getAttribute('data-image')) {
                imgTag = document.createElement('img');
                imgTag.src = instance.el.getAttribute('data-image');
                el.appendChild(imgTag);
            }
            if (instance.el.getAttribute('data-show-counts')) {
                var counterTag = document.createElement('span')
                counterTag.className = 'counter';
                counterTag.innerHTML = '…';
                el.appendChild(counterTag);
            }
            addEvent(el, 'click', function(e){
                var t = e? e.target : window.event.srcElement;
                e.preventDefault();
                window.open(el.getAttribute('href'), 'fb-share', 'left=' + (screen.availWidth/2 - 350) + ',top=' + (screen.availHeight/2 - 163) + ',height=325,width=700,menubar=0,resizable=0,status=0,titlebar=0');
            });
            instance.el.appendChild(el);
        },
        activate: function(instance){
            if (getParams(instance)['show-counts']) {
                appendStyleSheet();
                facebookGetCount(instance);
            }
        }
    });

})(window, window.document, window.Socialite);

<|endoftext|>