<commit_before>OrderItems = new Mongo.Collection("order_items");
Orders = new Mongo.Collection("orders");

function randomHash() {
  var hex = parseInt(Math.random() * 0xfff).toString(16);
  return ("000" + hex).slice(-3); // pad with 3 zeros
}

if (Meteor.isClient) {
  var hash = window.location.hash;

  Meteor.subscribe('orders', function() {
    var order, error = false;

    if (hash) {
      hash = hash.substring(1).toLowerCase();
    }

    order = Orders.findOne({'hash': hash});
    if (hash && !order) {
      error = true;
    }
    Session.set('error', error);

    if (!order && !error) {
      console.log('Creating new order!');
      do {
        hash = randomHash();
        order = Orders.findOne({'hash': hash});
      } while (order !== undefined);
      var id = Orders.insert({
        'hash': hash,
        createdAt: new Date()
      });
      order = Orders.findOne({'hash': hash});
      window.location.hash = hash;
    }
    if (order) {
      Session.set('order', order._id);
      Session.set('url', window.location.href);
    }
  });
  Session.set('url', window.location.href);

  Template.body.helpers({
    url: function() {
      return Session.get('url');
    },
    items: function () {
      var items = OrderItems.find({order: Session.get('order')}).fetch();
      var pizzas = _.groupBy(items, function(item) { return item.pizza });

      pizzas = _.map(pizzas, function(value, key) {
        value.name = key;
        return value;
      });
      return _.sortBy(pizzas, function(pizza){ return -pizza.length; });
    },
    error: function() {
      return Session.get('error');
    },
    timer_started: timer_started
  });

  Template.body.events({
    "submit .new-pizza": function (event) {
      var pizza = event.target,
          pizzaName = pizza.pizza.value.trim(),
          nick = pizza.nick.value.trim();
          // comment = pizza.comment.value.trim();
      if (!pizzaName || !nick) {
        return false;
      }
      OrderItems.insert({
        order: Session.get('order'),
        pizza: pizza.pizza.value.trim(),
        nick: pizza.nick.value.trim(),
        // comment: pizza.comment.value.trim(),
        createdAt: new Date()
      });
      pizza.pizza.value = '';
      // pizza.nick.value = '';
      // pizza.comment.value = '';

      return false;
    },
    "click .pizza-item .name": function(event) {
      var name = $(event.target).data('name');
      $('.new-pizza [name="pizza"]').val(name).next().focus();
    },
    "click .pizza-item small.deletable": function(event) {
      var id = $(event.target).data('id');
      OrderItems.remove({_id: id});
    }
  });
  function time_left() {
    var date = new Date();
    var order = Orders.findOne(Session.get('order'));
    var seconds = parseInt((order.timer_end - date) / 1000);
    var mins = parseInt(seconds / 60);
    seconds = seconds % 60;
    return [mins, seconds];
  }
  function timer_started() {
      var order = Orders.findOne(Session.get('order'));
      return Boolean(order && order.timer_end);
  }
  function format_time(time) {
    var prefix = '';
    if (time[1] < 0) {
      prefix = '-';
    }
    time[0] = Math.abs(time[0]);
    time[1] = Math.abs(time[1]);
    if (time[1] < 10) {
        time[1] = '0' + time[1];
    }
    if (isNaN(time[0])) {
      return '';
    }

    return prefix + time.join(':');
  }
  Template.pizza.helpers({
    classname: function() {
      return timer_started() ? '' : 'deletable';
    }
  });
  Template.timer.created = function() {
    this.handle = Meteor.setInterval(function() {
      var time = time_left();
      if (time[0] === 0 && time[1] === 0 || (time[1] < 0 && !window.playing)) {
        if (player && player.getPlayerState() === YT.PlayerState.CUED) {
          console.log('stop!');
          document.body.className += ' animated';
          window.playing = true;
          player.seekTo(51);
        }
        // Meteor.clearInterval(this.handle);
      }
      Session.set('time_left', format_time(time));
    }.bind(this), 500);
  };
  Template.timer.helpers({
    display_time: function() {
      return Session.get('time_left');
    },
    timer_started: timer_started
  });
  Template.timer.events({
    "submit .start-timer": function(event) {
      var mins = parseInt(event.target.minutes.value.trim());
      var end_date = (new Date()).valueOf() + mins * 60000;
      Orders.update(Session.get('order'), { $set: {timer_end: end_date}})
      return false;
    }
  });
  Template.swish.helpers({
      name_placeholder: "Name (optional)",
      name: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishName;
          }
      },
      number_placeholder: "Swish number",
      number: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishNbr;
          }
      },

      has_name: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishName !== undefined;
          }
          return false;
      },

      has_number: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishNbr !== undefined;
          }
          return false;
      }
  })
  Template.swish.events({
      "submit .submit-swish": function(event) {
          var name = event.target.swishName.value.trim();
          var number = event.target.swishNbr.value.trim();
          if (name.length == 0) {
              Orders.update(Session.get('order'), { $unset: {swishName: ""}});
          } else {
              Orders.update(Session.get('order'), { $set: {swishName: name}});
          }
          if (number.length == 0) {
              Orders.update(Session.get('order'), { $unset: {swishNbr: ""}});
          } else {
              Orders.update(Session.get('order'), { $set: {swishNbr: number}});
          }
          return false;
      }
  })
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    Meteor.publish('orders', function() {
      return Orders.find();
    });
    Meteor.publish('order_items', function() {
      return OrderItems.find();
    });
    Meteor.setInterval(function() {
      var time = new Date() - 10800000; // 3 hours
      Orders.remove({createdAt: { $lt: time }});
      OrderItems.remove({createdAt: { $lt: time }});
    }, 600000); // every 10th minute
  });
}
<commit_msg>Added missing semicolon.<commit_after>OrderItems = new Mongo.Collection("order_items");
Orders = new Mongo.Collection("orders");

function randomHash() {
  var hex = parseInt(Math.random() * 0xfff).toString(16);
  return ("000" + hex).slice(-3); // pad with 3 zeros
}

if (Meteor.isClient) {
  var hash = window.location.hash;

  Meteor.subscribe('orders', function() {
    var order, error = false;

    if (hash) {
      hash = hash.substring(1).toLowerCase();
    }

    order = Orders.findOne({'hash': hash});
    if (hash && !order) {
      error = true;
    }
    Session.set('error', error);

    if (!order && !error) {
      console.log('Creating new order!');
      do {
        hash = randomHash();
        order = Orders.findOne({'hash': hash});
      } while (order !== undefined);
      var id = Orders.insert({
        'hash': hash,
        createdAt: new Date()
      });
      order = Orders.findOne({'hash': hash});
      window.location.hash = hash;
    }
    if (order) {
      Session.set('order', order._id);
      Session.set('url', window.location.href);
    }
  });
  Session.set('url', window.location.href);

  Template.body.helpers({
    url: function() {
      return Session.get('url');
    },
    items: function () {
      var items = OrderItems.find({order: Session.get('order')}).fetch();
      var pizzas = _.groupBy(items, function(item) { return item.pizza });

      pizzas = _.map(pizzas, function(value, key) {
        value.name = key;
        return value;
      });
      return _.sortBy(pizzas, function(pizza){ return -pizza.length; });
    },
    error: function() {
      return Session.get('error');
    },
    timer_started: timer_started
  });

  Template.body.events({
    "submit .new-pizza": function (event) {
      var pizza = event.target,
          pizzaName = pizza.pizza.value.trim(),
          nick = pizza.nick.value.trim();
          // comment = pizza.comment.value.trim();
      if (!pizzaName || !nick) {
        return false;
      }
      OrderItems.insert({
        order: Session.get('order'),
        pizza: pizza.pizza.value.trim(),
        nick: pizza.nick.value.trim(),
        // comment: pizza.comment.value.trim(),
        createdAt: new Date()
      });
      pizza.pizza.value = '';
      // pizza.nick.value = '';
      // pizza.comment.value = '';

      return false;
    },
    "click .pizza-item .name": function(event) {
      var name = $(event.target).data('name');
      $('.new-pizza [name="pizza"]').val(name).next().focus();
    },
    "click .pizza-item small.deletable": function(event) {
      var id = $(event.target).data('id');
      OrderItems.remove({_id: id});
    }
  });
  function time_left() {
    var date = new Date();
    var order = Orders.findOne(Session.get('order'));
    var seconds = parseInt((order.timer_end - date) / 1000);
    var mins = parseInt(seconds / 60);
    seconds = seconds % 60;
    return [mins, seconds];
  }
  function timer_started() {
      var order = Orders.findOne(Session.get('order'));
      return Boolean(order && order.timer_end);
  }
  function format_time(time) {
    var prefix = '';
    if (time[1] < 0) {
      prefix = '-';
    }
    time[0] = Math.abs(time[0]);
    time[1] = Math.abs(time[1]);
    if (time[1] < 10) {
        time[1] = '0' + time[1];
    }
    if (isNaN(time[0])) {
      return '';
    }

    return prefix + time.join(':');
  }
  Template.pizza.helpers({
    classname: function() {
      return timer_started() ? '' : 'deletable';
    }
  });
  Template.timer.created = function() {
    this.handle = Meteor.setInterval(function() {
      var time = time_left();
      if (time[0] === 0 && time[1] === 0 || (time[1] < 0 && !window.playing)) {
        if (player && player.getPlayerState() === YT.PlayerState.CUED) {
          console.log('stop!');
          document.body.className += ' animated';
          window.playing = true;
          player.seekTo(51);
        }
        // Meteor.clearInterval(this.handle);
      }
      Session.set('time_left', format_time(time));
    }.bind(this), 500);
  };
  Template.timer.helpers({
    display_time: function() {
      return Session.get('time_left');
    },
    timer_started: timer_started
  });
  Template.timer.events({
    "submit .start-timer": function(event) {
      var mins = parseInt(event.target.minutes.value.trim());
      var end_date = (new Date()).valueOf() + mins * 60000;
      Orders.update(Session.get('order'), { $set: {timer_end: end_date}})
      return false;
    }
  });
  Template.swish.helpers({
      name_placeholder: "Name (optional)",
      name: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishName;
          }
      },
      number_placeholder: "Swish number",
      number: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishNbr;
          }
      },

      has_name: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishName !== undefined;
          }
          return false;
      },

      has_number: function() {
          var order = Orders.findOne(Session.get('order'));
          if (order) {
              return order.swishNbr !== undefined;
          }
          return false;
      }
  });
  Template.swish.events({
      "submit .submit-swish": function(event) {
          var name = event.target.swishName.value.trim();
          var number = event.target.swishNbr.value.trim();
          if (name.length == 0) {
              Orders.update(Session.get('order'), { $unset: {swishName: ""}});
          } else {
              Orders.update(Session.get('order'), { $set: {swishName: name}});
          }
          if (number.length == 0) {
              Orders.update(Session.get('order'), { $unset: {swishNbr: ""}});
          } else {
              Orders.update(Session.get('order'), { $set: {swishNbr: number}});
          }
          return false;
      }
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    Meteor.publish('orders', function() {
      return Orders.find();
    });
    Meteor.publish('order_items', function() {
      return OrderItems.find();
    });
    Meteor.setInterval(function() {
      var time = new Date() - 10800000; // 3 hours
      Orders.remove({createdAt: { $lt: time }});
      OrderItems.remove({createdAt: { $lt: time }});
    }, 600000); // every 10th minute
  });
}
<|endoftext|>


