<commit_before>$(function() {
  // handle accordion glyphicon swapping
  $('#accordion').on('show.bs.collapse', function () {
    $('#accordion .in').collapse('hide');
  });

  $('#widget')
    .html($('#sectionText').html())
    .editable({
      inlineMode: false,
      height: 450,
      buttons: [
        'bold', 'italic', 'underline', 'formatBlock', 'insertOrderedList',
        'insertUnorderedList', 'insertVariable', 'undo', 'redo',
      ],
      customButtons: {
        insertVariable: {
          title: 'Insert Variable',
          icon: {
            type: 'font',
            value: 'fa fa-plus'
          },
          callback: function () {
            var froala = this;
            insertVariable()

            froala.insertHTML(createVariableShell());
          }
        }
      }
    });

  // get the minimum variable id which should one more than the total number
  // of variables on the page right now
  variableId = $($('#widget').editable('getHTML')).find('.fr-variable').length + 1;

  function createVariableHtml(modal) {
    var _type = modal.find('.modal-variable-type').val();
    var _name = modal.find('.modal-variable-name').val();

    modal.find('.modal-variable-name').val('');
    modal.find('.modal-variable-type').val('Text');

    return '[[' + _type + '||' + _name + ']]';
  }

  function createVariableId(variableId, isElem) {
    // returns a hash if there is an elem, no hash if there isn't
    var variableIdRoot = 'template-variable-' + variableId;
    return isElem ? '#' + variableIdRoot : variableIdRoot;
  }

  function createVariableShell(modal) {
    // create a shell to insert the variable into
    var shell = '' + '';
    return shell;
  }

  function insertVariable() {
    // open the modal
    $('#variableModal').modal('show');
  }

  // insert the actual variable on save
  $('#variableModal').find('.modal-add-variable').on('click', function(e) {
    var modal = $('#variableModal');
    // figure out if we are inserting a new variable or modifying an old one
    var curVariable = $('.modal-variable-name').attr('data-variable-cur-id') ?
      $('.modal-variable-name').attr('data-variable-cur-id').split('-')[2] :
      variableId;
    // update the html
    $(createVariableId(curVariable, true)).html(createVariableHtml(modal));
    // only increment if we are inserting a new variable
    if (typeof(curVariable) === 'number') variableId++;
    // reset the name value to be an empty string
    $('.modal-variable-name').attr('data-variable-cur-id', null);
  });

  $('#variableModal').on('hide.bs.modal', function(e) {
    // if we are hiding the modal, we need to remove the new shell that we made
    // and reset the values
    $(createVariableId(variableId, true)).remove();
    $('.modal-variable-name').attr('data-variable-cur-id', null);
    $('.modal-variable-name').val('');
    $('.modal-variable-type').val('Text');
  });

  $('.froala-view').on('dblclick', '.fr-variable', function(e) {
    editVariable(e.target);
  });

  function editVariable(target) {
    var modalNameInput = $('.modal-variable-name');
    var modalTypeInput = $('.modal-variable-type');
    // if we are editing an existing variable, attach the existing values
    // to the modal field, and the variable id as a data attribute so that we
    // can access it later
    modalNameInput.val($(target).html().split(':')[1].slice(0, -2));
    modalTypeInput.val($(target).html().split(':')[0].slice(2, $(target).html().split(':')[0].length));
    modalNameInput.attr('data-variable-cur-id', target.id)
    $('#variableModal').modal('show');
  }

  var clicking = false, _el;

  // if we click on a variable, we have an element and we are clicking
  $('.froala-view').on('mousedown.froalaCustom.moveVariable', '.fr-variable', function(e){
    _el = e.target;
    clicking = true;
  });

  // we need to attach listeners both on the froala editor and the document
  // to get all possible mouseups. when we do, we are no longer clicking
  $('.froala-view').on('mouseup.froalaCustom.moveVariable', function(e) {
    clicking = false;
  });

  $('.froala-editor').on('mouseup.froalaCustom.moveVariable', function(e) {
    clicking = false;
  });

  $(document).on('mouseup.froalaCustom.moveVariable', function(e) {
    clicking = false;
  });

  $('.froala-view').on('mousemove.froalaCustom.moveVariable', function(e){
    if (clicking === false || !_el) return;
    e.preventDefault();

    var range, textRange, x = e.clientX, y = e.clientY;

    // detach and store the old element outside the DOM
    _el = _el.parentNode.removeChild(_el);

    // Try to set the range the standards-based way first
    if (document.caretPositionFromPoint) {
      var pos = document.caretPositionFromPoint(x, y);
      range = document.createRange();
      range.setStart(pos.offsetNode, pos.offset);
      range.collapse();
    }
    // Next, the WebKit way
    else if (document.caretRangeFromPoint) {
      range = document.caretRangeFromPoint(x, y);
    }
    // Finally, the IE way
    else if (document.body.createTextRange) {
      textRange = document.body.createTextRange();
      textRange.moveToPoint(x, y);
      var spanId = "temp_" + ("" + Math.random()).slice(2);
      textRange.pasteHTML(' ');
      var span = document.getElementById(spanId);
      //place the new pin
      span.parentNode.replaceChild(_el, span);
    }
    if (range) {
      // place the element at the beginning of the range
      range.insertNode(_el);
    }
  });
});<commit_msg>handle glyph swapping on the accordion<commit_after>$(function() {
  // handle accordion glyphicon swapping
  $('#accordion').on('hide.bs.collapse', function(e) {
    // if we are collapsing the currently opened one, flip the arrow
    if (e.target.id === $('#accordion .in').attr('id')) {
      $(e.target).parent().find('.js-glyphicon').
        attr('class', 'glyphicon glyphicon-arrow-down js-glyphicon');
    }
  });

  $('#accordion').on('show.bs.collapse', function(e) {
    // flip the up arrows to down arrows
    $('#accordion').find('.glyphicon-arrow-up').
      attr('class', 'glyphicon glyphicon-arrow-down js-glyphicon');
    // flip the clicked arrow to up
    $(e.target).parent().find('.js-glyphicon').
      attr('class', 'glyphicon glyphicon-arrow-up js-glyphicon');
    // hide the currently opened section
    $('#accordion .in').collapse('hide');
  });

  $('#widget')
    .html($('#sectionText').html())
    .editable({
      inlineMode: false,
      height: 450,
      buttons: [
        'bold', 'italic', 'underline', 'formatBlock', 'insertOrderedList',
        'insertUnorderedList', 'insertVariable', 'undo', 'redo',
      ],
      customButtons: {
        insertVariable: {
          title: 'Insert Variable',
          icon: {
            type: 'font',
            value: 'fa fa-plus'
          },
          callback: function () {
            var froala = this;
            insertVariable()

            froala.insertHTML(createVariableShell());
          }
        }
      }
    });

  // get the minimum variable id which should one more than the total number
  // of variables on the page right now
  variableId = $($('#widget').editable('getHTML')).find('.fr-variable').length + 1;

  function createVariableHtml(modal) {
    var _type = modal.find('.modal-variable-type').val();
    var _name = modal.find('.modal-variable-name').val();

    modal.find('.modal-variable-name').val('');
    modal.find('.modal-variable-type').val('Text');

    return '[[' + _type + '||' + _name + ']]';
  }

  function createVariableId(variableId, isElem) {
    // returns a hash if there is an elem, no hash if there isn't
    var variableIdRoot = 'template-variable-' + variableId;
    return isElem ? '#' + variableIdRoot : variableIdRoot;
  }

  function createVariableShell(modal) {
    // create a shell to insert the variable into
    var shell = '' + '';
    return shell;
  }

  function insertVariable() {
    // open the modal
    $('#variableModal').modal('show');
  }

  // insert the actual variable on save
  $('#variableModal').find('.modal-add-variable').on('click', function(e) {
    var modal = $('#variableModal');
    // figure out if we are inserting a new variable or modifying an old one
    var curVariable = $('.modal-variable-name').attr('data-variable-cur-id') ?
      $('.modal-variable-name').attr('data-variable-cur-id').split('-')[2] :
      variableId;
    // update the html
    $(createVariableId(curVariable, true)).html(createVariableHtml(modal));
    // only increment if we are inserting a new variable
    if (typeof(curVariable) === 'number') variableId++;
    // reset the name value to be an empty string
    $('.modal-variable-name').attr('data-variable-cur-id', null);
  });

  $('#variableModal').on('hide.bs.modal', function(e) {
    // if we are hiding the modal, we need to remove the new shell that we made
    // and reset the values
    $(createVariableId(variableId, true)).remove();
    $('.modal-variable-name').attr('data-variable-cur-id', null);
    $('.modal-variable-name').val('');
    $('.modal-variable-type').val('Text');
  });

  $('.froala-view').on('dblclick', '.fr-variable', function(e) {
    editVariable(e.target);
  });

  function editVariable(target) {
    var modalNameInput = $('.modal-variable-name');
    var modalTypeInput = $('.modal-variable-type');
    // if we are editing an existing variable, attach the existing values
    // to the modal field, and the variable id as a data attribute so that we
    // can access it later
    modalNameInput.val($(target).html().split(':')[1].slice(0, -2));
    modalTypeInput.val($(target).html().split(':')[0].slice(2, $(target).html().split(':')[0].length));
    modalNameInput.attr('data-variable-cur-id', target.id)
    $('#variableModal').modal('show');
  }

  var clicking = false, _el;

  // if we click on a variable, we have an element and we are clicking
  $('.froala-view').on('mousedown.froalaCustom.moveVariable', '.fr-variable', function(e){
    _el = e.target;
    clicking = true;
  });

  // we need to attach listeners both on the froala editor and the document
  // to get all possible mouseups. when we do, we are no longer clicking
  $('.froala-view').on('mouseup.froalaCustom.moveVariable', function(e) {
    clicking = false;
  });

  $('.froala-editor').on('mouseup.froalaCustom.moveVariable', function(e) {
    clicking = false;
  });

  $(document).on('mouseup.froalaCustom.moveVariable', function(e) {
    clicking = false;
  });

  $('.froala-view').on('mousemove.froalaCustom.moveVariable', function(e){
    if (clicking === false || !_el) return;
    e.preventDefault();

    var range, textRange, x = e.clientX, y = e.clientY;

    // detach and store the old element outside the DOM
    _el = _el.parentNode.removeChild(_el);

    // Try to set the range the standards-based way first
    if (document.caretPositionFromPoint) {
      var pos = document.caretPositionFromPoint(x, y);
      range = document.createRange();
      range.setStart(pos.offsetNode, pos.offset);
      range.collapse();
    }
    // Next, the WebKit way
    else if (document.caretRangeFromPoint) {
      range = document.caretRangeFromPoint(x, y);
    }
    // Finally, the IE way
    else if (document.body.createTextRange) {
      textRange = document.body.createTextRange();
      textRange.moveToPoint(x, y);
      var spanId = "temp_" + ("" + Math.random()).slice(2);
      textRange.pasteHTML(' ');
      var span = document.getElementById(spanId);
      //place the new pin
      span.parentNode.replaceChild(_el, span);
    }
    if (range) {
      // place the element at the beginning of the range
      range.insertNode(_el);
    }
  });
});<|endoftext|>