<commit_before>/*
 * tuxedo
 * https://github.com/goliatone/tuxedo
 * Created with gbase.
 * Copyright (c) 2014 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
var Handlebars = require('handlebars'),
    fs = require('fs'),
    util = require('util'),
    helpers = require('./helpers'),
    momentHelper = require('handlebars-helper-moment')(),
    Events = require('events').EventEmitter,
    faker = require('faker');

var extend = Handlebars.Utils.extend;

var CoreHelpers = {};

extend(CoreHelpers, faker.Date);
extend(CoreHelpers, faker.Name);
extend(CoreHelpers, faker.Image);
extend(CoreHelpers, faker.Lorem);
extend(CoreHelpers, faker.random);
extend(CoreHelpers, faker.Address);
extend(CoreHelpers, faker.Company);
extend(CoreHelpers, faker.Internet);
extend(CoreHelpers, faker.PhoneNumber);

extend(CoreHelpers, momentHelper);

extend(CoreHelpers, helpers);

var Tuxedo = function(){
    Events.call(this);
};
util.inherits(Tuxedo, Events);

Tuxedo.prototype.fromFile = function(path, options, callback) {
    options || (options = {});

    fs.readFile(path, {
        encoding: 'utf8'
    }, function(err, template) {
        if (err) {
            this.emit('error', err);
            callback && callback(err, null);
        }
        var result = this.parse(template, options.data);

        this.emit('complete', result);

        callback && callback(null, result);

    }.bind(this));
};

Tuxedo.prototype.parse = function(string, options) {
    options || (options = {});

    var mergeHelpers = {};
    Handlebars.Utils.extend(mergeHelpers, CoreHelpers);
    Handlebars.Utils.extend(mergeHelpers, options.helpers);

    // Reset indexes on each parse
    return Handlebars.compile(string)(options.data, {
        helpers: mergeHelpers
    });
};

module.exports = new Tuxedo();<commit_msg>tuxedo.js: Working on random element of array helper<commit_after>/*
 * tuxedo
 * https://github.com/goliatone/tuxedo
 * Created with gbase.
 * Copyright (c) 2014 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
var Handlebars = require('handlebars'),
    fs = require('fs'),
    util = require('util'),
    helpers = require('./helpers'),
    momentHelper = require('handlebars-helper-moment')(),
    Events = require('events').EventEmitter,
    faker = require('faker');

var extend = Handlebars.Utils.extend;

var CoreHelpers = {
    randomElementOf:function(path, options){
        return faker.random.array_element(path);
    }
};

extend(CoreHelpers, faker.Date);
extend(CoreHelpers, faker.Name);
extend(CoreHelpers, faker.Image);
extend(CoreHelpers, faker.Lorem);
extend(CoreHelpers, faker.random);
extend(CoreHelpers, faker.Address);
extend(CoreHelpers, faker.Company);
extend(CoreHelpers, faker.Internet);
extend(CoreHelpers, faker.PhoneNumber);

extend(CoreHelpers, momentHelper);

extend(CoreHelpers, helpers);

var Tuxedo = function(){
    Events.call(this);
};
util.inherits(Tuxedo, Events);

Tuxedo.prototype.fromFile = function(path, options, callback) {
    options || (options = {});

    fs.readFile(path, {
        encoding: 'utf8'
    }, function(err, template) {
        if (err) {
            this.emit('error', err);
            callback && callback(err, null);
        }
        var result = this.parse(template, options);

        this.emit('complete', result);

        callback && callback(null, result);

    }.bind(this));
};

Tuxedo.prototype.parse = function(string, options) {
    options || (options = {});

    var mergeHelpers = {};
    Handlebars.Utils.extend(mergeHelpers, CoreHelpers);
    Handlebars.Utils.extend(mergeHelpers, options.helpers);

    // Reset indexes on each parse
    return Handlebars.compile(string)(options.data, {
        helpers: mergeHelpers
    });
};

module.exports = new Tuxedo();<|endoftext|>


<commit_before>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Generating Python for text blocks.
 * @author fraser@google.com (Neil Fraser)
 * Due to the frequency of long strings, the 80-column wrap rule need not apply
 * to language files.
 */

Blockly.Python = Blockly.Generator.get('Python');

Blockly.Python.text = function() {
  // Text value.
  return Blockly.Python.quote_(this.getTitleText('TEXT'));
};

Blockly.Python.text_join = function(opt_dropParens) {
  // Create a string made up of any number of elements of any type.
  //Should we allow joining by '-' or ',' or any other characters?
  if (this.itemCount_ == 0) {
    return '\'\'';
  } else if (this.itemCount_ == 1) {
    return 'str(' + (Blockly.Python.valueToCode(this, 'ADD0', opt_dropParens) || '\'\'') + ')';
  } else if (this.itemCount_ == 2) {
    var argument0 = Blockly.Python.valueToCode(this, 'ADD0', true) || '\'\'';
    var argument1 = Blockly.Python.valueToCode(this, 'ADD1', true) || '\'\'';
    var code = 'str(' + argument0 + ') + str(' + argument1 + ')';
    if (!opt_dropParens) {
      code = '(' + code + ')';
    }
    return code;
  } else {
    var code = [];
    for (n = 0; n < this.itemCount_; n++) {
      code[n] = Blockly.Python.valueToCode(this, 'ADD' + n, true) || '\'\'';
    }
    var tempVar = Blockly.Python.variableDB_.getDistinctName('temp_value',
        Blockly.Variables.NAME_TYPE);
    code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + code.join(', ') + ']])';
    if (!opt_dropParens) {
      code = '(' + code + ')';
    }
    return code;
  }
};

Blockly.Python.text_length = function() {
  // String length.
  var argument0 = Blockly.Python.valueToCode(this, 'VALUE', true) || '\'\'';
  return 'len(' + argument0 + ')';
};

Blockly.Python.text_isEmpty = function(opt_dropParens) {
  // Is the string null?
  var argument0 = Blockly.Python.valueToCode(this, 'VALUE', true) || '\'\'';
  var code = 'not len(' + argument0 + ')';
  if (!opt_dropParens) {
    code = '(' + code + ')';
  }
  return code;
};

Blockly.Python.text_endString = function() {
  // Return a leading or trailing substring.
  // Do we need to prevent 'List index out of range' ERROR by checking
  // if argument 0 > len(argument1)? Or will ALL error be handled systematically?
  var first = this.getInputLabelValue('NUM') == 'FIRST';
  var argument0 = Blockly.Python.valueToCode(this, 'NUM', true) || '1';
  var argument1 = Blockly.Python.valueToCode(this, 'TEXT') || '\'\'';
  var code = argument1 + '[' +
      (first ? ':' + argument0 : '-' + argument0 + ':') + ']';
  return code;
};

Blockly.Python.text_indexOf = function(opt_dropParens) {
  // Search the text for a substring.
  // Should we allow for non-case sensitive???
  var operator = this.getTitleValue('END') == 'FIRST' ? 'find' : 'rfind';
  var argument0 = Blockly.Python.valueToCode(this, 'FIND') || '\'\'';
  var argument1 = Blockly.Python.valueToCode(this, 'VALUE') || '\'\'';
  var code = argument1 + '.' + operator + '(' + argument0 + ') + 1';
  if (!opt_dropParens) {
    code = '(' + code + ')';
  }
  return code;
};

Blockly.Python.text_charAt = function() {
  // Get letter at index.
  var argument0 = Blockly.Python.valueToCode(this, 'AT', true) || '1';
  var argument1 = Blockly.Python.valueToCode(this, 'VALUE') || '[]';
  // Blockly uses one-based indicies.
  if (argument0.match(/^\d+$/)) {
    // If the index is a naked number, decrement it right now.
    // Except not allowing negative index by constraining at 0.
    argument0 = Math.max(0, parseInt(argument0, 10) - 1);
  } else {
    // If the index is dynamic, decrement it in code.
    argument0 += ' - 1';
  }
  return argument1 + '[' + argument0 + ']';
};