<commit_before>// Dependencies
var request = require('request-promise').defaults({
  pool: {maxSockets: Infinity},
  simple: false
});

// Includes
var options = require('../options.js');
var settings = require('../../settings.json');
var cache = require('../cache');
var getHash = require('./getHash.js').func;

// Args
exports.required = ['url'];
exports.optional = ['options'];

// Define
function http (url, opt) {
  if (opt && !opt.jar && Object.keys(opt).indexOf('jar') > -1) {
    opt.jar = options.jar;
  }
  if (settings.session_only && opt && opt.jar) {
    if (!opt.headers) {
      opt.headers = {};
    }
    opt.headers.cookie = '.ROBLOSECURITY=' + opt.jar.session + ';';
  }
  if (url.indexOf('http') !== 0) {
    url = 'https:' + url;
  }
  return request(url, opt)
  .catch(function (err) {
    console.error('Http error: ' + err.stack);
  });
}

exports.func = function (args) {
  var opt = args.options;
  var depth = args.depth || 0;
  if (opt && opt.headers && opt.headers['X-CSRF-TOKEN']) {
    var full = opt.resolveWithFullResponse || false;
    opt.resolveWithFullResponse = true;
    return http(args.url, opt).then(function (res) {
      if (res.statusCode === 403 && res.statusMessage === 'XSRF Token Validation Failed') {
        depth++;
        if (depth >= 3) {
          throw new Error('Tried ' + depth + ' times and could not refresh XCSRF token successfully');
        }
        var token = res.headers['x-csrf-token'];
        if (token) {
          opt.headers['X-CSRF-TOKEN'] = token;
          args.depth = depth + 1;
          return exports.func(args);
        } else {
          throw new Error('Could not refresh X-CSRF-TOKEN, make sure you are still logged in');
        }
      } else {
        if (depth > 0) {
          cache.add(options.cache, 'XCSRF', getHash({jar: opt.jar}), opt.headers['X-CSRF-TOKEN']);
        }
        return full ? res : res.body;
      }
    });
  } else {
    return http(args.url, opt);
  }
};
<commit_msg>Remove jar completely if unused<commit_after>// Dependencies
var request = require('request-promise').defaults({
  pool: {maxSockets: Infinity},
  simple: false
});

// Includes
var options = require('../options.js');
var settings = require('../../settings.json');
var cache = require('../cache');
var getHash = require('./getHash.js').func;

// Args
exports.required = ['url'];
exports.optional = ['options'];

// Define
function http (url, opt) {
  if (opt && !opt.jar && Object.keys(opt).indexOf('jar') > -1) {
    opt.jar = options.jar;
  }
  if (settings.session_only && opt && opt.jar) {
    if (!opt.headers) {
      opt.headers = {};
    }
    opt.headers.cookie = '.ROBLOSECURITY=' + opt.jar.session + ';';
    opt.jar = null;
  }
  if (url.indexOf('http') !== 0) {
    url = 'https:' + url;
  }
  return request(url, opt)
  .catch(function (err) {
    console.error('Http error: ' + err.stack);
  });
}

exports.func = function (args) {
  var opt = args.options;
  var jar = opt && opt.jar;
  var depth = args.depth || 0;
  if (opt && opt.headers && opt.headers['X-CSRF-TOKEN']) {
    var full = opt.resolveWithFullResponse || false;
    opt.resolveWithFullResponse = true;
    return http(args.url, opt).then(function (res) {
      if (res.statusCode === 403 && res.statusMessage === 'XSRF Token Validation Failed') {
        depth++;
        if (depth >= 3) {
          throw new Error('Tried ' + depth + ' times and could not refresh XCSRF token successfully');
        }
        var token = res.headers['x-csrf-token'];
        if (token) {
          opt.headers['X-CSRF-TOKEN'] = token;
          opt.jar = jar;
          args.depth = depth + 1;
          return exports.func(args);
        } else {
          throw new Error('Could not refresh X-CSRF-TOKEN, make sure you are still logged in');
        }
      } else {
        if (depth > 0) {
          cache.add(options.cache, 'XCSRF', getHash({jar: jar}), opt.headers['X-CSRF-TOKEN']);
        }
        return full ? res : res.body;
      }
    });
  } else {
    return http(args.url, opt);
  }
};
<|endoftext|>


<commit_before>var angularApp = angular.module('angularApp', []);

angularApp.controller('angCtrl', function ($scope, $http) {

    $scope.getNewBooks = function () {
        $scope.newBooksHead = {
            title: "Название", author: "Автор", description: "Описание", updated: "Дата",
            language: "Язык", format: "Формат", category: "Категория", bookid: "ID"
        };

        var newBooks = [];
        var url = 'http://localhost:3000/getBooksFromDB/';
        $http.get(url)
            .success(function (data, status, headers, config) {

                data.forEach(function (item, i, data) {
                    newBooks.push(item);
                });

                $scope.newBooks = newBooks;
                $scope.summary = 'Общее количество книг в базе: ' + newBooks.length;

            }).error(function () {
            alert('error!');
        });
        ;

    };

    var date = new Date();
    $scope.today = date;
});<commit_msg>try to fix url for getBookFromDB<commit_after>var angularApp = angular.module('angularApp', []);

angularApp.controller('angCtrl', function ($scope, $http) {

    $scope.getNewBooks = function () {
        $scope.newBooksHead = {
            title: "Название", author: "Автор", description: "Описание", updated: "Дата",
            language: "Язык", format: "Формат", category: "Категория", bookid: "ID"
        };

        var newBooks = [];
        var url = '/getBooksFromDB';
        $http.get(url)
            .success(function (data, status, headers, config) {

                data.forEach(function (item, i, data) {
                    newBooks.push(item);
                });

                $scope.newBooks = newBooks;
                $scope.summary = 'Общее количество книг в базе: ' + newBooks.length;

            }).error(function () {
            alert('error!');
        });
        ;

    };

    var date = new Date();
    $scope.today = date;
});<|endoftext|>


<commit_before>const RateLimiter = require("rolling-rate-limiter");
const prettyMs = require('pretty-ms');
const config = require('../config');

const validKeys = ['key1','key2', 'key3', 'key4', 'key5']; //TODO replace API keys in request with JWT-token?
const limiter = RateLimiter({
    interval: 60*60*1000, //1 hour in miliseconds
    maxInInterval: config.max_requests_per_hour
});
const validator = {
    validateKey: function(req, res, next){
        if(validKeys.indexOf(req.query.key)===-1){
            res.status(401);
            res.json({"status":"401", "message":"invalid credentials"});
            return;
        }
        next();
    },
    validateRate: function(req, res, next){
        const timeLeft = limiter(req.query.key);
        if (timeLeft > 0) {
            // limit was exceeded, action should not be allowed
            // timeLeft is the number of ms until the next action will be allowed
            res.status(429);
            res.json({"status":"429", "message":"Request limit excceded- try after "+prettyMs(timeLeft)});
        } else {
            next();
            // limit was not exceeded, action should be allowed
        }
    }
};