Blockly.Python.text_changeCase = function() {
  // Change capitalization.
  var mode = this.getInputLabelValue('TEXT');
  var operator = Blockly.JavaScript.text_changeCase.OPERATORS[mode];
  var argument0 = Blockly.Python.valueToCode(this, 'TEXT', true) || '\'\'';
  var code = argument0 + operator;
  return code;
};

Blockly.Python.text_changeCase.OPERATORS = {
  UPPERCASE: '.upper()',
  LOWERCASE: '.lower()',
  TITLECASE: '.title()'
};

Blockly.Python.text_trim = function() {
  // Trim spaces.
  var mode = this.getTitleValue('MODE');
  var operator = Blockly.JavaScript.text_trim.OPERATORS[mode];
  var argument0 = Blockly.Python.valueToCode(this, 'TEXT') || '\'\'';
  return argument0 + operator;
};

Blockly.Python.text_trim.OPERATORS = {
  LEFT: '.lstrip()',
  RIGHT: '.rstrip()',
  BOTH: '.strip()'
};

Blockly.Python.text_print = function() {
  // Print statement.
  var argument0 = Blockly.Python.valueToCode(this, 'TEXT', true) || '\'\'';
  return 'print ' + argument0 + '\n';
};

Blockly.Python.text_prompt = function() {
  // Prompt function.
  var msg = Blockly.Python.quote_(this.getTitleValue('TEXT'));
  var code = 'raw_input(' + msg + ')';
  var toNumber = this.getTitleValue('TYPE') == 'NUMBER';
  if (toNumber) {
    code = 'float(' + code + ')';
  }
  return code;
};
<commit_msg>Fix Python generation errors in text blocks.<commit_after>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Generating Python for text blocks.
 * @author fraser@google.com (Neil Fraser)
 * Due to the frequency of long strings, the 80-column wrap rule need not apply
 * to language files.
 */

Blockly.Python = Blockly.Generator.get('Python');

Blockly.Python.text = function() {
  // Text value.
  return Blockly.Python.quote_(this.getTitleText('TEXT'));
};

Blockly.Python.text_join = function(opt_dropParens) {
  // Create a string made up of any number of elements of any type.
  //Should we allow joining by '-' or ',' or any other characters?
  if (this.itemCount_ == 0) {
    return '\'\'';
  } else if (this.itemCount_ == 1) {
    return 'str(' + (Blockly.Python.valueToCode(this, 'ADD0', opt_dropParens) || '\'\'') + ')';
  } else if (this.itemCount_ == 2) {
    var argument0 = Blockly.Python.valueToCode(this, 'ADD0', true) || '\'\'';
    var argument1 = Blockly.Python.valueToCode(this, 'ADD1', true) || '\'\'';
    var code = 'str(' + argument0 + ') + str(' + argument1 + ')';
    if (!opt_dropParens) {
      code = '(' + code + ')';
    }
    return code;
  } else {
    var code = [];
    for (n = 0; n < this.itemCount_; n++) {
      code[n] = Blockly.Python.valueToCode(this, 'ADD' + n, true) || '\'\'';
    }
    var tempVar = Blockly.Python.variableDB_.getDistinctName('temp_value',
        Blockly.Variables.NAME_TYPE);
    code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + code.join(', ') + ']])';
    if (!opt_dropParens) {
      code = '(' + code + ')';
    }
    return code;
  }
};

Blockly.Python.text_length = function() {
  // String length.
  var argument0 = Blockly.Python.valueToCode(this, 'VALUE', true) || '\'\'';
  return 'len(' + argument0 + ')';
};

Blockly.Python.text_isEmpty = function(opt_dropParens) {
  // Is the string null?
  var argument0 = Blockly.Python.valueToCode(this, 'VALUE', true) || '\'\'';
  var code = 'not len(' + argument0 + ')';
  if (!opt_dropParens) {
    code = '(' + code + ')';
  }
  return code;
};

Blockly.Python.text_endString = function() {
  // Return a leading or trailing substring.
  // Do we need to prevent 'List index out of range' ERROR by checking
  // if argument 0 > len(argument1)? Or will ALL error be handled systematically?
  var first = this.getInputLabelValue('NUM') == 'FIRST';
  var argument0 = Blockly.Python.valueToCode(this, 'NUM', true) || '1';
  var argument1 = Blockly.Python.valueToCode(this, 'TEXT') || '\'\'';
  var code = argument1 + '[' +
      (first ? ':' + argument0 : '-' + argument0 + ':') + ']';
  return code;
};

Blockly.Python.text_indexOf = function(opt_dropParens) {
  // Search the text for a substring.
  // Should we allow for non-case sensitive???
  var operator = this.getTitleValue('END') == 'FIRST' ? 'find' : 'rfind';
  var argument0 = Blockly.Python.valueToCode(this, 'FIND') || '\'\'';
  var argument1 = Blockly.Python.valueToCode(this, 'VALUE') || '\'\'';
  var code = argument1 + '.' + operator + '(' + argument0 + ') + 1';
  if (!opt_dropParens) {
    code = '(' + code + ')';
  }
  return code;
};

Blockly.Python.text_charAt = function() {
  // Get letter at index.
  var argument0 = Blockly.Python.valueToCode(this, 'AT', true) || '1';
  var argument1 = Blockly.Python.valueToCode(this, 'VALUE') || '[]';
  // Blockly uses one-based indicies.
  if (argument0.match(/^\d+$/)) {
    // If the index is a naked number, decrement it right now.
    // Except not allowing negative index by constraining at 0.
    argument0 = Math.max(0, parseInt(argument0, 10) - 1);
  } else {
    // If the index is dynamic, decrement it in code.
    argument0 += ' - 1';
  }
  return argument1 + '[' + argument0 + ']';
};

Blockly.Python.text_changeCase = function() {
  // Change capitalization.
  var mode = this.getInputLabelValue('TEXT');
  var operator = Blockly.Python.text_changeCase.OPERATORS[mode];
  var argument0 = Blockly.Python.valueToCode(this, 'TEXT', true) || '\'\'';
  var code = argument0 + operator;
  return code;
};

Blockly.Python.text_changeCase.OPERATORS = {
  UPPERCASE: '.upper()',
  LOWERCASE: '.lower()',
  TITLECASE: '.title()'
};

Blockly.Python.text_trim = function() {
  // Trim spaces.
  var mode = this.getTitleValue('MODE');
  var operator = Blockly.Python.text_trim.OPERATORS[mode];
  var argument0 = Blockly.Python.valueToCode(this, 'TEXT') || '\'\'';
  return argument0 + operator;
};

Blockly.Python.text_trim.OPERATORS = {
  LEFT: '.lstrip()',
  RIGHT: '.rstrip()',
  BOTH: '.strip()'
};

Blockly.Python.text_print = function() {
  // Print statement.
  var argument0 = Blockly.Python.valueToCode(this, 'TEXT', true) || '\'\'';
  return 'print ' + argument0 + '\n';
};

Blockly.Python.text_prompt = function() {
  // Prompt function.
  var msg = Blockly.Python.quote_(this.getTitleValue('TEXT'));
  var code = 'raw_input(' + msg + ')';
  var toNumber = this.getTitleValue('TYPE') == 'NUMBER';
  if (toNumber) {
    code = 'float(' + code + ')';
  }
  return code;
};
<|endoftext|>


<commit_before>"use strict";

let moment = require("moment");
let mongoose = require("mongoose");
let util = require("util");

let BetSchema = new mongoose.Schema({
  account: String,
  nick: String,
  season: Number,
  round: Number,
  points: Number,
  maximum: Boolean,
  bets: [{
    _id: false,
    firstName: String,
    lastName: String,
    code: String,
    points: Number,
  }],
  created: {
    type: Date,
    default: Date.now,
  },
});

BetSchema.statics.isBetWindowOpen = function() {
  return this.db.model("event").findNext("qualifying").then(event => {
    if (event) {
      let min = moment(event.start).startOf("isoweek");
      if (min.isBefore()) {
        return Promise.resolve(event.start);
      } else {
        let date = min.format("MMMM D, HH:mm UTC");
        let message = util.format("Bets will be allowed after %s, until qualifying!", date);
        throw new Error(message);
      }
    } else {
      throw new Error("Bets are not allowed");
    }
  });
};

BetSchema.statics.activeBetRound = function() {
  let max_date = moment().utc().isoWeekday(7).hour(23).minute(59).toDate();
  let season = max_date.getFullYear();
  return this.db.model("event").findOne({season: season, start: {$lte: max_date}}).sort("-start")
    .then(event => {
      if (event) {
        return event;
      } else {
        throw new Error("No active round found");
      }
    });
};

BetSchema.statics.userBets = function(nick, round) {
  return this.findOne({nick: nick, round: round, season: (new Date).getFullYear()});
};

BetSchema.statics.setUserBets = function(account, round, names) {
  return this.db.model("season").driversForNames(names).then(drivers => {
    drivers.forEach((driver, i) => {
      if (drivers.indexOf(driver) < i) {
        throw new Error(util.format("Duplicate driver '%s'", driver.code));
      }
    });

    let query = {
      account: account.account,
      season: (new Date).getFullYear(),
      round: round,
    };
    let values = {
      nick: account.nick,
      account: account.account,
      season: query.season,
      round: query.round,
      bets: drivers,
      created: new Date,
    };
    return this
      .update(query, values, {upsert: true})
      .then(status => drivers);
  });
};

BetSchema.statics.latestRound = function(season) {
  let query = {points: {$exists: true}};

  if (season) {
    query.season = season;
  }

  return this.findOne(query)
    .sort({season: -1, round: -1})
    .then(bet => bet || Promise.reject(new Error("No points data")))
    .then(bet => ({season: bet.season, round: bet.round}));
};

BetSchema.statics.pointsForRound = function(round) {
  if (round == "last") {
    round = 0;
  }
  return Promise.resolve(round == 0 ? this.latestRound() : {round: round, season: (new Date).getFullYear()})
    .then(entry => this.find({round: entry.round, season: entry.season, points: {$exists: true}}).sort("-points"))
    .then(bets => {
      if (!bets.length) {
        throw new Error(util.format("No results for round %d", round));
      }

      return this.model("event").findOne({type: "race", season: bets[0].season, round: bets[0].round}).then(event => ({
        season: event.season,
        round: event.round,
        name: event.name,
        bets: bets
      }));
    });
};

BetSchema.statics.pointsForSeason = function(season) {
  return this.latestRound(season)
    .then(entry => this.model("event").findOne({type: "race", season: entry.season, round: entry.round}))
    .then(event => new Promise((resolve, reject) => {
      let query = [
        {$match: {season: event.season, points: {$exists: true}}},
        {$group: {_id: "$nick", points: {$sum: "$points"}, nick: {$first: "$nick"}}},
        {$sort: {points: -1}}
      ];
      this.aggregate(query, (error, result) => {
        if (error) {
          throw error;
        }
        resolve({
          season: event.season,
          round: event.round,
          name: event.name,
          bets: result
        });
      })
    }));
}

class PointsCalculator {
  constructor(results) {
    this.results = results;
    this.scoring = [10, 5, 3, 1];
    this.bonus = 5;
  }

  process(bet) {
    return new Promise((resolve, reject) => {
      bet.points = this.scores(bet);
      resolve(bet);
    });
  }

  scores(bet) {
    let points = [...bet.bets].reduce((value, driver, i) => {
      if (driver.code == this.results[i].code) {
        driver.points = this.scoring[i];
        return value + driver.points;
      } else if (this.driverOnPodium(driver)) {
        driver.points = this.scoring[3];
        return value + driver.points;
      } else {
        driver.points = 0;
      }
      return value;
    }, 0);

    let max = this.scoring.slice(0, 3).reduce((sum, x) => sum + x, 0);

    if (points == max) {
      bet.maximum = true;
      points += this.bonus;
    } else {
      bet.maximum = false;
    }

    return points;
  }

  driverOnPodium(driver) {
    return this.results.slice(0, 3).map(d => d.code).indexOf(driver.code) != -1;
  }
}

exports.configure = services => {
  let database = services.get("database");
  let whois = services.get("whois");
  let commands = services.get("command.manager");
  let events = services.get("event.manager");
  let Bet = database.model("bet", BetSchema);
  let Event = database.model("event");
  let connection = services.get("connection");
  let notifier = services.get("racecalendar.notifications");

  notifier.on("qualifying", event => {
    Bet.find({season: event.season, round: event.round}).then(bets => {
      connection.amsg(util.format("Qualifying is starting so bet window is now closed. We got %d bets placed for this round!", bets.length));
    });
  });

  notifier.on("qualifying", -30, () => {
    connection.amsg("Qualifying starts in 30 minutes! Time for placing bets is NOW!");
  });

  notifier.on("qualifying", -120, () => {
    connection.amsg("Only two hours until the start of qualifying! Remember to check your bets for this round!");
  });

  commands.add("bet", command => {
    let names = command.params;
    if (names.length == 0) {
      return Bet.userBets(command.nick, this.activeEvent.round).then(doc => {
        if (doc) {
          let names = doc.bets.map((d, i) => util.format("%d. %s %s", i+1, d.firstName, d.lastName));
          return util.format("%s: %s", command.nick, names.join(" "));
        } else {
          return Promise.resolve(util.format("%s: You have no bets for this round", command.nick));
        }
      });
    } else {
      return whois.auth(command.nick)
        .then(account => Bet.setUserBets(account, this.activeEvent.round, names))
        .then(drivers => {
          let names = drivers.map((d, i) => util.format("%d. %s %s", i+1, d.firstName, d.lastName));
          return util.format("%s: %s [OK]", command.nick, names.join(" "));
        });
    }
  })
  .validate(() => Bet.isBetWindowOpen())
  .validate((nick, ...names) => {
    if (names.length != 0 && names.length != 3) {
      return Promise.reject("Need three names to bet");
    }
  })
  .validate(() => {
    return Bet.activeBetRound().then(event => {
      // console.log("GOT EVENT", event);
      this.activeEvent = event;
    });
  });

  commands.add("top", command => {
    let round = command.params[0];
    if (round) {
      return Bet.pointsForRound(round).then(result => {
        let points = result.bets.map(bet => util.format("%s %d", bet.nick, bet.points));
        let message = util.format("Score for %s: %s", result.name, points.join("; "));
        return {type: "notice", content: message};
      });
    } else {
      return Bet.pointsForSeason().then(result => {
        let points = result.bets.map(bet => util.format("%s %d", bet.nick, bet.points));
        let message = util.format("Points after %s: %s", result.name, points.join("; "));
        return {type: "notice", content: message};
      });
    }
  });

  events.on("racecalendar.results", event => {
    let query = {season: event.event.season, round: event.event.round};
    let results = event.event.results;

    Bet.find(query)
      .then(bets => Promise.all(bets.map(bet => (new PointsCalculator(results)).process(bet))))
      .then(bets => Promise.all(bets.map(bet => bet.save())))
      .then(bets => {
        if (bets.length) {
          let winners = bets.sort((a, b) => b.points - a.points)
            .filter((bet, i, temp) => (i == 0 || bet.points == temp[0].points));
          let points = winners[0].points;
          let nicks = winners.map(bet => bet.nick).join(", ");
          connection.amsg(util.format("Dudes! Bet scores are updated for the %s", event.event.name));
          connection.amsg(util.format("The highest score in this round was %d by %s", winners[0].points, nicks));
        }
      })
      .then(() => console.log("Updated scores for round", event.event.round));
  });
};
<commit_msg>Check bet window only if trying to set bets<commit_after>"use strict";

let moment = require("moment");
let mongoose = require("mongoose");
let util = require("util");

let BetSchema = new mongoose.Schema({
  account: String,
  nick: String,
  season: Number,
  round: Number,
  points: Number,
  maximum: Boolean,
  bets: [{
    _id: false,
    firstName: String,
    lastName: String,
    code: String,
    points: Number,
  }],
  created: {
    type: Date,
    default: Date.now,
  },
});

BetSchema.statics.isBetWindowOpen = function() {
  return this.db.model("event").findNext("qualifying").then(event => {
    if (event) {
      let min = moment(event.start).startOf("isoweek");
      if (min.isBefore()) {
        return Promise.resolve(event.start);
      } else {
        let date = min.format("MMMM D, HH:mm UTC");
        let message = util.format("Bets will be allowed after %s, until qualifying!", date);
        throw new Error(message);
      }
    } else {
      throw new Error("Bets are not allowed");
    }
  });
};

BetSchema.statics.activeBetRound = function() {
  let max_date = moment().utc().isoWeekday(7).hour(23).minute(59).toDate();
  let season = max_date.getFullYear();
  return this.db.model("event").findOne({season: season, start: {$lte: max_date}}).sort("-start")
    .then(event => {
      if (event) {
        return event;
      } else {
        throw new Error("No active round found");
      }
    });
};

BetSchema.statics.userBets = function(nick, round) {
  return this.findOne({nick: nick, round: round, season: (new Date).getFullYear()});
};

BetSchema.statics.setUserBets = function(account, round, names) {
  return this.db.model("season").driversForNames(names).then(drivers => {
    drivers.forEach((driver, i) => {
      if (drivers.indexOf(driver) < i) {
        throw new Error(util.format("Duplicate driver '%s'", driver.code));
      }
    });

    let query = {
      account: account.account,
      season: (new Date).getFullYear(),
      round: round,
    };
    let values = {
      nick: account.nick,
      account: account.account,
      season: query.season,
      round: query.round,
      bets: drivers,
      created: new Date,
    };
    return this
      .update(query, values, {upsert: true})
      .then(status => drivers);
  });
};

BetSchema.statics.latestRound = function(season) {
  let query = {points: {$exists: true}};

  if (season) {
    query.season = season;
  }

  return this.findOne(query)
    .sort({season: -1, round: -1})
    .then(bet => bet || Promise.reject(new Error("No points data")))
    .then(bet => ({season: bet.season, round: bet.round}));
};

BetSchema.statics.pointsForRound = function(round) {
  if (round == "last") {
    round = 0;
  }
  return Promise.resolve(round == 0 ? this.latestRound() : {round: round, season: (new Date).getFullYear()})
    .then(entry => this.find({round: entry.round, season: entry.season, points: {$exists: true}}).sort("-points"))
    .then(bets => {
      if (!bets.length) {
        throw new Error(util.format("No results for round %d", round));
      }

      return this.model("event").findOne({type: "race", season: bets[0].season, round: bets[0].round}).then(event => ({
        season: event.season,
        round: event.round,
        name: event.name,
        bets: bets
      }));
    });
};

BetSchema.statics.pointsForSeason = function(season) {
  return this.latestRound(season)
    .then(entry => this.model("event").findOne({type: "race", season: entry.season, round: entry.round}))
    .then(event => new Promise((resolve, reject) => {
      let query = [
        {$match: {season: event.season, points: {$exists: true}}},
        {$group: {_id: "$nick", points: {$sum: "$points"}, nick: {$first: "$nick"}}},
        {$sort: {points: -1}}
      ];
      this.aggregate(query, (error, result) => {
        if (error) {
          throw error;
        }
        resolve({
          season: event.season,
          round: event.round,
          name: event.name,
          bets: result
        });
      })
    }));
}