module.exports = validator;<commit_msg>Read client keys from environment variable rather than hardcoding in source<commit_after>const RateLimiter = require("rolling-rate-limiter");
const prettyMs = require('pretty-ms');
const config = require('../config');
const validKeys = process.env.CLIENT_KEYS.split(',');
const limiter = RateLimiter({
    interval: 60*60*1000, //1 hour in miliseconds
    maxInInterval: config.max_requests_per_hour
});
const validator = {
    validateKey: function(req, res, next){
        if(validKeys.indexOf(req.query.key)===-1){
            res.status(401);
            res.json({"status":"401", "message":"invalid credentials"});
            return;
        }
        next();
    },
    validateRate: function(req, res, next){
        const timeLeft = limiter(req.query.key);
        if (timeLeft > 0) {
            // limit was exceeded, action should not be allowed
            // timeLeft is the number of ms until the next action will be allowed
            res.status(429);
            res.json({"status":"429", "message":"Request limit excceded- try after "+prettyMs(timeLeft)});
        } else {
            next();
            // limit was not exceeded, action should be allowed
        }
    }
};

module.exports = validator;<|endoftext|>


<commit_before>(function () {
  module.exports = function validator(n) {
    var result = [];
    if(n <= 0)
      result.push('error.nonpositive');
    else if(n % 3 === 0)
      result.push('error.three');
    else if(n % 5 === 0)
      result.push('error.five');
    return result;
  };
})();
<commit_msg>refactor<commit_after>(function () {
  module.exports = function validator(n) {
    function modBy5() { if(n % 5 === 0) result.push('error.five'); }
    function modBy3() { if(n % 3 === 0) result.push('error.three'); }

    var result = [];
    if(n <= 0) {
      result.push('error.nonpositive');
      return result;
    }
    modBy3();
    modBy5();
    return result;
  };
})();
<|endoftext|>


<commit_before>const mocha = require('mocha');
const chai = require('chai');

// classes to tests
//const { generateBomb } = require('../js/bombs');
import { generateBomb } from '../js/bombs.js';
import {Board} from '../board.js';

describe('Bombs tests', () => {

});

describe('Board tests', () => {
	it('constructor should set correct number of rows', function() {
			var board = new Board(16, 16);
            var actual = board.rows;
            var expected = 16;

            expect(actual).to.be.equal(expected);
        });
	it('constructor should set correct number of columns', function() {
			var board = new Board(16, 16);
            var actual = board.cols;
            var expected = 16;

            expect(actual).to.be.equal(expected);
        });
})<commit_msg>add tests<commit_after>const mocha = require('mocha');
const chai = require('chai');

// classes to tests
//const { generateBomb } = require('../js/bombs');
import { generateBomb } from '../js/bombs.js';
import {Board} from '../board.js';

describe('Bombs tests', () => {
	it('generateBomb should return an array', function() {
			var board = new Board(16, 16);
            var actual = board.rows;
            var expected = 16;

            expect(actual).to.be.equal(expected);
        });
});

describe('Board tests', () => {
	it('constructor should set correct number of rows', function() {
			var board = new Board(16, 16);
            var actual = board.rows;
            var expected = 16;

            expect(actual).to.be.equal(expected);
        });
	it('constructor should set correct number of columns', function() {
			var board = new Board(16, 16);
            var actual = board.cols;
            var expected = 16;

            expect(actual).to.be.equal(expected);
        });
});

describe('Timer tests', () => {
	it('constructor should set correct number of minutes', function() {
			var timer = new Timer();
            var actual = timer.minutes;
            var expected = 0;

            expect(actual).to.be.equal(expected);
        });
	it('constructor should set correct number of seconds', function() {
			var timer = new Timer();
            var actual = timer.seconds;
            var expected = 0;

            expect(actual).to.be.equal(expected);
        });
})<|endoftext|>


<commit_before>export default [
  {
    id: 1,
    libroId: 1,
    titulo: 'Autómatas, comandos, procedimientos y repetición',
  },
  {
    id: 2,
    libroId: 1,
    titulo: 'Alternativa condicional',
  },
  {
    id: 3,
    libroId: 1,
    titulo: 'Repetición condicional',
  },
  {
    id: 4,
    libroId: 1,
    titulo: 'Sensores Numéricos',
  },
  {
    id: 5,
    libroId: 1,
    titulo: 'Parametrización de soluciones',
  },
	{
		id: 6,
		libroId: 2,
		titulo: 'Capítulo 3',
	},
  {
		id: 7,
		libroId: 2,
		titulo: 'Capítulo 4',
	},
];
<commit_msg>Definiendo nuevos capítulos del libro de Tandil<commit_after>export default [
  {
    id: 1,
    libroId: 1,
    titulo: 'Autómatas, comandos, procedimientos y repetición',
  },
  {
    id: 2,
    libroId: 1,
    titulo: 'Alternativa condicional',
  },
  {
    id: 3,
    libroId: 1,
    titulo: 'Repetición condicional',
  },
  {
    id: 4,
    libroId: 1,
    titulo: 'Sensores Numéricos',
  },
  {
    id: 5,
    libroId: 1,
    titulo: 'Parametrización de soluciones',
  },
	{
		id: 6,
		libroId: 2,
		titulo: 'Capítulo 3',
	},
  {
		id: 7,
		libroId: 2,
		titulo: 'Capítulo 4',
  },
  {
    id: 11,
    libroId: 2,
    titulo: 'Capítulo 2: Programando en la computadora'
  },
  {
    id: 12,
    libroId: 2,
    titulo: 'Capítulo 3: Repetición'
  },
  {
    id: 13,
    libroId: 2,
    titulo: 'Capítulo 4: Alternativa condicional'
  }
];
<|endoftext|>


<commit_before>/*global Promise:true*/
var Promise = require('bluebird');

var workQueue = {
    queue: [],
    drain: function () {
        this.queue.forEach(function (fn) {
            fn();
        });
        this.queue = [];
    }
};

var scheduler = Promise.setScheduler(function (fn) {
    workQueue.queue.push(fn);
    scheduler(function () {
        workQueue.drain();
    });
});

var _notifyUnhandledRejection = Promise.prototype._notifyUnhandledRejection;
Promise.prototype._notifyUnhandledRejection = function () {
    var that = this;
    var args = arguments;
    scheduler(function () {
        if (that._isRejectionUnhandled()) {
            throw that.reason();
        }
    });
};

module.exports = workQueue;
<commit_msg>Removed dead code<commit_after>/*global Promise:true*/
var Promise = require('bluebird');

var workQueue = {
    queue: [],
    drain: function () {
        this.queue.forEach(function (fn) {
            fn();
        });
        this.queue = [];
    }
};

var scheduler = Promise.setScheduler(function (fn) {
    workQueue.queue.push(fn);
    scheduler(function () {
        workQueue.drain();
    });
});

Promise.prototype._notifyUnhandledRejection = function () {
    var that = this;
    scheduler(function () {
        if (that._isRejectionUnhandled()) {
            throw that.reason();
        }
    });
};

module.exports = workQueue;
<|endoftext|>


<commit_before>var util = require('util'); // util.inherits
var net = require('net');

var handshake = require('./handshake');
//var xevents = require('./xevents');

var EventEmitter = require('events').EventEmitter;
var PackStream = require('./unpackstream');
var coreRequestsTemplate = require('./corereqs');
var hexy = require('./hexy').hexy;

var Buffer = require('buffer').Buffer;
// add 'unpack' method for buffer
require('./unpackbuffer').addUnpack(Buffer);

var xerrors = require('./xerrors');
var coreRequests = require('./corereqs');

function XClient(stream)
{
    EventEmitter.call(this);
    this.stream = stream;

    this.core_requests = {};
    this.ext_requests = {};

    pack_stream = new PackStream();

    // data received from stream is dispached to
    // read requests set by calls to .unpack and .unpackTo
    //stream.pipe(pack_stream);

     // pack_stream write requests are buffered and
    // flushed to stream as result of call to .flush
    // TODO: listen for drain event and flush automatically
    //pack_stream.pipe(stream);

    pack_stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'from packer '}));
        stream.write(data);
    });
    stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'to unpacker '}));
        pack_stream.write(data);
    });

    this.pack_stream = pack_stream;

    this.rcrc_id = 0; // generated for each new resource
    this.seq_num = 0; // incremented in each request. (even if we don't expect reply)

    // in/out packets indexed by sequence ID
    this.requests = {};
    this.replies = {};
    this.events = {};

    this.importRequestsFromTemplates(this, coreRequests);
    this.startHandshake();

    this.event_consumers = {}; // maps window id to eventemitter

    // import available extentions
    // TODO: lazy import on first call?
    /*
    this.ext = {};
    this.ListExtensions( function(err, extentionsList ) {
        for (ext in extentionsList) {
            var extRequests = require('./ext/' + extentionsList[ext]);
            // TODO: need to call QueryExtention to get [major opcode, first event, first error]
            importRequestsFromTemplates(this, extRequests);
        }
    }
    */
    // init comon extentions

}
util.inherits(XClient, EventEmitter);