class PointsCalculator {
  constructor(results) {
    this.results = results;
    this.scoring = [10, 5, 3, 1];
    this.bonus = 5;
  }

  process(bet) {
    return new Promise((resolve, reject) => {
      bet.points = this.scores(bet);
      resolve(bet);
    });
  }

  scores(bet) {
    let points = [...bet.bets].reduce((value, driver, i) => {
      if (driver.code == this.results[i].code) {
        driver.points = this.scoring[i];
        return value + driver.points;
      } else if (this.driverOnPodium(driver)) {
        driver.points = this.scoring[3];
        return value + driver.points;
      } else {
        driver.points = 0;
      }
      return value;
    }, 0);

    let max = this.scoring.slice(0, 3).reduce((sum, x) => sum + x, 0);

    if (points == max) {
      bet.maximum = true;
      points += this.bonus;
    } else {
      bet.maximum = false;
    }

    return points;
  }

  driverOnPodium(driver) {
    return this.results.slice(0, 3).map(d => d.code).indexOf(driver.code) != -1;
  }
}

exports.configure = services => {
  let database = services.get("database");
  let whois = services.get("whois");
  let commands = services.get("command.manager");
  let events = services.get("event.manager");
  let Bet = database.model("bet", BetSchema);
  let Event = database.model("event");
  let connection = services.get("connection");
  let notifier = services.get("racecalendar.notifications");

  notifier.on("qualifying", event => {
    Bet.find({season: event.season, round: event.round}).then(bets => {
      connection.amsg(util.format("Qualifying is starting so bet window is now closed. We got %d bets placed for this round!", bets.length));
    });
  });

  notifier.on("qualifying", -30, () => {
    connection.amsg("Qualifying starts in 30 minutes! Time for placing bets is NOW!");
  });

  notifier.on("qualifying", -120, () => {
    connection.amsg("Only two hours until the start of qualifying! Remember to check your bets for this round!");
  });

  commands.add("bet", command => {
    let names = command.params;
    if (names.length == 0) {
      return Bet.userBets(command.nick, this.activeEvent.round).then(doc => {
        if (doc) {
          let names = doc.bets.map((d, i) => util.format("%d. %s %s", i+1, d.firstName, d.lastName));
          return util.format("%s: %s", command.nick, names.join(" "));
        } else {
          return Promise.resolve(util.format("%s: You have no bets for this round", command.nick));
        }
      });
    } else {
      return whois.auth(command.nick)
        .then(account => Bet.setUserBets(account, this.activeEvent.round, names))
        .then(drivers => {
          let names = drivers.map((d, i) => util.format("%d. %s %s", i+1, d.firstName, d.lastName));
          return util.format("%s: %s [OK]", command.nick, names.join(" "));
        });
    }
  })
  .validate((nick, ...names) => {
    if (names.length) {
      return Bet.isBetWindowOpen()
    }
  })
  .validate((nick, ...names) => {
    if (names.length != 0 && names.length != 3) {
      return Promise.reject("Need three names to bet");
    }
  })
  .validate(() => {
    return Bet.activeBetRound().then(event => {
      // console.log("GOT EVENT", event);
      this.activeEvent = event;
    });
  });

  commands.add("top", command => {
    let round = command.params[0];
    if (round) {
      return Bet.pointsForRound(round).then(result => {
        let points = result.bets.map(bet => util.format("%s %d", bet.nick, bet.points));
        let message = util.format("Score for %s: %s", result.name, points.join("; "));
        return {type: "notice", content: message};
      });
    } else {
      return Bet.pointsForSeason().then(result => {
        let points = result.bets.map(bet => util.format("%s %d", bet.nick, bet.points));
        let message = util.format("Points after %s: %s", result.name, points.join("; "));
        return {type: "notice", content: message};
      });
    }
  });

  events.on("racecalendar.results", event => {
    let query = {season: event.event.season, round: event.event.round};
    let results = event.event.results;

    Bet.find(query)
      .then(bets => Promise.all(bets.map(bet => (new PointsCalculator(results)).process(bet))))
      .then(bets => Promise.all(bets.map(bet => bet.save())))
      .then(bets => {
        if (bets.length) {
          let winners = bets.sort((a, b) => b.points - a.points)
            .filter((bet, i, temp) => (i == 0 || bet.points == temp[0].points));
          let points = winners[0].points;
          let nicks = winners.map(bet => bet.nick).join(", ");
          connection.amsg(util.format("Dudes! Bet scores are updated for the %s", event.event.name));
          connection.amsg(util.format("The highest score in this round was %d by %s", winners[0].points, nicks));
        }
      })
      .then(() => console.log("Updated scores for round", event.event.round));
  });
};
<|endoftext|>


<commit_before>/* global jasmine, describe, beforeAll, afterEach, it, expect */
/* eslint-disable max-nested-callbacks */
'use strict';

var oddcast = require('../../lib/oddcast');

describe('InprocessTransport with EventChannel', function () {
	var PAYLOAD_1 = Object.freeze({event: 1});

	beforeAll(function () {
		this.channel = oddcast.eventChannel();
		this.transport = oddcast.inprocessTransport();
		this.channel.use({role: 'foo'}, this.transport);
	});

	afterEach(function () {
		this.channel.removeAllListeners('error');
		this.channel.remove({});
	});

	it('calls handlers asynchronously', function (done) {
		var async = false;

		this.channel.observe({role: 'foo'}, function () {
			expect(async).toBe(true);
			done();
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
		async = true;
	});

	it('passes the payload to handlers', function (done) {
		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			done();
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
	});

	it('broadcasts specific events to generic observers', function (done) {
		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			done();
		});

		this.channel.broadcast({role: 'foo', item: 'bar'}, PAYLOAD_1);
	});

	it('does not broadcast generic events to specific observers', function (done) {
		var spy = jasmine.createSpy('observer');
		this.channel.observe({role: 'foo', item: 'bar'}, spy);

		this.channel.observe({role: 'foo'}, function () {
			// Give it some time to fire.
			setTimeout(function () {
				expect(spy).not.toHaveBeenCalled();
				done();
			}, 24);
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
	});

	it('broadcasts events to multiple observers', function (done) {
		var resolve = createResolver(done, 3);

		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			resolve();
		});
		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			resolve();
		});
		this.channel.observe({role: 'foo', item: 'bar'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			resolve();
		});

		this.channel.broadcast({role: 'foo', item: 'bar'}, PAYLOAD_1);
	});

	it('removes all handlers (in afterEach())', function () {
		var matcher = this.channel.handlerMatcher;
		expect(Object.keys(matcher.index).length).toBe(0);
	});
});

function createResolver(done, expectedCount) {
	var count = 0;
	return function () {
		count += 1;
		if (count >= expectedCount) {
			done();
		}
	};
}
<commit_msg>Test Inprocess Event Channel error handling.<commit_after>/* global jasmine, describe, beforeAll, afterEach, it, expect */
/* eslint-disable max-nested-callbacks */
'use strict';

var Promise = require('bluebird');
var oddcast = require('../../lib/oddcast');