XClient.prototype.importRequestsFromTemplates = function(target, reqs)
{
    var client = this;
    for (r in reqs)
    {
        // r is request name
        target[r] = (function(reqName) {
            var reqFunc = function req_proxy() {
            client.seq_num++; // TODO: handle overflow (seq should be last 15 (?)  bits of the number
            // is it fast?
            var args = Array.prototype.slice.call(req_proxy.arguments);

            var callback = args.length > 0 ? args[args.length - 1] : null;
            if (callback && callback.constructor.name != 'Function')
                callback = null;

            // TODO: see how much we can calculate in advance (not in each request)
            var reqReplTemplate = reqs[reqName];
            var reqTemplate  = reqReplTemplate[0];
            var templateType = typeof reqTemplate;

            if (templateType == 'object')
                templateType = reqTemplate.constructor.name;

            if (templateType == 'function')
            {
                 // call template with input arguments (not including callback which is last argument TODO currently with callback. won't hurt)
                 //reqPack = reqTemplate.call(args);
                 reqPack = reqTemplate.apply(this, req_proxy.arguments);
                 var format = reqPack[0];
                 var requestArguments = reqPack[1];

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];

                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else if (templateType == 'Array'){
                 var format = reqTemplate[0];
                 var requestArguments = reqTemplate[1];
                 for (a in args)
                     requestArguments.push(args[a]);

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];

                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else {
                 throw 'unknown request format - ' + templateType;
            }
        }
        return reqFunc;
        })(r);
    }
}

XClient.prototype.AllocID = function()
{
    // TODO: handle overflow (XCMiscGetXIDRange from XC_MISC ext)
    // TODO: unused id buffer
    this.display.rsrc_id++;
    return (this.display.rsrc_id << this.display.rsrc_shift) + this.display.resource_base;
}

XClient.prototype.unpackEvent = function(type, seq, extra, raw)
{
    var event = {}; // TODO: constructor & base functions
    event.type = type;
    event.seq = seq;

    if (type == 6) { // motion event
        var values = raw.unpack('LLLSSSSSC'); //TODO: should be LLLLsssssSC
        //event.raw = values;
        // TODO: use unpackTo???
        event.time = extra;
        event.root = values[0];
        event.wid = values[1];
        event.child = values[2];
        event.rootx = values[3];
        event.rooty = values[4];
        event.x = values[5];
        event.y = values[6];
        event.buttons = values[7];
        event.sameScreen = values[8];
    } else if (type == 12) { // Expose
        var values = raw.unpack('LSSSSS');
        event.wid = values[0];
        event.x = values[1];
        event.y = values[2];
        event.width = values[3];
        event.height = values[4];
        event.count = values[5]; // TODO: ???
    }
    return event;
}

XClient.prototype.expectReplyHeader = function()
{
    // TODO: BigReq!!!!

    var client = this;
    client.pack_stream.unpack(
       'CCSL', function(res) {
            var type = res[0];
            var seq_num = res[2];
            if (type == 0)
            {
                var error_code = res[1];
                // unpack error packet (32 bytes for all error types, 8 of them in CCSL header)
                client.pack_stream.get(24, function(buf) {
                    // TODO: dispatch, use sequence number
                    console.error('error!!!!' + xerrors.errorText[error_code]);
                    client.expectReplyHeader();
                } );
                return;
            } else if (type > 1)
            {
                client.pack_stream.get(24, function(buf) {
                    var extra = res[3];
                    var ev = client.unpackEvent(type, seq_num, extra, buf);
                    client.emit('event', ev);
                    var ee = client.event_consumers[ev.wid];
                    if (ee) {
                       ee.emit('event', ev);
                    }
                    client.expectReplyHeader();
                } );
                return;
            }

            var opt_data = res[1];
            var length_total = res[3];            // in 4-bytes units, _including_ this header
            var bodylength = 24 + length_total*4; // 24 is rest if 32-bytes header

            client.pack_stream.get( bodylength, function( data ) {

                var handler = client.replies[seq_num];
                if (handler) {
                    var reqName = handler[0];
                    var req = coreRequests[reqName];
                    var unpack = req[1];
                    var result = unpack( data );
                    var callback = handler[1];
                    callback(result);
                }

                // wait for new packet from server
                client.expectReplyHeader();
            });
        }
    );
}

XClient.prototype.startHandshake = function()
{
    var client = this;

    handshake.writeClientHello(this.pack_stream);
    handshake.readServerHello(this.pack_stream, function(display)
    {
        // TODO: readServerHello can set erro state in display
        // emit error in that case
        client.expectReplyHeader();
        client.display = display;
        client.emit('connect', display);
    });
}

var platformDefaultTransport = {
   win32: 'tcp',
   win64: 'tcp',
   cygwin: 'tcp',
   linux: 'unix'
   // TODO: check process.platform on SmartMachine solaris box
}

module.exports.createClient = function()
{
    // TODO: parse $DISPLAY

    // open stream
    var stream;
    var defaultTransportName = platformDefaultTransport[process.platform];
    // use tcp if stated explicitly or if not defined at all
    if (!defaultTransportName || defaultTransportName == 'tcp')
        stream = net.createConnection(6000);
    if (defaultTransportName == 'unix')
        stream = net.createConnection('/tmp/.X11-unix/X0');

   return new XClient(stream);
}
<commit_msg>initial error reply unpacking, client emits 'error' event<commit_after>var util = require('util'); // util.inherits
var net = require('net');

var handshake = require('./handshake');
//var xevents = require('./xevents');

var EventEmitter = require('events').EventEmitter;
var PackStream = require('./unpackstream');
var coreRequestsTemplate = require('./corereqs');
var hexy = require('./hexy').hexy;

var Buffer = require('buffer').Buffer;
// add 'unpack' method for buffer
require('./unpackbuffer').addUnpack(Buffer);

var xerrors = require('./xerrors');
var coreRequests = require('./corereqs');

function XClient(stream)
{
    EventEmitter.call(this);
    this.stream = stream;

    this.core_requests = {};
    this.ext_requests = {};

    pack_stream = new PackStream();

    // data received from stream is dispached to
    // read requests set by calls to .unpack and .unpackTo
    //stream.pipe(pack_stream);

     // pack_stream write requests are buffered and
    // flushed to stream as result of call to .flush
    // TODO: listen for drain event and flush automatically
    //pack_stream.pipe(stream);

    pack_stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'from packer '}));
        stream.write(data);
    });
    stream.on('data', function( data ) {
        //console.error(hexy(data, {prefix: 'to unpacker '}));
        pack_stream.write(data);
    });

    this.pack_stream = pack_stream;

    this.rcrc_id = 0; // generated for each new resource
    this.seq_num = 0; // incremented in each request. (even if we don't expect reply)

    // in/out packets indexed by sequence ID
    this.requests = {};
    this.replies = {};
    this.events = {};

    this.importRequestsFromTemplates(this, coreRequests);
    this.startHandshake();

    this.event_consumers = {}; // maps window id to eventemitter

    // import available extentions
    // TODO: lazy import on first call?
    /*
    this.ext = {};
    this.ListExtensions( function(err, extentionsList ) {
        for (ext in extentionsList) {
            var extRequests = require('./ext/' + extentionsList[ext]);
            // TODO: need to call QueryExtention to get [major opcode, first event, first error]
            importRequestsFromTemplates(this, extRequests);
        }
    }
    */
    // init comon extentions

}
util.inherits(XClient, EventEmitter);

XClient.prototype.importRequestsFromTemplates = function(target, reqs)
{
    var client = this;
    for (r in reqs)
    {
        // r is request name
        target[r] = (function(reqName) {
            var reqFunc = function req_proxy() {
            client.seq_num++; // TODO: handle overflow (seq should be last 15 (?)  bits of the number
            // is it fast?
            var args = Array.prototype.slice.call(req_proxy.arguments);

            var callback = args.length > 0 ? args[args.length - 1] : null;
            if (callback && callback.constructor.name != 'Function')
                callback = null;

            // TODO: see how much we can calculate in advance (not in each request)
            var reqReplTemplate = reqs[reqName];
            var reqTemplate  = reqReplTemplate[0];
            var templateType = typeof reqTemplate;

            if (templateType == 'object')
                templateType = reqTemplate.constructor.name;

            if (templateType == 'function')
            {
                 // call template with input arguments (not including callback which is last argument TODO currently with callback. won't hurt)
                 //reqPack = reqTemplate.call(args);
                 reqPack = reqTemplate.apply(this, req_proxy.arguments);
                 var format = reqPack[0];
                 var requestArguments = reqPack[1];

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];

                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else if (templateType == 'Array'){
                 var format = reqTemplate[0];
                 var requestArguments = reqTemplate[1];
                 for (a in args)
                     requestArguments.push(args[a]);

                 if (callback)
                     this.replies[this.seq_num] = [reqName, callback];

                 //console.error([format, requestArguments]);
                 client.pack_stream.pack(format, requestArguments);
                 client.pack_stream.flush();
            } else {
                 throw 'unknown request format - ' + templateType;
            }
        }
        return reqFunc;
        })(r);
    }
}