describe('InprocessTransport with EventChannel', function () {
	var PAYLOAD_1 = Object.freeze({event: 1});
	var ERROR_1 = new Error('ERROR_1');

	beforeAll(function () {
		this.channel = oddcast.eventChannel();
		this.transport = oddcast.inprocessTransport();
		this.channel.use({role: 'foo'}, this.transport);
	});

	afterEach(function () {
		this.channel.removeAllListeners('error');
		this.channel.remove({});
	});

	it('calls handlers asynchronously', function (done) {
		var async = false;

		this.channel.observe({role: 'foo'}, function () {
			expect(async).toBe(true);
			done();
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
		async = true;
	});

	it('passes the payload to handlers', function (done) {
		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			done();
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
	});

	it('broadcasts specific events to generic observers', function (done) {
		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			done();
		});

		this.channel.broadcast({role: 'foo', item: 'bar'}, PAYLOAD_1);
	});

	it('does not broadcast generic events to specific observers', function (done) {
		var spy = jasmine.createSpy('observer');
		this.channel.observe({role: 'foo', item: 'bar'}, spy);

		this.channel.observe({role: 'foo'}, function () {
			// Give it some time to fire.
			setTimeout(function () {
				expect(spy).not.toHaveBeenCalled();
				done();
			}, 24);
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
	});

	it('broadcasts events to multiple observers', function (done) {
		var resolve = createResolver(done, 3);

		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			resolve();
		});
		this.channel.observe({role: 'foo'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			resolve();
		});
		this.channel.observe({role: 'foo', item: 'bar'}, function (payload) {
			expect(payload).toBe(PAYLOAD_1);
			resolve();
		});

		this.channel.broadcast({role: 'foo', item: 'bar'}, PAYLOAD_1);
	});

	it('emits the error when a handler throws an error', function (done) {
		this.channel.observe({role: 'foo'}, function () {
			throw ERROR_1;
		});

		this.channel.on('error', function (err) {
			expect(err).toBe(ERROR_1);
			done();
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
	});

	it('emits the error when a handler rejects', function (done) {
		this.channel.observe({role: 'foo'}, function () {
			return Promise.reject(ERROR_1);
		});

		this.channel.on('error', function (err) {
			expect(err).toBe(ERROR_1);
			done();
		});

		this.channel.broadcast({role: 'foo'}, PAYLOAD_1);
	});

	it('removes all handlers (in afterEach())', function () {
		var matcher = this.channel.handlerMatcher;
		expect(Object.keys(matcher.index).length).toBe(0);
	});
});

function createResolver(done, expectedCount) {
	var count = 0;
	return function () {
		count += 1;
		if (count >= expectedCount) {
			done();
		}
	};
}
<|endoftext|>


<commit_before>/**
 *
 *  -----------------------------------
 *  spotifyCurrentlyPlaying.js - v0.3.1
 *  -----------------------------------
 *
 *  Display your currently playing Spotify song(s) using Last.fm scrobbling.
 *
 *  https://github.com/kjbrum/spotifyCurrentlyPlaying.js
 *  @license Copyright (c) Kyle Brumm <http://kylebrumm.com>
 *
 */

;
(function(global) {
    var SpotifyCurrentlyPlaying = function(settings) {
        return new SpotifyCurrentlyPlaying.init(settings);
    }

    SpotifyCurrentlyPlaying.prototype = {
        /******************************
         * Display the Spotify player *
         ******************************/
        displayPlayer: function() {
            var self = this;

            // Check for missing selector
            if (!this.selector) {
                throw 'Missing selector';
            }

            // Get the most recent track
            self.queryLastfm(function() {

                // Search Spotify for the track
                self.searchSpotify(function() {
                    var container = self.selector;

                    // Check the type of selector that was supplied
                    if (typeof self.selector === 'string') {
                        container = document.querySelector(self.selector);
                    }

                    // Display the iframe if we found a track URI
                    if (self.spotify_URIs.length > 0 || self.backup_ids.length > 0) {
                        var track_uri = '';
                        var iframe = document.createElement('iframe');

                        // Build the iframe
                        iframe.width = self.width;
                        iframe.height = self.height;
                        iframe.frameBorder = 0;
                        iframe.setAttribute('allowtransparency', 'true');

                        // Check if we found anything or need to show backup tracks
                        if (self.spotify_URIs.length > 0) {
                            // Check if we found multiple tracks
                            if (self.spotify_URIs.length > 1) {
                                track_uri += 'spotify:trackset:Recently+Played:'

                                // Loop through the Spotify URIs
                                self.spotify_URIs.forEach(function(el, idx, arr) {
                                    var track_pieces = arr[idx].split(':');
                                    track_uri += track_pieces[2];

                                    if ((idx + 1) !== arr.length) {
                                        track_uri += ',';
                                    }
                                });
                            } else {
                                track_uri = self.spotify_URIs[0];
                            }
                        } else {
                            // Check if we have multiple backup IDs
                            if (self.backup_ids.length > 1) {
                                track_uri += 'spotify:trackset:Recently+Played:'

                                // Loop through the backup IDs
                                self.backup_ids.forEach(function(el, idx, arr) {
                                    track_uri += arr[idx];

                                    if ((idx + 1) !== arr.length) {
                                        track_uri += ',';
                                    }
                                });
                            } else {
                                track_uri = self.backup_ids[0];
                            }
                        }

                        iframe.src = 'https://embed.spotify.com/?uri=' + track_uri + '&theme=' + self.theme + '&view=' + self.view;
                        container.appendChild(iframe);
                    } else {
                        var paragraph = document.createElement('p');
                        var paragraph_text = document.createTextNode('No track found.');
                        paragraph.appendChild(paragraph_text);
                        container.appendChild(paragraph);
                    }
                });
            });
        },

        /******************************************************
         * Get the most recently scrobbled track from Last.fm *
         ******************************************************/
        queryLastfm: function(callback) {
            var self = this;

            // Set the request URL for Last.fm
            var lastfm_request_url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=' + this.username + '&api_key=' + this.api_key + '&limit=' + self.count + '&format=json';

            // Make a request to the Last.fm API
            var request = new XMLHttpRequest();
            request.open('GET', lastfm_request_url, true);

            // Check for a successful response
            request.onload = function() {
                // Parse the response
                var data = JSON.parse(request.responseText);
                var tracks = data.recenttracks.track;

                // Check the status of the request
                if (request.status >= 200 && request.status < 400) {
                    if (tracks.length > 0) {
                        // Update our values
                        if (tracks[0]) {
                            // Loop through the tracks
                            tracks.forEach(function(el, idx, arr) {
                                self.lastfm_tracks.push({
                                    title: arr[idx].name,
                                    artist: arr[idx].artist['#text'],
                                    album: arr[idx].album['#text']
                                });
                            });
                        } else {
                            self.lastfm_tracks.push({
                                title: tracks.name,
                                artist: tracks.artist['#text'],
                                album: tracks.album['#text']
                            });
                        }
                    }

                    // Run the callback function
                    callback();
                } else {
                    // Error from the server
                    throw data.message;
                }
            };

            // Handle any errors
            request.onerror = function() {
                // Connection error
                throw 'connection error';
            };

            // Send the request
            request.send();
        },

        /*******************************************
         * Search for track information on Spotify *
         *******************************************/
        searchSpotify: function(callback) {
            var self = this;
            var tracks = self.lastfm_tracks;
            var tracksProcessed = 0;

            if (tracks.length > 0) {
                // Loop through the tracks
                tracks.forEach(function(el, idx, arr) {
                    var search_query = '';

                    // Loop through the track properties
                    for (var key in el) {
                        // Skip if the property is from prototype
                        if (!el.hasOwnProperty(key)) continue;

                        // Build the search query string
                        if (el[key]) {
                            search_query += key + ':' + el[key] + ' ';
                        }
                    }

                    // Set the request URL for Spotify
                    var spotify_request_url = 'https://api.spotify.com/v1/search?query=' + encodeURIComponent(search_query) + '&offset=0&limit=1&type=track';

                    // Make a request to the Spotify API
                    var request = new XMLHttpRequest();
                    request.open('GET', spotify_request_url, true);

                    // Check for a successful response
                    request.onload = function() {
                        // Parse the response
                        var data = JSON.parse(request.responseText);

                        // Check the status of the request
                        if (request.status >= 200 && request.status < 400) {
                            // Update our values
                            if (data.tracks.items[0]) {
                                // If we found a track, push it into our array
                                self.spotify_URIs.push(data.tracks.items[0].uri);
                            }

                            // tracksProcessed++;
                            // Check if we are on the last track
                            if (++tracksProcessed === arr.length) {
                                // Run the callback function
                                callback();
                            }
                        } else {
                            // Error from the server
                            throw data.message;
                        }
                    };

                    // Handle any errors
                    request.onerror = function() {
                        // Connection error
                        throw 'connection error';
                    };

                    // Send the request
                    request.send();
                });
            } else {
                // Run the callback function if no tracks were found
                callback();
            }
        }
    };

    /*************************************
     *  Initializing our function *
     *************************************/
    SpotifyCurrentlyPlaying.init = function(settings) {
        var self = this;

        // Setup settings
        self.selector      = settings.selector || '.scp-container';    // Selector for the container
        self.username      = settings.username || '';                  // LastFM username
        self.api_key       = settings.api_key || '';                   // LastFM API key
        self.width         = settings.width || 300;                    // Width of the player
        self.height        = settings.height || 400;                   // Height of the player
        self.theme         = settings.theme || 'black';                // Theme of the player
        self.view          = settings.view || 'list';                  // View of the player
        self.count         = parseInt(settings.count) || 1;            // Number of tracks to display
        self.backup_ids    = settings.backup_ids || [];                // Backup IDs of tracks to display if no tracks are found

        // Used for storing data
        self.spotify_URIs  = [];                                       // Spotify URIs
        self.lastfm_tracks = [];                                       // Array of LastFM track info

        // Display the Spotify player
        self.displayPlayer();
    }

    SpotifyCurrentlyPlaying.init.prototype = SpotifyCurrentlyPlaying.prototype;

    global.SpotifyCurrentlyPlaying = global.SCP = SpotifyCurrentlyPlaying;
}(window));
<commit_msg>Fix count returning 1 extra track<commit_after>/**
 *
 *  -----------------------------------
 *  spotifyCurrentlyPlaying.js - v0.3.1
 *  -----------------------------------
 *
 *  Display your currently playing Spotify song(s) using Last.fm scrobbling.
 *
 *  https://github.com/kjbrum/spotifyCurrentlyPlaying.js
 *  @license Copyright (c) Kyle Brumm <http://kylebrumm.com>
 *
 */

;
(function(global) {
    var SpotifyCurrentlyPlaying = function(settings) {
        return new SpotifyCurrentlyPlaying.init(settings);
    }

    SpotifyCurrentlyPlaying.prototype = {
        /******************************
         * Display the Spotify player *
         ******************************/
        displayPlayer: function() {
            var self = this;

            // Check for missing selector
            if (!this.selector) {
                throw 'Missing selector';
            }

            // Get the most recent track
            self.queryLastfm(function() {

                // Search Spotify for the track
                self.searchSpotify(function() {
                    var container = self.selector;

                    // Check the type of selector that was supplied
                    if (typeof self.selector === 'string') {
                        container = document.querySelector(self.selector);
                    }

                    // Display the iframe if we found a track URI
                    if (self.spotify_URIs.length > 0 || self.backup_ids.length > 0) {
                        var track_uri = '';
                        var iframe = document.createElement('iframe');

                        // Build the iframe
                        iframe.width = self.width;
                        iframe.height = self.height;
                        iframe.frameBorder = 0;
                        iframe.setAttribute('allowtransparency', 'true');

                        // Check if we found anything or need to show backup tracks
                        if (self.spotify_URIs.length > 0) {
                            // Check if we found multiple tracks
                            if (self.spotify_URIs.length > 1) {
                                track_uri += 'spotify:trackset:Recently+Played:'

                                // Loop through the Spotify URIs
                                self.spotify_URIs.forEach(function(el, idx, arr) {
                                    var track_pieces = arr[idx].split(':');
                                    track_uri += track_pieces[2];

                                    if ((idx + 1) !== arr.length) {
                                        track_uri += ',';
                                    }
                                });
                            } else {
                                track_uri = self.spotify_URIs[0];
                            }
                        } else {
                            // Check if we have multiple backup IDs
                            if (self.backup_ids.length > 1) {
                                track_uri += 'spotify:trackset:Recently+Played:'

                                // Loop through the backup IDs
                                self.backup_ids.forEach(function(el, idx, arr) {
                                    track_uri += arr[idx];

                                    if ((idx + 1) !== arr.length) {
                                        track_uri += ',';
                                    }
                                });
                            } else {
                                track_uri = self.backup_ids[0];
                            }
                        }

                        iframe.src = 'https://embed.spotify.com/?uri=' + track_uri + '&theme=' + self.theme + '&view=' + self.view;
                        container.appendChild(iframe);
                    } else {
                        var paragraph = document.createElement('p');
                        var paragraph_text = document.createTextNode('No track found.');
                        paragraph.appendChild(paragraph_text);
                        container.appendChild(paragraph);
                    }
                });
            });
        },

        /******************************************************
         * Get the most recently scrobbled track from Last.fm *
         ******************************************************/
        queryLastfm: function(callback) {
            var self = this;

            // Set the request URL for Last.fm
            var lastfm_request_url = 'http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=' + this.username + '&api_key=' + this.api_key + '&limit=' + self.count + '&format=json';

            // Make a request to the Last.fm API
            var request = new XMLHttpRequest();
            request.open('GET', lastfm_request_url, true);

            // Check for a successful response
            request.onload = function() {
                // Parse the response
                var data = JSON.parse(request.responseText);
                var tracks = data.recenttracks.track;

                // Check the status of the request
                if (request.status >= 200 && request.status < 400) {
                    if (tracks.length > 0) {
                        // Update our values
                        if (tracks[0]) {
                            // Loop through the tracks
                            tracks.forEach(function(el, idx, arr) {
                                self.lastfm_tracks.push({
                                    title: arr[idx].name,
                                    artist: arr[idx].artist['#text'],
                                    album: arr[idx].album['#text']
                                });
                            });
                        } else {
                            self.lastfm_tracks.push({
                                title: tracks.name,
                                artist: tracks.artist['#text'],
                                album: tracks.album['#text']
                            });
                        }
                    }

                    // Run the callback function
                    callback();
                } else {
                    // Error from the server
                    throw data.message;
                }
            };

            // Handle any errors
            request.onerror = function() {
                // Connection error
                throw 'connection error';
            };

            // Send the request
            request.send();
        },

        /*******************************************
         * Search for track information on Spotify *
         *******************************************/
        searchSpotify: function(callback) {
            var self = this;
            var tracks = self.lastfm_tracks;
            var tracksProcessed = 1;

            if (tracks.length > 0) {
                // Loop through the tracks
                tracks.forEach(function(el, idx, arr) {
                    var search_query = '';

                    // Loop through the track properties
                    for (var key in el) {
                        // Skip if the property is from prototype
                        if (!el.hasOwnProperty(key)) continue;

                        // Build the search query string
                        if (el[key]) {
                            search_query += key + ':' + el[key] + ' ';
                        }
                    }

                    // Set the request URL for Spotify
                    var spotify_request_url = 'https://api.spotify.com/v1/search?query=' + encodeURIComponent(search_query) + '&offset=0&limit=1&type=track';

                    // Make a request to the Spotify API
                    var request = new XMLHttpRequest();
                    request.open('GET', spotify_request_url, true);

                    // Check for a successful response
                    request.onload = function() {
                        // Parse the response
                        var data = JSON.parse(request.responseText);

                        // Check the status of the request
                        if (request.status >= 200 && request.status < 400) {
                            // Update our values
                            if (data.tracks.items[0]) {
                                // If we found a track, push it into our array
                                self.spotify_URIs.push(data.tracks.items[0].uri);
                            }

                            // tracksProcessed++;
                            // Check if we are on the last track
                            if (++tracksProcessed === arr.length) {
                                // Run the callback function
                                callback();
                            }
                        } else {
                            // Error from the server
                            throw data.message;
                        }
                    };

                    // Handle any errors
                    request.onerror = function() {
                        // Connection error
                        throw 'connection error';
                    };

                    // Send the request
                    request.send();
                });
            } else {
                // Run the callback function if no tracks were found
                callback();
            }
        }
    };

    /*************************************
     *  Initializing our function *
     *************************************/
    SpotifyCurrentlyPlaying.init = function(settings) {
        var self = this;

        // Setup settings
        self.selector      = settings.selector || '.scp-container';    // Selector for the container
        self.username      = settings.username || '';                  // LastFM username
        self.api_key       = settings.api_key || '';                   // LastFM API key
        self.width         = settings.width || 300;                    // Width of the player
        self.height        = settings.height || 400;                   // Height of the player
        self.theme         = settings.theme || 'black';                // Theme of the player
        self.view          = settings.view || 'list';                  // View of the player
        self.count         = parseInt(settings.count) || 1;            // Number of tracks to display
        self.backup_ids    = settings.backup_ids || [];                // Backup IDs of tracks to display if no tracks are found

        // Used for storing data
        self.spotify_URIs  = [];                                       // Spotify URIs
        self.lastfm_tracks = [];                                       // Array of LastFM track info

        // Display the Spotify player
        self.displayPlayer();
    }

    SpotifyCurrentlyPlaying.init.prototype = SpotifyCurrentlyPlaying.prototype;

    global.SpotifyCurrentlyPlaying = global.SCP = SpotifyCurrentlyPlaying;
}(window));
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var nodes = require('../../lib/nodes')
  , should = require('should');

module.exports = {
  'test Color': function(){
    var node = new nodes.Color(255,50,0,1)
      , clone = node.clone();

    clone.should.not.equal(node);
    clone.r.should.equal(255);
    clone.g.should.equal(50);
    clone.b.should.equal(0);
    clone.a.should.equal(1);
  },

  'test HSLA': function(){
    var node = new nodes.HSLA(100, 50, 12, 1)
      , clone = node.clone();

    clone.should.not.equal(node);
    clone.h.should.equal(100);
    clone.s.should.equal(50);
    clone.l.should.equal(12);
    clone.a.should.equal(1);
  }
};<commit_msg>Added Params#clone() test<commit_after>
/**
 * Module dependencies.
 */

var nodes = require('../../lib/nodes')
  , should = require('should');

module.exports = {
  'test default': function(){
    nodes.null.clone().should.equal(nodes.null);
  },

  'test Color': function(){
    var node = new nodes.Color(255,50,0,1)
      , clone = node.clone();

    clone.should.not.equal(node);
    clone.r.should.equal(255);
    clone.g.should.equal(50);
    clone.b.should.equal(0);
    clone.a.should.equal(1);
  },

  'test HSLA': function(){
    var node = new nodes.HSLA(100, 50, 12, 1)
      , clone = node.clone();

    clone.should.not.equal(node);
    clone.h.should.equal(100);
    clone.s.should.equal(50);
    clone.l.should.equal(12);
    clone.a.should.equal(1);
  },

  'test Params': function(){
    var node = new nodes.Params
      , a = new nodes.Unit(15)
      , b = new nodes.Unit(20);

    node.nodes.push(a);
    node.nodes.push(b);

    var clone = node.clone();
    clone.should.not.equal(node);
    clone.nodes[0].should.not.equal(a);
    clone.nodes[1].should.not.equal(b);
    clone.nodes[0].val.should.equal(15);
    clone.nodes[1].val.should.equal(20);
  }
};<|endoftext|>


<commit_before>import initialState from './initialState';
import {
    LOGO_SPIN_STARTED,
    LOGO_SPIN_ENDED,
    PAGE_SCROLL_STARTED,
    PAGE_SCROLL_ENDED,
    APP_INIT,
    STATUS_UPDATE,
    PROVIDER_CHANGE,
    BOARD_CHANGE,

    SCROLL_HEADER,

    THREAD_REQUESTED,
    BOARD_REQUESTED,
} from '../constants';

export default function (state = initialState.status, action) {
    switch (action.type) {

        // case APP_INIT:
        //     return Object.assign({}, state, {
        //         isMainPage: true
        //     })

        case PAGE_SCROLL_STARTED:
            return Object.assign({}, state, {
                isScrolling: true,
                currentPage: action.payload
            })

        case PAGE_SCROLL_ENDED:
            return Object.assign({}, state, {
                isScrolling: false,
                currentPage: action.payload
            })

        case SCROLL_HEADER:
            return Object.assign({}, state, {
                isHeaderVisible: action.payload
            })

        case PROVIDER_CHANGE:
            return Object.assign({}, state, {
                provider: action.payload
            })

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                boardID: action.payload
            })

        case THREAD_REQUESTED:
            return Object.assign({}, state, {
                threadID: action.payload
            })

        case STATUS_UPDATE:
            return Object.assign({}, state, {
                statusMessage: action.payload
            })

        default:
            return state
    }
}
<commit_msg>Add reducer cases for thread/board destroyed<commit_after>import initialState from './initialState';
import {
    LOGO_SPIN_STARTED,
    LOGO_SPIN_ENDED,
    PAGE_SCROLL_STARTED,
    PAGE_SCROLL_ENDED,
    APP_INIT,
    STATUS_UPDATE,
    PROVIDER_CHANGE,
    BOARD_CHANGE,

    SCROLL_HEADER,

    THREAD_REQUESTED,
    THREAD_DESTROYED,

    BOARD_REQUESTED,
    BOARD_DESTROYED,
} from '../constants';