XClient.prototype.AllocID = function()
{
    // TODO: handle overflow (XCMiscGetXIDRange from XC_MISC ext)
    // TODO: unused id buffer
    this.display.rsrc_id++;
    return (this.display.rsrc_id << this.display.rsrc_shift) + this.display.resource_base;
}

XClient.prototype.unpackEvent = function(type, seq, extra, raw)
{
    var event = {}; // TODO: constructor & base functions
    event.type = type;
    event.seq = seq;

    if (type == 6) { // motion event
        var values = raw.unpack('LLLSSSSSC'); //TODO: should be LLLLsssssSC
        //event.raw = values;
        // TODO: use unpackTo???
        event.time = extra;
        event.root = values[0];
        event.wid = values[1];
        event.child = values[2];
        event.rootx = values[3];
        event.rooty = values[4];
        event.x = values[5];
        event.y = values[6];
        event.buttons = values[7];
        event.sameScreen = values[8];
    } else if (type == 12) { // Expose
        var values = raw.unpack('LSSSSS');
        event.wid = values[0];
        event.x = values[1];
        event.y = values[2];
        event.width = values[3];
        event.height = values[4];
        event.count = values[5]; // TODO: ???
    }
    return event;
}

XClient.prototype.expectReplyHeader = function()
{
    // TODO: BigReq!!!!

    var client = this;
    client.pack_stream.unpack(
       'CCSL', function(res) {
            var type = res[0];
            var seq_num = res[2];
            if (type == 0)
            {
                var error_code = res[1];
                var error = {};
                error.code = error_code;
                error.seq = seq_num;
                error.message = xerrors.errorText[error_code];

                // unpack error packet (32 bytes for all error types, 8 of them in CCSL header)
                client.pack_stream.get(24, function(buf) {
                    // TODO: dispatch, use sequence number
                    //TODO: add more generic way to read common values
                    // if (error_code == 14)
                    {
                        var res = buf.unpack('LSC');
                        error.badPparam = res[0]; // id: GC, WinID, Font, Atom etc; Value
                        error.minorOpcode = res[1];
                        error.majorOpcode = res[2];
                    }
                    client.emit('error', error);
                    client.expectReplyHeader();
                } );
                return;
            } else if (type > 1)
            {
                client.pack_stream.get(24, function(buf) {
                    var extra = res[3];
                    var ev = client.unpackEvent(type, seq_num, extra, buf);
                    client.emit('event', ev);
                    var ee = client.event_consumers[ev.wid];
                    if (ee) {
                       ee.emit('event', ev);
                    }
                    client.expectReplyHeader();
                } );
                return;
            }

            var opt_data = res[1];
            var length_total = res[3];            // in 4-bytes units, _including_ this header
            var bodylength = 24 + length_total*4; // 24 is rest if 32-bytes header

            client.pack_stream.get( bodylength, function( data ) {

                var handler = client.replies[seq_num];
                if (handler) {
                    var reqName = handler[0];
                    var req = coreRequests[reqName];
                    var unpack = req[1];
                    var result = unpack( data );
                    var callback = handler[1];
                    callback(result);
                }

                // wait for new packet from server
                client.expectReplyHeader();
            });
        }
    );
}

XClient.prototype.startHandshake = function()
{
    var client = this;

    handshake.writeClientHello(this.pack_stream);
    handshake.readServerHello(this.pack_stream, function(display)
    {
        // TODO: readServerHello can set erro state in display
        // emit error in that case
        client.expectReplyHeader();
        client.display = display;
        client.emit('connect', display);
    });
}

var platformDefaultTransport = {
   win32: 'tcp',
   win64: 'tcp',
   cygwin: 'tcp',
   linux: 'unix'
   // TODO: check process.platform on SmartMachine solaris box
}

module.exports.createClient = function()
{
    // TODO: parse $DISPLAY

    // open stream
    var stream;
    var defaultTransportName = platformDefaultTransport[process.platform];
    // use tcp if stated explicitly or if not defined at all
    if (!defaultTransportName || defaultTransportName == 'tcp')
        stream = net.createConnection(6000);
    if (defaultTransportName == 'unix')
        stream = net.createConnection('/tmp/.X11-unix/X0');

   return new XClient(stream);
}
<|endoftext|>


<commit_before>/** @hidden */
module.exports = function patchPTYModule (mod) {
  const oldSpawn = mod.spawn
  if (mod.patched) {
    return
  }
  mod.patched = true
  mod.spawn = (file, args, opt) => {
    let terminal = oldSpawn(file, args, opt)
    let timeout = null
    let buffer = Buffer.from('')
    let lastFlush = 0
    let nextTimeout = 0

    // Minimum prebuffering window (ms) if the input is non-stop flowing
    const minWindow = 10

    // Maximum buffering time (ms) until output must be flushed unconditionally
    const maxWindow = 100

    function flush () {
        if (buffer.length) {
            terminal.emit('data-buffered', buffer)
        }
        lastFlush = Date.now()
        buffer = Buffer.from('')
    }

    function reschedule () {
        if (timeout) {
            clearTimeout(timeout)
        }
        nextTimeout = Date.now() + minWindow
        timeout = setTimeout(() => {
            timeout = null
            flush()
        }, minWindow)
    }

    terminal.on('data', data => {
        buffer = Buffer.concat([buffer, data])
        if (Date.now() - lastFlush > maxWindow) {
            // Taking too much time buffering, flush to keep things interactive
            flush()
        } else {
            if (Date.now() > nextTimeout - maxWindow / 10) {
                // Extend the window if it's expiring
                reschedule()
            }
        }
    })
    return terminal
  }
}
<commit_msg>don't crash if the PTY suddenly produces strings (#2004, fixes #1955)<commit_after>/** @hidden */
module.exports = function patchPTYModule (mod) {
  const oldSpawn = mod.spawn
  if (mod.patched) {
    return
  }
  mod.patched = true
  mod.spawn = (file, args, opt) => {
    let terminal = oldSpawn(file, args, opt)
    let timeout = null
    let buffer = Buffer.from('')
    let lastFlush = 0
    let nextTimeout = 0

    // Minimum prebuffering window (ms) if the input is non-stop flowing
    const minWindow = 10

    // Maximum buffering time (ms) until output must be flushed unconditionally
    const maxWindow = 100

    function flush () {
        if (buffer.length) {
            terminal.emit('data-buffered', buffer)
        }
        lastFlush = Date.now()
        buffer = Buffer.from('')
    }

    function reschedule () {
        if (timeout) {
            clearTimeout(timeout)
        }
        nextTimeout = Date.now() + minWindow
        timeout = setTimeout(() => {
            timeout = null
            flush()
        }, minWindow)
    }

    terminal.on('data', data => {
        if (typeof data === 'string') {
            data = Buffer.from(data)
        }
        buffer = Buffer.concat([buffer, data])
        if (Date.now() - lastFlush > maxWindow) {
            // Taking too much time buffering, flush to keep things interactive
            flush()
        } else {
            if (Date.now() > nextTimeout - maxWindow / 10) {
                // Extend the window if it's expiring
                reschedule()
            }
        }
    })
    return terminal
  }
}
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter;
var util = require('util');
var request;
if (process.title === 'browser')
    request = require('browser-request');
else {
    var requestPath = 'request';
    request = require(requestPath);
}
var ltx = require('ltx');

function BOSHConnection(opts) {
    var that = this;
    EventEmitter.call(this);

    this.boshURL = opts.boshURL;
    this.jid = opts.jid;
    this.xmlnsAttrs = {
	xmlns: "http://jabber.org/protocol/httpbind",
	'xmlns:xmpp': "urn:xmpp:xbosh",
	'xmlns:stream': "http://etherx.jabber.org/streams"
    };
    if (opts.xmlns)
	for(var prefix in opts.xmlns)
	    if (prefix)
		this.xmlnsAttrs["xmlns:" + prefix] = opts.xmlns[prefix];
	    else
		this.xmlnsAttrs["xmlns"] = opts.xmlns[prefix];
    this.currentRequests = 0;
    this.queue = [];
    this.rid = Math.ceil(Math.random() * 9999999999);

    this.request({
	to: this.jid.domain,
	ver: "1.6",
	wait: "10",
	hold: "1",
	content: this.contentType
    }, [], function(err, bodyEl) {
	if (err) {
	    that.emit('error', err);
	} else if (bodyEl && bodyEl.attrs) {
	    that.sid = bodyEl.attrs.sid;
	    that.maxRequests = parseInt(bodyEl.attrs.requests, 10) || 2;
	    if (that.sid && that.maxRequests > 0) {
		that.emit('connect');
		that.processResponse(bodyEl);
		process.nextTick(that.mayRequest.bind(that));
	    } else
		that.emit('error', "Invalid parameters");
	}
    });
}
util.inherits(BOSHConnection, EventEmitter);
exports.BOSHConnection = BOSHConnection;