export default function (state = initialState.status, action) {
    switch (action.type) {

        // case APP_INIT:
        //     return Object.assign({}, state, {
        //         isMainPage: true
        //     })

        case PAGE_SCROLL_STARTED:
            return Object.assign({}, state, {
                isScrolling: true,
                currentPage: action.payload
            })

        case PAGE_SCROLL_ENDED:
            return Object.assign({}, state, {
                isScrolling: false,
                currentPage: action.payload
            })

        case SCROLL_HEADER:
            return Object.assign({}, state, {
                isHeaderVisible: action.payload
            })

        case PROVIDER_CHANGE:
            return Object.assign({}, state, {
                provider: action.payload
            })

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                boardID: action.payload
            })

        case THREAD_REQUESTED:
            return Object.assign({}, state, {
                threadID: action.payload
            })

        case STATUS_UPDATE:
            return Object.assign({}, state, {
                alertMessage: action.payload
            })

        case THREAD_DESTROYED:
            return Object.assign({}, state, {
                threadID: null
            })

        case BOARD_DESTROYED:
            return Object.assign({}, state, {
                boardID: null
            })

        default:
            return state
    }
}
<|endoftext|>


<commit_before>var parseUrl = require('url').parse
var querystring = require('querystring')
var logger = require('./logger')('cas_validate::ticket')
var force_protocol=require('./force_protocol')
var request = require('request')