BOSHConnection.prototype.contentType = "text/xml; charset=utf-8";

BOSHConnection.prototype.send = function(stanza) {
    this.queue.push(stanza.root());
    process.nextTick(this.mayRequest.bind(this));
};

BOSHConnection.prototype.processResponse = function(bodyEl) {
    if (bodyEl && bodyEl.children) {
	for(var i = 0; i < bodyEl.children.length; i++) {
	    var child = bodyEl.children[i];
	    if (child.name && child.attrs && child.children)
		this.emit('stanza', child);
	}
    }
    if (bodyEl && bodyEl.attrs.type === 'terminate') {
	this.emit('error', new Error(bodyEl.attrs.condition || "Session terminated"));
	this.emit('close');
    }
};

BOSHConnection.prototype.mayRequest = function() {
    var that = this;
    var canRequest =
	/* Must have a session already */
	this.sid &&
	/* We can only receive when one request is in flight */
	(this.currentRequests === 0 ||
	 /* Is there something to send, and are we allowed? */
	 ((this.queue.length > 0 && this.currentRequests < this.maxRequests))
	);
    if (!canRequest)
	return;

    var stanzas = this.queue;
    this.queue = [];
    this.request({}, stanzas, function(err, bodyEl) {
	if (!err && bodyEl)
	    that.processResponse(bodyEl);

	process.nextTick(that.mayRequest.bind(that));
    });
};

BOSHConnection.prototype.end = function(stanzas) {
    stanzas = stanzas || [];
    if (typeof stanzas !== 'array')
	stanzas = [stanzas];

    stanzas = this.queue.concat(stanzas);
    this.queue = [];

    var that = this;
    this.request({ type: 'terminate' }, stanzas, function(err, bodyEl) {
	if (bodyEl)
	    that.processResponse(bodyEl);

	that.emit('end');
	that.emit('close');
	delete that.sid;
    });
};

BOSHConnection.prototype.request = function(attrs, children, cb) {
    var that = this;

    attrs.rid = this.rid.toString();
    this.rid++;
    if (this.sid)
	attrs.sid = this.sid;

    for(var k in this.xmlnsAttrs)
	attrs[k] = this.xmlnsAttrs[k];
    var boshEl = new ltx.Element('body', attrs);
    for(var i = 0; i < children.length; i++)
	boshEl.cnode(children[i]);

    request({
	uri: this.boshURL,
	method: 'POST',
	headers: {
	    "Content-Type": this.contentType
	},
	body: boshEl.toString()
    }, function(err, res, body) {
	that.currentRequests--;

	if (err)
	    return cb(err);
	if (res.statusCode < 200 || res.statusCode >= 400)
	    return cb(new Error("HTTP status " + res.statusCode));

	var bodyEl;
	try {
	    bodyEl = ltx.parse(body);
	} catch(e) {
	    return cb(e);
	}

	if (bodyEl && bodyEl.attrs.type === 'terminate')
	    cb(new Error(bodyEl.attrs.condition));
	else if (bodyEl)
	    cb(null, bodyEl);
	else
	    cb(new Error('no <body/>'));
    });
    this.currentRequests++;
};
<commit_msg>bosh: retry 5x on request errors, bail out on all other errors<commit_after>var EventEmitter = require('events').EventEmitter;
var util = require('util');
var request;
if (process.title === 'browser')
    request = require('browser-request');
else {
    var requestPath = 'request';
    request = require(requestPath);
}
var ltx = require('ltx');

function BOSHConnection(opts) {
    var that = this;
    EventEmitter.call(this);

    this.boshURL = opts.boshURL;
    this.jid = opts.jid;
    this.xmlnsAttrs = {
	xmlns: "http://jabber.org/protocol/httpbind",
	'xmlns:xmpp': "urn:xmpp:xbosh",
	'xmlns:stream': "http://etherx.jabber.org/streams"
    };
    if (opts.xmlns)
	for(var prefix in opts.xmlns)
	    if (prefix)
		this.xmlnsAttrs["xmlns:" + prefix] = opts.xmlns[prefix];
	    else
		this.xmlnsAttrs["xmlns"] = opts.xmlns[prefix];
    this.currentRequests = 0;
    this.queue = [];
    this.rid = Math.ceil(Math.random() * 9999999999);

    this.request({
	to: this.jid.domain,
	ver: "1.6",
	wait: "10",
	hold: "1",
	content: this.contentType
    }, [], function(err, bodyEl) {
	if (err) {
	    that.emit('error', err);
	} else if (bodyEl && bodyEl.attrs) {
	    that.sid = bodyEl.attrs.sid;
	    that.maxRequests = parseInt(bodyEl.attrs.requests, 10) || 2;
	    if (that.sid && that.maxRequests > 0) {
		that.emit('connect');
		that.processResponse(bodyEl);
		process.nextTick(that.mayRequest.bind(that));
	    } else
		that.emit('error', "Invalid parameters");
	}
    });
}
util.inherits(BOSHConnection, EventEmitter);
exports.BOSHConnection = BOSHConnection;

BOSHConnection.prototype.contentType = "text/xml; charset=utf-8";

BOSHConnection.prototype.send = function(stanza) {
    this.queue.push(stanza.root());
    process.nextTick(this.mayRequest.bind(this));
};

BOSHConnection.prototype.processResponse = function(bodyEl) {
    if (bodyEl && bodyEl.children) {
	for(var i = 0; i < bodyEl.children.length; i++) {
	    var child = bodyEl.children[i];
	    if (child.name && child.attrs && child.children)
		this.emit('stanza', child);
	}
    }
    if (bodyEl && bodyEl.attrs.type === 'terminate') {
	this.emit('error', new Error(bodyEl.attrs.condition || "Session terminated"));
	this.emit('close');
    }
};

BOSHConnection.prototype.mayRequest = function() {
    var that = this;
    var canRequest =
	/* Must have a session already */
	this.sid &&
	/* We can only receive when one request is in flight */
	(this.currentRequests === 0 ||
	 /* Is there something to send, and are we allowed? */
	 ((this.queue.length > 0 && this.currentRequests < this.maxRequests))
	);
    if (!canRequest)
	return;

    var stanzas = this.queue;
    this.queue = [];
    this.request({}, stanzas, function(err, bodyEl) {
	if (err) {
	    that.emit('error', err);
	    that.emit('close');
	    delete that.sid;
	} else {
	    if (bodyEl)
		that.processResponse(bodyEl);

	    process.nextTick(that.mayRequest.bind(that));
	}
    });
};

BOSHConnection.prototype.end = function(stanzas) {
    stanzas = stanzas || [];
    if (typeof stanzas !== 'array')
	stanzas = [stanzas];

    stanzas = this.queue.concat(stanzas);
    this.queue = [];

    var that = this;
    this.request({ type: 'terminate' }, stanzas, function(err, bodyEl) {
	if (bodyEl)
	    that.processResponse(bodyEl);

	that.emit('end');
	that.emit('close');
	delete that.sid;
    });
};

BOSHConnection.prototype.maxHTTPRetries = 5;

BOSHConnection.prototype.request = function(attrs, children, cb, retry) {
    var that = this;
    retry = retry || 0;

    attrs.rid = this.rid.toString();
    this.rid++;
    if (this.sid)
	attrs.sid = this.sid;

    for(var k in this.xmlnsAttrs)
	attrs[k] = this.xmlnsAttrs[k];
    var boshEl = new ltx.Element('body', attrs);
    for(var i = 0; i < children.length; i++)
	boshEl.cnode(children[i]);

    request({
	uri: this.boshURL,
	method: 'POST',
	headers: {
	    "Content-Type": this.contentType
	},
	body: boshEl.toString()
    }, function(err, res, body) {
	that.currentRequests--;

	if (err) {
	    if (retry < that.maxHTTPRetries)
		return request(attrs, children, cb, retry + 1);
	    else
		return cb(err);
	}
	if (res.statusCode < 200 || res.statusCode >= 400)
	    return cb(new Error("HTTP status " + res.statusCode));

	var bodyEl;
	try {
	    bodyEl = ltx.parse(body);
	} catch(e) {
	    return cb(e);
	}

	if (bodyEl && bodyEl.attrs.type === 'terminate')
	    cb(new Error(bodyEl.attrs.condition));
	else if (bodyEl)
	    cb(null, bodyEl);
	else
	    cb(new Error('no <body/>'));
    });
    this.currentRequests++;
};
<|endoftext|>


<commit_before>/* **************************************************************

   Copyright 2013 Zoovy, Inc.

   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.

************************************************************** */

/*!
 * isFontFaceSupported - v0.9 - 12/19/2009
 * http://paulirish.com/2009/font-face-feature-detection/
 *
 * Copyright (c) 2009 Paul Irish
 * MIT license
 */

var isFontFaceSupported = (function(){
var
sheet, doc = document,
head = doc.head || doc.getElementsByTagName('head')[0] || docElement,
style = doc.createElement("style"),
impl = doc.implementation || { hasFeature: function() { return false; } };

style.type = 'text/css';
head.insertBefore(style, head.firstChild);
sheet = style.sheet || style.styleSheet;

var supportAtRule = impl.hasFeature('CSS2', '') ?
        function(rule) {
            if (!(sheet && rule)) return false;
            var result = false;
            try {
                sheet.insertRule(rule, 0);
                result = !(/unknown/i).test(sheet.cssRules[0].cssText);
                sheet.deleteRule(sheet.cssRules.length - 1);
            } catch(e) { }
            return result;
        } :
        function(rule) {
            if (!(sheet && rule)) return false;
            sheet.cssText = rule;

            return sheet.cssText.length !== 0 && !(/unknown/i).test(sheet.cssText) &&
              sheet.cssText
                    .replace(/\r+|\n+/g, '')
                    .indexOf(rule.split(' ')[0]) === 0;
        };

return supportAtRule('@font-face { font-family: "font"; src: "font.ttf"; }');
})();