function ticket(options){

    var cas_host = force_protocol(options.cas_host)
    if (! cas_host ) throw new Error('no CAS host specified');
    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';
    if(opt_service === undefined){
        throw new Error('API change. The service parameter is now required in the options object because guessing the service from the request header is a security bug')
    }
    var login_service = options.login_service; // for example: 'http://safety.ctmlabs.net/geojson';
    if(login_service === undefined){
        // for now, just go with standard path, but perhaps throw here too?
        logger.info('setting login service endpoint on CAS server to /cas/login')
        login_service = '/cas/login'
    }
    var validation_service = options.login_service;
    if(validation_service === undefined){
        // for now, just go with standard path, but perhaps throw here too?
        logger.info('setting validation service endpoint on CAS server to /cas/login')
        validation_service = '/cas/serviceValidate'
    }
    return function(req,res,next){
        var url = parseUrl(req.url,true);

        if(url.query === undefined || url.query.ticket === undefined){
            logger.debug('moving along, no ticket');
            return next();
        }
        logger.debug('have ticket in query')
        if(req.session.st){
            // have session, so move along)
            logger.debug('session still valid, do not reparse ticket')
            return next()
        }
        // prevent double checking.  issue #5
        if(req.session.ticket !== undefined
          && req.session.ticket === url.query.ticket){
            logger.debug('ticket already checked')
            // tried various ways to strip the ticket and redirect
            // back, but they all failed, as the browser kept clearing
            // the session
            // so live with the ticket in the URL
            return next()
        }
        logger.debug('checking ticket')

        // validate the ticket.
        //
        // MUST begin with the characters, "ST-", and must be between
        // 32 and 256 characters in length.  Unclear is 32 includes
        // the ST- characters, so allow for it
        //

        if(!/^ST-.{29,256}$/.test(url.query.ticket)){
            var queryopts = {'service':opt_service};
            res.writeHead(307, { 'location': cas_host+login_service
                                           +'?'
                                           +querystring.stringify(queryopts)
                               });
            res.end()
            // res.statusCode = 400
            // res.end('Invalid service ticket. Contact the Central Authentication Service  server administrator')
            return null
        }
        // at this point, the above regex has validated the ticket, so it is okay to pass on as is to the CAS server

        // have a ticket, try to set up a CAS session

        // validate the service ticket
        var ticket = url.query.ticket;
        req.session.ticket = ticket // for next time, to prevent double checks
        var cas_uri =  cas_host+validation_service
                    +'?'
                    +querystring.stringify(
                        {'service':opt_service,
                         'ticket':ticket});
        logger.debug('firing: '+cas_uri)

        request({uri:cas_uri}, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                logger.debug('calling sax parser to parse: '+body)
                var saxparser = require('./sax_parser')(req,res,next)
                saxparser.parseString(body)
            }else{
                // not sure what is up, pass along
                // okay, not logged in, but don't get worked up about it
                return next()
            }
            return null;

        });
        return null
    }
}

module.exports=ticket
<commit_msg>removed an extra verbose logging statement<commit_after>var parseUrl = require('url').parse
var querystring = require('querystring')
var logger = require('./logger')('cas_validate::ticket')
var force_protocol=require('./force_protocol')
var request = require('request')

function ticket(options){

    var cas_host = force_protocol(options.cas_host)
    if (! cas_host ) throw new Error('no CAS host specified');
    var opt_service = options.service; // for example: 'http://safety.ctmlabs.net/geojson';
    if(opt_service === undefined){
        throw new Error('API change. The service parameter is now required in the options object because guessing the service from the request header is a security bug')
    }
    var login_service = options.login_service; // for example: 'http://safety.ctmlabs.net/geojson';
    if(login_service === undefined){
        // for now, just go with standard path, but perhaps throw here too?
        logger.info('setting login service endpoint on CAS server to /cas/login')
        login_service = '/cas/login'
    }
    var validation_service = options.login_service;
    if(validation_service === undefined){
        // for now, just go with standard path, but perhaps throw here too?
        logger.info('setting validation service endpoint on CAS server to /cas/login')
        validation_service = '/cas/serviceValidate'
    }
    return function(req,res,next){
        var url = parseUrl(req.url,true);

        if(url.query === undefined || url.query.ticket === undefined){
            logger.debug('moving along, no ticket');
            return next();
        }
        logger.debug('have ticket in query')
        if(req.session.st){
            // have session, so move along)
            logger.debug('session still valid, do not reparse ticket')
            return next()
        }
        // prevent double checking.  issue #5
        if(req.session.ticket !== undefined
          && req.session.ticket === url.query.ticket){
            logger.debug('ticket already checked')
            // tried various ways to strip the ticket and redirect
            // back, but they all failed, as the browser kept clearing
            // the session
            // so live with the ticket in the URL
            return next()
        }
        logger.debug('checking ticket')

        // validate the ticket.
        //
        // MUST begin with the characters, "ST-", and must be between
        // 32 and 256 characters in length.  Unclear is 32 includes
        // the ST- characters, so allow for it
        //

        if(!/^ST-.{29,256}$/.test(url.query.ticket)){
            var queryopts = {'service':opt_service};
            res.writeHead(307, { 'location': cas_host+login_service
                                           +'?'
                                           +querystring.stringify(queryopts)
                               });
            res.end()
            // res.statusCode = 400
            // res.end('Invalid service ticket. Contact the Central Authentication Service  server administrator')
            return null
        }
        // at this point, the above regex has validated the ticket, so it is okay to pass on as is to the CAS server

        // have a ticket, try to set up a CAS session

        // validate the service ticket
        var ticket = url.query.ticket;
        req.session.ticket = ticket // for next time, to prevent double checks
        var cas_uri =  cas_host+validation_service
                    +'?'
                    +querystring.stringify(
                        {'service':opt_service,
                         'ticket':ticket});
        logger.debug('firing: '+cas_uri)

        request({uri:cas_uri}, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var saxparser = require('./sax_parser')(req,res,next)
                saxparser.parseString(body)
            }else{
                // not sure what is up, pass along
                // okay, not logged in, but don't get worked up about it
                return next()
            }
            return null;

        });
        return null
    }
}

module.exports=ticket
<|endoftext|>


<commit_before>var _ = require('./icebreaker')
var stream = require('stream')
var to = require('stream-to-pull-stream')
var Source = _.source
var Through = _.through
var Sink = _.sink
var Pair = _.pair

function done(cb){
  return cb?function(end){
    if(cb){ cb(end); cb=null }
  }:cb
}

_.mixin({
  source: function (s) {
    if (s instanceof stream.Stream) {
      if (s.readable) return to.source(s)
      throw Error('source: node.js stream must be readable')
    }
    return Source.apply(this, arguments)
  },
  through: function (s,cb) {
    if (s instanceof stream.Stream) {
      if (s.readable && s.writable) return Through(to(s,done(cb)))()
      throw Error('through: node.js stream must be readable and writable')
    }
    return Through.apply(this, arguments)
  },
  sink: function (s,cb) {
    if (s instanceof stream.Stream) {
      if (s.writable) return to.sink(s, done(cb))
      throw Error('sink: node.js stream must be writable')
    }
    return Sink.apply(this, arguments)
  },
  pair: function (s,cb) {
    if (s instanceof stream.Stream) {
      if (s.readable && s.writable) return to.duplex(s,done(cb))
      throw Error('pair: node.js stream must be readable and writable')
    }
    return Pair.apply(this, arguments)
  }
})
<commit_msg>Removed a strict type check from the _.pair method<commit_after>var _ = require('./icebreaker')
var stream = require('stream')
var to = require('stream-to-pull-stream')
var Source = _.source
var Through = _.through
var Sink = _.sink
var Pair = _.pair

function done(cb){
  return cb?function(end){
    if(cb){ cb(end); cb=null }
  }:cb
}

_.mixin({
  source: function (s) {
    if (s instanceof stream.Stream) {
      if (s.readable) return to.source(s)
      throw Error('source: node.js stream must be readable')
    }
    return Source.apply(this, arguments)
  },
  through: function (s,cb) {
    if (s instanceof stream.Stream) {
      if (s.readable && s.writable) return Through(to(s,done(cb)))()
      throw Error('through: node.js stream must be readable and writable')
    }
    return Through.apply(this, arguments)
  },
  sink: function (s,cb) {
    if (s instanceof stream.Stream) {
      if (s.writable) return to.sink(s, done(cb))
      throw Error('sink: node.js stream must be writable')
    }
    return Sink.apply(this, arguments)
  },
  pair: function (s,cb) {
    if (s instanceof stream.Stream) {
      return to.duplex(s,done(cb))
    }
    return Pair.apply(this, arguments)
  }
})
<|endoftext|>