var jerseypreview = function() {
	var r = {
////////////////////////////////////   CALLBACKS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

	callbacks : {
//executed when extension is loaded. should include any validation that needs to occur.
		init : {
			onSuccess : function()	{
				var r = false; //return false if extension won't load for some reason (account config, dependencies, etc).

				//if there is any functionality required for this extension to load, put it here. such as a check for async google, the FB object, etc. return false if dependencies are not present. don't check for other extensions.
				r = true;
				app.rq.push(['templateFunction','productTemplate','onInits', function(P){
					}]);
				app.rq.push(['templateFunction','productTemplate','onCompletes',function(P){
					app.u.dump('Jersey Preview Oncompletes Running');
					var pid = P.pid;
					var $context = $(app.u.jqSelector('#', P.parentID));
					var $canvas = $('canvas.prodPreviewer', $context);
					app.u.dump(app.ext.jerseypreview.vars.paramsByPID[pid]);
					if(!app.ext.jerseypreview.vars.paramsByPID[pid]){
						//In future update, run only for products marked as customizable?
						$.getJSON("extensions/jerseypreview/products/"+pid+".json")
							.done(function(data, textStatus, jqXHR){
								app.u.dump("Checking font face support");
								if(isFontFaceSupported){
									app.u.dump("Font Face Supported");
									if(app.data[P.datapointer]["%attribs"]["zoovy:prod_image8"]){
										font = data.font;
										if(font){
											$context.append($("
Some Text
").css({"height": "0px", "overflow":"hidden"}));
											}
										$('input[name=B5], input[name=B6]', $context).keyup(function(){
											app.u.dump("keyup called");
											if($(this).data('timer')){
												clearTimeout($(this).data('timer'));
												}
											$(this).data('timer', setTimeout(function(){
												app.ext.jerseypreview.u.updatePreview($context);
												}, 600));
											});
										data.imgsrc = app.data[P.datapointer]["%attribs"]["zoovy:prod_image8"];
										app.ext.jerseypreview.vars.paramsByPID[pid] = data;
										app.ext.jerseypreview.u.assignPreviewerData($canvas, data, pid);
										}
									else {
										app.ext.jerseypreview.vars.paramsByPID[pid] = "Supported, but no Image Found";
										}
									}
								})
							.fail(function(datajqXHR, textStatus, errorThrown){
								$canvas.remove();
								app.ext.jerseypreview.vars.paramsByPID[pid] = "Not Supported";
								app.u.dump("JSON failed to load");
								//report failure?
								});
						}
					}]);
				return r;
				},
			onError : function()	{
//errors will get reported for this callback as part of the extensions loading.  This is here for extra error handling purposes.
//you may or may not need it.
				app.u.dump('BEGIN app.ext.cubworld.callbacks.init.onError');
				}
			}

		}, //callbacks

////////////////////////////////////   ACTION    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//actions are functions triggered by a user interaction, such as a click/tap.
//these are going the way of the do do, in favor of app events. new extensions should have few (if any) actions.
		a : {

			}, //Actions

////////////////////////////////////   RENDERFORMATS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//renderFormats are what is used to actually output data.
//on a data-bind, format: is equal to a renderformat. extension: tells the rendering engine where to look for the renderFormat.
//that way, two render formats named the same (but in different extensions) don't overwrite each other.
		renderFormats : {
			//uses pid to grab from map in vars, adds img tag from img8
			}, //renderFormats
////////////////////////////////////   UTIL [u]   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//utilities are typically functions that are exected by an event or action.
//any functions that are recycled should be here.
		u : {
			assignPreviewerData : function($canvas, data, pid){
				data.img = $(app.u.makeImage({
					name : data.imgsrc,
					w : $canvas.attr('width'),
					h : $canvas.attr('height'),
					b : "FFFFFF",
					tag : 1
					})).get(0);

				$canvas.data('preview-params', data);
				},
			updatePreview : function($context){
				//app.u.dump(name+" "+number);
				var $canvas = $('canvas.prodPreviewer', $context);
				if($canvas.length != 0){
					var name = $('input[name=B5]', $context).val().toUpperCase();
					var number = $('input[name=B6]', $context).val().toUpperCase();
					if(name != "" && number != ""){
						var context = $canvas.get(0).getContext('2d');
						var params = $canvas.data('preview-params');
						var w = $canvas.innerWidth();
						var h = $canvas.innerHeight();

						context.fillStyle = "#ffffff";
						context.clearRect(0,0,w,h);
						context.fillRect(0,0,w,h);

						//scratchpads
						var textSize;
						var x;
						//draw image to canvas
						context.drawImage(params.img, 0, 0);

						//draw number to canvas
						context.font = params.number.size+"px "+params.font;
						context.fillStyle = "#"+params.number.color;
						context.strokeStyle = "#"+params.number.strokeColor;
						context.lineWidth = params.number.strokeThickness;
						context.textAlign = "center";

						x = w/2 + params.number.xOffset;

						context.fillText(number, x, params.number.y+params.number.size);
						context.strokeText(number, x, params.number.y+params.number.size);

						//draw name to canvas

						context.font = params.name.size+"px "+params.font;
						context.fillStyle = "#"+params.name.color;
						context.strokeStyle = "#"+params.name.strokeColor;
						context.lineWidth = params.name.strokeThickness;

						x = w/2 + params.name.xOffset;
						this.drawTextAlongArc(context, name, x, params.name.y+params.name.radius, params.name.radius, params.name.charSize);

						//textSize = context.measureText(name);
						//x = (params.name.x < 0 ? w/2 : params.name.x) - textSize.width/2;

						//context.fillText(name, x, params.name.y+params.name.size);
						//context.strokeText(name, x, params.name.y+params.name.size);

						if($canvas.hasClass('displayNone')){
							$canvas.fadeIn();
							$canvas.removeClass('displayNone');
							}
						}
					else  if(!$canvas.hasClass('displayNone')){
						$canvas.fadeOut();
						$canvas.addClass('displayNone');
						}
					else {
						//Nothing to update and canvas is already hidden.  Do nothing.
						}
					}
				},
			drawTextAlongArc : function(context, str, centerX, centerY, radius, charSize) {
				var len = str.length, s;
				context.save();
				context.translate(centerX, centerY);
				//angle is in radians (terms of pi) so string length times charSize is the length of the arc,
				//divide by circumference for a percentage, multiply again by 2pi for rads- so just divide by radius
				var angle = (str.length * charSize) / radius

				context.rotate(-1 * angle / 2);
				context.rotate(-1 * (angle / len) / 2);
				for(var n = 0; n < len; n++) {
				  context.rotate(angle / len);
				  context.save();
				  context.translate(0, -1 * radius);
				  s = str[n];
				  context.fillText(s, 0, 0);
				  context.strokeText(s, 0, 0);
				  context.restore();
				}
				context.restore();
			  }

			}, //u [utilities]

//app-events are added to an element through data-app-event="extensionName|functionName"
//right now, these are not fully supported, but they will be going forward.
//they're used heavily in the admin.html file.
//while no naming convention is stricly forced,
//when adding an event, be sure to do off('click.appEventName') and then on('click.appEventName') to ensure the same event is not double-added if app events were to get run again over the same template.
		e : {
			}, //e [app Events]
		vars : {
			paramsByPID : {

				}
			}
		} //r object.

	return r;
	}<commit_msg>Customizer Messaging<commit_after>/* **************************************************************

   Copyright 2013 Zoovy, Inc.

   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.

************************************************************** */

/*!
 * isFontFaceSupported - v0.9 - 12/19/2009
 * http://paulirish.com/2009/font-face-feature-detection/
 *
 * Copyright (c) 2009 Paul Irish
 * MIT license
 */

var isFontFaceSupported = (function(){
var
sheet, doc = document,
head = doc.head || doc.getElementsByTagName('head')[0] || docElement,
style = doc.createElement("style"),
impl = doc.implementation || { hasFeature: function() { return false; } };

style.type = 'text/css';
head.insertBefore(style, head.firstChild);
sheet = style.sheet || style.styleSheet;

var supportAtRule = impl.hasFeature('CSS2', '') ?
        function(rule) {
            if (!(sheet && rule)) return false;
            var result = false;
            try {
                sheet.insertRule(rule, 0);
                result = !(/unknown/i).test(sheet.cssRules[0].cssText);
                sheet.deleteRule(sheet.cssRules.length - 1);
            } catch(e) { }
            return result;
        } :
        function(rule) {
            if (!(sheet && rule)) return false;
            sheet.cssText = rule;

            return sheet.cssText.length !== 0 && !(/unknown/i).test(sheet.cssText) &&
              sheet.cssText
                    .replace(/\r+|\n+/g, '')
                    .indexOf(rule.split(' ')[0]) === 0;
        };

return supportAtRule('@font-face { font-family: "font"; src: "font.ttf"; }');
})();

var jerseypreview = function() {
	var r = {
////////////////////////////////////   CALLBACKS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

	callbacks : {
//executed when extension is loaded. should include any validation that needs to occur.
		init : {
			onSuccess : function()	{
				var r = false; //return false if extension won't load for some reason (account config, dependencies, etc).

				//if there is any functionality required for this extension to load, put it here. such as a check for async google, the FB object, etc. return false if dependencies are not present. don't check for other extensions.
				r = true;
				app.rq.push(['templateFunction','productTemplate','onInits', function(P){
					}]);
				app.rq.push(['templateFunction','productTemplate','onCompletes',function(P){
					app.u.dump('Jersey Preview Oncompletes Running');
					var pid = P.pid;
					var $context = $(app.u.jqSelector('#', P.parentID));
					var $canvas = $('canvas.prodPreviewer', $context);
					app.u.dump(app.ext.jerseypreview.vars.paramsByPID[pid]);
					if(!app.ext.jerseypreview.vars.paramsByPID[pid]){
						//In future update, run only for products marked as customizable?
						$.getJSON("extensions/jerseypreview/products/"+pid+".json")
							.done(function(data, textStatus, jqXHR){
								app.u.dump("Checking font face support");
								if(isFontFaceSupported){
									app.u.dump("Font Face Supported");
									if(app.data[P.datapointer]["%attribs"]["zoovy:prod_image8"]){
										font = data.font;
										if(font){
											$context.append($("
Some Text
").css({"height": "0px", "overflow":"hidden"}));
											}
										$('input[name=B5], input[name=B6]', $context).keyup(function(){
											app.u.dump("keyup called");
											if($(this).data('timer')){
												clearTimeout($(this).data('timer'));
												}
											$(this).data('timer', setTimeout(function(){
												app.ext.jerseypreview.u.updatePreview($context);
												}, 600));
											});
										data.imgsrc = app.data[P.datapointer]["%attribs"]["zoovy:prod_image8"];
										app.ext.jerseypreview.vars.paramsByPID[pid] = data;
										app.ext.jerseypreview.u.assignPreviewerData($canvas, data, pid);
										}
									else {
										app.ext.jerseypreview.vars.paramsByPID[pid] = "Supported, but no Image Found";
										}
									}
								else {
									app.u.throwMessage("Custom Jersey Previews are not available in your browser's version, sorry!");
									app.ext.jerseypreview.vars.paramsByPID[pid] = "Font Face Not Supported";
									}
								})
							.fail(function(datajqXHR, textStatus, errorThrown){
								$canvas.remove();
								app.ext.jerseypreview.vars.paramsByPID[pid] = "JSON failed to load";
								app.u.dump("JSON failed to load");
								//report failure?
								});
						}
					}]);
				return r;
				},
			onError : function()	{
//errors will get reported for this callback as part of the extensions loading.  This is here for extra error handling purposes.
//you may or may not need it.
				app.u.dump('BEGIN app.ext.cubworld.callbacks.init.onError');
				}
			}

		}, //callbacks

////////////////////////////////////   ACTION    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//actions are functions triggered by a user interaction, such as a click/tap.
//these are going the way of the do do, in favor of app events. new extensions should have few (if any) actions.
		a : {

			}, //Actions

////////////////////////////////////   RENDERFORMATS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//renderFormats are what is used to actually output data.
//on a data-bind, format: is equal to a renderformat. extension: tells the rendering engine where to look for the renderFormat.
//that way, two render formats named the same (but in different extensions) don't overwrite each other.
		renderFormats : {
			//uses pid to grab from map in vars, adds img tag from img8
			}, //renderFormats
////////////////////////////////////   UTIL [u]   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//utilities are typically functions that are exected by an event or action.
//any functions that are recycled should be here.
		u : {
			assignPreviewerData : function($canvas, data, pid){
				data.img = $(app.u.makeImage({
					name : data.imgsrc,
					w : $canvas.attr('width'),
					h : $canvas.attr('height'),
					b : "FFFFFF",
					tag : 1
					})).get(0);

				$canvas.data('preview-params', data);
				},
			updatePreview : function($context){
				//app.u.dump(name+" "+number);
				var $canvas = $('canvas.prodPreviewer', $context);
				if($canvas.length != 0){
					var name = $('input[name=B5]', $context).val().toUpperCase();
					var number = $('input[name=B6]', $context).val().toUpperCase();
					if(name != "" && number != ""){
						var context = $canvas.get(0).getContext('2d');
						var params = $canvas.data('preview-params');
						var w = $canvas.innerWidth();
						var h = $canvas.innerHeight();

						context.fillStyle = "#ffffff";
						context.clearRect(0,0,w,h);
						context.fillRect(0,0,w,h);

						//scratchpads
						var textSize;
						var x;
						//draw image to canvas
						context.drawImage(params.img, 0, 0);

						//draw number to canvas
						context.font = params.number.size+"px "+params.font;
						context.fillStyle = "#"+params.number.color;
						context.strokeStyle = "#"+params.number.strokeColor;
						context.lineWidth = params.number.strokeThickness;
						context.textAlign = "center";

						x = w/2 + params.number.xOffset;

						context.fillText(number, x, params.number.y+params.number.size);
						context.strokeText(number, x, params.number.y+params.number.size);

						//draw name to canvas

						context.font = params.name.size+"px "+params.font;
						context.fillStyle = "#"+params.name.color;
						context.strokeStyle = "#"+params.name.strokeColor;
						context.lineWidth = params.name.strokeThickness;

						x = w/2 + params.name.xOffset;
						this.drawTextAlongArc(context, name, x, params.name.y+params.name.radius, params.name.radius, params.name.charSize);

						//textSize = context.measureText(name);
						//x = (params.name.x < 0 ? w/2 : params.name.x) - textSize.width/2;

						//context.fillText(name, x, params.name.y+params.name.size);
						//context.strokeText(name, x, params.name.y+params.name.size);

						if($canvas.hasClass('displayNone')){
							$canvas.fadeIn();
							$canvas.removeClass('displayNone');
							}
						}
					else  if(!$canvas.hasClass('displayNone')){
						$canvas.fadeOut();
						$canvas.addClass('displayNone');
						}
					else {
						//Nothing to update and canvas is already hidden.  Do nothing.
						}
					}
				},
			drawTextAlongArc : function(context, str, centerX, centerY, radius, charSize) {
				var len = str.length, s;
				context.save();
				context.translate(centerX, centerY);
				//angle is in radians (terms of pi) so string length times charSize is the length of the arc,
				//divide by circumference for a percentage, multiply again by 2pi for rads- so just divide by radius
				var angle = (str.length * charSize) / radius

				context.rotate(-1 * angle / 2);
				context.rotate(-1 * (angle / len) / 2);
				for(var n = 0; n < len; n++) {
				  context.rotate(angle / len);
				  context.save();
				  context.translate(0, -1 * radius);
				  s = str[n];
				  context.fillText(s, 0, 0);
				  context.strokeText(s, 0, 0);
				  context.restore();
				}
				context.restore();
			  }

			}, //u [utilities]

//app-events are added to an element through data-app-event="extensionName|functionName"
//right now, these are not fully supported, but they will be going forward.
//they're used heavily in the admin.html file.
//while no naming convention is stricly forced,
//when adding an event, be sure to do off('click.appEventName') and then on('click.appEventName') to ensure the same event is not double-added if app events were to get run again over the same template.
		e : {
			}, //e [app Events]
		vars : {
			paramsByPID : {

				}
			}
		} //r object.

	return r;
	}<|endoftext|>