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

var net = require('net');
var url = require('url');
var http = require('http');
var assert = require('assert');
var inherits = require('util').inherits;
var debug = require('debug')('icecast:client');
var Reader = require('./reader');
var preprocess = require('./preprocessor');

/**
 * Module exports.
 */

exports = module.exports = Client;

/**
 * The `Client` class is a subclass of the `http.ClientRequest` object.
 *
 * It adds a stream preprocessor to make "ICY" responses work. This is only needed
 * because of the strictness of node's HTTP parser. I'll volley for ICY to be
 * supported in the http header for the JavaScript HTTP rewrite (v0.12 of node?).
 *
 * The other big difference is that it passes a `icecast.Reader` instance instead
 * of a `http.ClientResponse` instance to the "response" event callback.
 */

function Client (options, cb) {
  if (typeof options === 'string') {
    options = url.parse(options);
  }

  if (options.protocol && options.protocol !== 'http:') {
    throw new Error('Protocol:' + options.protocol + ' not supported.');
  }

  // TODO: extend instead of mutating the opts directly
  options.createConnection = createConnection;

  // add the "Icy-MetaData" header
  if (!options.headers) options.headers = {};
  options.headers['Icy-MetaData'] = 1;

  // force a new http.Agent instance by default
  if (!options.agent) {
    options.agent = new http.Agent();
    options.agent.createConnection = createConnection;
  }

  http.ClientRequest.call(this, options, onResponse);

  this.realOn = this.on;
  this.on = this.addListener = on;
  // TODO: overwrite removeListener

  if ('function' == typeof cb) {
    this.on('response', cb);
  }
}
inherits(Client, http.ClientRequest);

/**
 * The overwritten "on" function translates new "response" events into
 * "icecastResponse".
 */

function on (name, fn) {
  debug('on: %s', name);
  if ('response' == name) {
    debug('remapping as "icecastResponse" listener', fn);
    name = 'icecastResponse';
  }
  return this.realOn(name, fn);
}

/**
 * The "response" event callback.
 */

function onResponse (res) {
  debug('onResponse');
  var s = res;
  var metaint = res.headers['icy-metaint'];
  if (metaint) {
    debug('got metaint: %d', metaint);
    s = new Reader(metaint);
    res.pipe(s);

    s.res = res;
    proxy(s, 'headers');
    proxy(s, 'statusCode');
    proxy(s, 'httpVersion');
  }
  this.emit('icecastResponse', s);
}

/**
 * Proxies "key" from `stream` to `stream.res`.
 */

function proxy (stream, key) {
  function get () {
    return stream.res[key];
  }
  function set (v) {
    return stream.res[key] = v;
  }
  Object.defineProperty(stream, key, {
    configurable: true,
    enumerable: true,
    get: get,
    set: set
  });
}

/**
 * This block mostly coped from the "https" core module. What a mess...
 *
 * This function creates a regular net connection to the destination, but returns
 * a Preprocessor stream that basically does this s/^ICY/HTTP\\1\.0/i, because of
 * node's strict HTTP parser.
 */

function createConnection(/* [port, host, options] */) {
  var options = {};

  if (typeof arguments[0] === 'object') {
    options = arguments[0];
  } else if (typeof arguments[1] === 'object') {
    options = arguments[1];
    options.port = arguments[0];
  } else if (typeof arguments[2] === 'object') {
    options = arguments[2];
    options.port = arguments[0];
    options.host = arguments[1];
  } else {
    if (typeof arguments[0] === 'number') {
      options.port = arguments[0];
    }
    if (typeof arguments[1] === 'string') {
      options.host = arguments[1];
    }
  }

  // TODO: SSL support? (I haven't found any streams that support that, but
  // node-icecast's `Server` could do that...)
  var stream = net.createConnection(options);

  // we have to preprocess the stream (that is, intercept "data" events and emit
  // our own) to make the invalid "ICY" http version get translated into
  // "HTTP/1.0"
  preprocess(stream);

  return stream;
}
<commit_msg>client: add instanceof check<commit_after>
/**
 * Module dependencies.
 */

var net = require('net');
var url = require('url');
var http = require('http');
var assert = require('assert');
var inherits = require('util').inherits;
var debug = require('debug')('icecast:client');
var Reader = require('./reader');
var preprocess = require('./preprocessor');

/**
 * Module exports.
 */

exports = module.exports = Client;

/**
 * The `Client` class is a subclass of the `http.ClientRequest` object.
 *
 * It adds a stream preprocessor to make "ICY" responses work. This is only needed
 * because of the strictness of node's HTTP parser. I'll volley for ICY to be
 * supported in the http header for the JavaScript HTTP rewrite (v0.12 of node?).
 *
 * The other big difference is that it passes a `icecast.Reader` instance instead
 * of a `http.ClientResponse` instance to the "response" event callback.
 */

function Client (options, cb) {
  if (!(this instanceof Client)) {
    return new Client(options, cb);
  }

  if (typeof options === 'string') {
    options = url.parse(options);
  }

  if (options.protocol && options.protocol !== 'http:') {
    throw new Error('Protocol:' + options.protocol + ' not supported.');
  }

  // TODO: extend instead of mutating the opts directly
  options.createConnection = createConnection;

  // add the "Icy-MetaData" header
  if (!options.headers) options.headers = {};
  options.headers['Icy-MetaData'] = 1;

  // force a new http.Agent instance by default
  if (!options.agent) {
    options.agent = new http.Agent();
    options.agent.createConnection = createConnection;
  }

  http.ClientRequest.call(this, options, onResponse);

  this.realOn = this.on;
  this.on = this.addListener = on;
  // TODO: overwrite removeListener

  if ('function' == typeof cb) {
    this.on('response', cb);
  }
}
inherits(Client, http.ClientRequest);

/**
 * The overwritten "on" function translates new "response" events into
 * "icecastResponse".
 */

function on (name, fn) {
  debug('on: %s', name);
  if ('response' == name) {
    debug('remapping as "icecastResponse" listener', fn);
    name = 'icecastResponse';
  }
  return this.realOn(name, fn);
}

/**
 * The "response" event callback.
 */

function onResponse (res) {
  debug('onResponse');
  var s = res;
  var metaint = res.headers['icy-metaint'];
  if (metaint) {
    debug('got metaint: %d', metaint);
    s = new Reader(metaint);
    res.pipe(s);

    s.res = res;
    proxy(s, 'headers');
    proxy(s, 'statusCode');
    proxy(s, 'httpVersion');
  }
  this.emit('icecastResponse', s);
}

/**
 * Proxies "key" from `stream` to `stream.res`.
 */

function proxy (stream, key) {
  function get () {
    return stream.res[key];
  }
  function set (v) {
    return stream.res[key] = v;
  }
  Object.defineProperty(stream, key, {
    configurable: true,
    enumerable: true,
    get: get,
    set: set
  });
}

/**
 * This block mostly coped from the "https" core module. What a mess...
 *
 * This function creates a regular net connection to the destination, but returns
 * a Preprocessor stream that basically does this s/^ICY/HTTP\\1\.0/i, because of
 * node's strict HTTP parser.
 */

function createConnection(/* [port, host, options] */) {
  var options = {};

  if (typeof arguments[0] === 'object') {
    options = arguments[0];
  } else if (typeof arguments[1] === 'object') {
    options = arguments[1];
    options.port = arguments[0];
  } else if (typeof arguments[2] === 'object') {
    options = arguments[2];
    options.port = arguments[0];
    options.host = arguments[1];
  } else {
    if (typeof arguments[0] === 'number') {
      options.port = arguments[0];
    }
    if (typeof arguments[1] === 'string') {
      options.host = arguments[1];
    }
  }

  // TODO: SSL support? (I haven't found any streams that support that, but
  // node-icecast's `Server` could do that...)
  var stream = net.createConnection(options);

  // we have to preprocess the stream (that is, intercept "data" events and emit
  // our own) to make the invalid "ICY" http version get translated into
  // "HTTP/1.0"
  preprocess(stream);

  return stream;
}
<|endoftext|>


<commit_before>// View model for the courses page
function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.numberOfCompetencesToAdd = 0;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value'), function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };
}

function addCompetence() {
    var competencePanelWrapper = document.createElement('div');
    var competencePanel = document.createElement('div');
    var competencePanelHeading = document.createElement('div');
    var competencePanelBody = document.createElement('div');
    var competencePanelFooter = document.createElement('div');
    var competenceCode = document.createElement('input');
    var competenceName = document.createElement('input');
    var subcompetenceButton = document.createElement('button');
    var removeCompetenceButton = document.createElement('button');

    competenceCode.type = 'text';
    competenceCode.placeholder = "Competence-Code";
    $(competenceCode).addClass("form-control");

    competenceName.type = 'text';
    competenceName.placeholder = "Name of the competence";
    $(competenceName).addClass("form-control");

    $(subcompetenceButton).addClass("btn");
    $(subcompetenceButton).text("Add a subcompetence");
    $(subcompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(subcompetenceButton).on('click', function() {
        addSubCompetence("comp-" + $(subcompetenceButton).val())
    });

    $(removeCompetenceButton).addClass("btn pull-right");
    $(removeCompetenceButton).text("Remove this competence");
    $(removeCompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(removeCompetenceButton).on('click', function() {
        $(".panel-" + $(removeCompetenceButton).val()).remove();
    });

    $(competencePanelWrapper).addClass("col-md-9 compPanel panel-" + $(subcompetenceButton).val());
    $(competencePanel).addClass("panel panel-default");
    $(competencePanelHeading).addClass("panel-heading comp-" + $(subcompetenceButton).val());
    $(competencePanelBody).addClass("panel-body");
    $(competencePanelFooter).addClass("panel-footer");

    if(!$(".compPanel")[0]) {
        $("#top-col").after(competencePanelWrapper);
    } else {
        $(".compPanel:last").after(competencePanelWrapper);
    }
    ++viewModel.numberOfCompetencesToAdd;
    $(competencePanelWrapper).append(competencePanel);
    $(competencePanel).append(competencePanelHeading);
    $(competencePanelHeading).append(competenceName);
    $(competenceName).after(competenceCode);
    $(competencePanelHeading).after(competencePanelBody);
    $(competencePanelBody).after(competencePanelFooter);
    $(competencePanelFooter).append(subcompetenceButton);
    $(subcompetenceButton).after(removeCompetenceButton);
}

function addSubCompetence(competence) {
    alert(competence);
}

function addIndicator() {

}

function initPage() {
    viewModel.getProjectInfo();
    $("#addCompetenceBtn").click(function() {
        addCompetence();
    })
}<commit_msg>projectpage<commit_after>// View model for the courses page
function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.numberOfCompetencesToAdd = 0;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value'), function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };
}

function addCompetence() {
    var competencePanelWrapper = document.createElement('div');
    var competencePanel = document.createElement('div');
    var competencePanelHeading = document.createElement('div');
    var competencePanelBody = document.createElement('div');
    var competencePanelFooter = document.createElement('div');
    var competenceCode = document.createElement('input');
    var competenceName = document.createElement('input');
    var subcompetenceButton = document.createElement('button');
    var removeCompetenceButton = document.createElement('button');

    competenceCode.type = 'text';
    competenceCode.placeholder = "Competence-Code";
    $(competenceCode).addClass("form-control");

    competenceName.type = 'text';
    competenceName.placeholder = "Name of the competence";
    $(competenceName).addClass("form-control");

    $(subcompetenceButton).addClass("btn");
    $(subcompetenceButton).text("Add a subcompetence");
    $(subcompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(subcompetenceButton).on('click', function() {
        addSubCompetence("comp-" + $(subcompetenceButton).val())
    });

    $(removeCompetenceButton).addClass("btn pull-right");
    $(removeCompetenceButton).text("Remove this competence");
    $(removeCompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(removeCompetenceButton).on('click', function() {
        $(".panel-" + $(removeCompetenceButton).val()).remove();
    });

    $(competencePanelWrapper).addClass("col-md-9 compPanel panel-" + $(subcompetenceButton).val());
    $(competencePanel).addClass("panel panel-default");
    $(competencePanelHeading).addClass("panel-heading comp-" + $(subcompetenceButton).val());
    $(competencePanelBody).addClass("panel-body comp-"  + $(subcompetenceButton).val());
    $(competencePanelFooter).addClass("panel-footer");

    if(!$(".compPanel")[0]) {
        $("#top-col").after(competencePanelWrapper);
    } else {
        $(".compPanel:last").after(competencePanelWrapper);
    }
    ++viewModel.numberOfCompetencesToAdd;
    $(competencePanelWrapper).append(competencePanel);
    $(competencePanel).append(competencePanelHeading);
    $(competencePanelHeading).append(competenceName);
    $(competenceName).after(competenceCode);
    $(competencePanelHeading).after(competencePanelBody);
    $(competencePanelBody).after(competencePanelFooter);
    $(competencePanelFooter).append(subcompetenceButton);
    $(subcompetenceButton).after(removeCompetenceButton);
}

function addSubCompetence(competence) {
    $(".panel-body." + competence).append("hallo");
}

function addIndicator() {

}

function initPage() {
    viewModel.getProjectInfo();
    $("#addCompetenceBtn").click(function() {
        addCompetence();
    })
}<|endoftext|>


<commit_before>import path from 'path';
import postcss from 'postcss';
import slash from 'slash';

var query = [
  '(min--moz-device-pixel-ratio: 1.5)',
  '(-o-min-device-pixel-ratio: 3/2)',
  '(-webkit-min-device-pixel-ratio: 1.5)',
  '(min-device-pixel-ratio: 1.5)',
  '(min-resolution: 144dpi)',
  '(min-resolution: 1.5dppx)'
];

module.exports = postcss.plugin('postcss-at2x', at2x);

function at2x(opts) {
  opts = opts || {};
  var identifier = opts.identifier !== undefined ? opts.identifier : '@2x';

  return function(root) {
    root.eachRule(function(rule) {
      // Create an @media retina rule
      var mediaParent = rule.parent;
      var params = mediaParent.name === 'media' ?
                 combineMediaQuery(mediaParent.params.split(/,\s*/), query) :
                 query.join(', ');
      var media = postcss.atRule({ name: 'media', params: params });

      // Check for any `background-size` declarations. These need to be added
      // again to prevent it being overriden by usage of the `background`
      // shorthand
      var backgroundSize;
      rule.eachDecl('background-size', function(decl) {
        backgroundSize = decl;
      });

      rule.eachDecl(/^background/, function(decl) {
        if (!backgroundWithHiResURL(decl)) {
          return;
        }

        var newRule = postcss.rule({ selector: decl.parent.selector });
        newRule.append(postcss.decl({
          prop: decl.prop,
          value: parseUrl(decl, identifier)
        }));

        if (backgroundSize) {
          newRule.append(postcss.decl(backgroundSize));
        }

        media.append(newRule);
        root.append(media);
      });
    });
  };
}

/**
 * Add all the resolutions to each media query to scope them
 */
function combineMediaQuery(queries, resolutions) {
  return queries.reduce((finalQuery, query) => {
    resolutions.forEach(resolution => finalQuery.push(`${query} and ${resolution}`));
    return finalQuery;
  }, []).join(', ');
}

function parseUrl(decl, identifier) {
  // Strip out `at-2x`
  var val = decl.value.replace(/\s+(at-2x)\s*(;|$)/, '$2');
  decl.value = val;

  var i = val.indexOf('url(');
  var url = val.slice(i + 4, val.indexOf(')', i));
  var ext = path.extname(url);

  // ignore .svg
  if (ext === '.svg') {
    return;
  }

  // @2x url value
  var retinaUrl = path.join(path.dirname(url), path.basename(url, ext) + identifier + ext);

  // path.join uses backslash on windows, which breaks in CSS. Reverse that
  retinaUrl = slash(retinaUrl);

  // Replace all instances of `url(a/path/test.png)` with `url(a/path/test@2x.png)`.
  // This preserves other values set by background such as no-repeat, color etc
  return val.replace(/url\((.*?)\)/g, 'url(' + retinaUrl + ')');
}

function backgroundWithHiResURL(decl) {
  return decl.value.indexOf('url(') !== -1 &&
         decl.value.indexOf('at-2x') !== -1;
}
<commit_msg>Use destructuring for function arguments<commit_after>import path from 'path';
import postcss from 'postcss';
import slash from 'slash';

var query = [
  '(min--moz-device-pixel-ratio: 1.5)',
  '(-o-min-device-pixel-ratio: 3/2)',
  '(-webkit-min-device-pixel-ratio: 1.5)',
  '(min-device-pixel-ratio: 1.5)',
  '(min-resolution: 144dpi)',
  '(min-resolution: 1.5dppx)'
];

export default postcss.plugin('postcss-at2x', at2x);

function at2x({ identifier = '@2x' } = {}) {
  return function(root) {
    root.eachRule(function(rule) {
      // Create an @media retina rule
      var mediaParent = rule.parent;
      var params = mediaParent.name === 'media' ?
                 combineMediaQuery(mediaParent.params.split(/,\s*/), query) :
                 query.join(', ');
      var media = postcss.atRule({ name: 'media', params: params });

      // Check for any `background-size` declarations. These need to be added
      // again to prevent it being overriden by usage of the `background`
      // shorthand
      var backgroundSize;
      rule.eachDecl('background-size', function(decl) {
        backgroundSize = decl;
      });

      rule.eachDecl(/^background/, function(decl) {
        if (!backgroundWithHiResURL(decl)) {
          return;
        }

        var newRule = postcss.rule({ selector: decl.parent.selector });
        newRule.append(postcss.decl({
          prop: decl.prop,
          value: parseUrl(decl, identifier)
        }));

        if (backgroundSize) {
          newRule.append(postcss.decl(backgroundSize));
        }

        media.append(newRule);
        root.append(media);
      });
    });
  };
}

/**
 * Add all the resolutions to each media query to scope them
 */
function combineMediaQuery(queries, resolutions) {
  return queries.reduce((finalQuery, query) => {
    resolutions.forEach(resolution => finalQuery.push(`${query} and ${resolution}`));
    return finalQuery;
  }, []).join(', ');
}

function parseUrl(decl, identifier) {
  // Strip out `at-2x`
  var val = decl.value.replace(/\s+(at-2x)\s*(;|$)/, '$2');
  decl.value = val;

  var i = val.indexOf('url(');
  var url = val.slice(i + 4, val.indexOf(')', i));
  var ext = path.extname(url);

  // ignore .svg
  if (ext === '.svg') {
    return;
  }

  // @2x url value
  var retinaUrl = path.join(path.dirname(url), path.basename(url, ext) + identifier + ext);

  // path.join uses backslash on windows, which breaks in CSS. Reverse that
  retinaUrl = slash(retinaUrl);

  // Replace all instances of `url(a/path/test.png)` with `url(a/path/test@2x.png)`.
  // This preserves other values set by background such as no-repeat, color etc
  return val.replace(/url\((.*?)\)/g, 'url(' + retinaUrl + ')');
}

function backgroundWithHiResURL(decl) {
  return decl.value.indexOf('url(') !== -1 &&
         decl.value.indexOf('at-2x') !== -1;
}
<|endoftext|>


<commit_before>// # The game controller
// This gets a bit trippy because it can be used both by
// the browser or server

var controller = function(io){

  // utils
  var each = function(arr, fn){
    for (var i=0; i < arr.length; i++) {
      fn.call(arr[i], arr[i]);
    };
  };
  var rest = function(arr){
    return Array.prototype.slice.call(arr,1);
  };
  var equalCoordinates = function (coord1, coord2) {
    return coord1[0] === coord2[0] && coord1[1] === coord2[1];
  };
  var checkCoordinateInArray = function (coord, arr) {
    var isInArray = false;
    each(arr, function (item) {
      if (equalCoordinates(coord, item)) {
        isInArray = true;
      }
    });
    return isInArray;
  };

  var ArenaCounter = 0;
  var PlayerCounter = 0;

  // a list of players and arenas
  var arenas = [];
  var players = [];

  var gameover = function(){

  }

  /*
    Arena & Controller logic
  */

  var Game = function(){
    this.players = [];
    this.arenas = [];

    this.transports = [];

    this.start();

    // start the game loop
    var g = this;

    // FPS
    // 50 = 20FPS - acceptable
    // 30 = 30FPS - good
    // 20 = 50FPS - smooth
    var FPS = 30;

    (function l(){

      // A setTimeout period doesn't take in to account execution time
      // We need to know execution time too
      var tLoopExecStart = new Date().getTime();
      g.loop();
      var tLoopExec = new Date().getTime() - tLoopExecStart;

      // loop the time diff from target FPS to game execution time
      var loopOffset = FPS - tLoopExec;

      setTimeout(l,loopOffset);
    })();
  };

  // Add entities to the game
  Game.prototype.addPlayer = function(player){
    this.players.push(player);

    var playerCount = 0;
    //purge any disconnected players
    for (var i = this.players.length - 1; i >= 0; i--){
      if(!this.players[i].disconnected){
        playerCount++;
      }
    };

    // one connnected player
    if(playerCount == 1){
      this.start();
    }
    this.report();
  };
  Game.prototype.addArena = function(arena){
    this.arenas.push(arena);
    this.start();
    this.report();
  };

  //Hack because games get socks added later and multiple transports
  Game.prototype.addTransport = function(trans){
    this.transports.push(trans);

    var game = this;
    // All ON stuff should be added here, and the context won't be `game`
    // trans.on('button')…
    trans.on('restart', function(){
      game.start();
    })

    this.report();
  };

  Game.prototype.emit = function(){
    var _this = this;
    var args = arguments;
    for(var t in this.transports){
      var trans = this.transports[t];
            console.log(t,this.transports)
      trans.emit.apply(trans,args);
    }
  };

  // report the current game state
  Game.prototype.report = function(){
    this.emit('update:player_count',this.players.length);
    this.emit('update:arena_count',this.arenas.length);
  }

  /* start the game, position the players, etc */
  Game.prototype.start = function(){
    if(this.arenas.length == 0){
      console.log("Couldn't start game,  no arenas."); return;
    }

    // clear the arenas
    each(this.arenas, function(){
      this.usedpoints = [];
      this.emit('clear');
    })

    var startArena = this.arenas[0];

    //purge any disconnected players
    for (var i = this.players.length - 1; i >= 0; i--){
      if(this.players[i].disconnected){
        console.log("Removing player" + this.players[i].id)
        this.players.splice(i,1);
      }
    };

    var y = 0;
    var i = 0;

    var firstarena = this.arenas[0];

    each(this.players, function(player){
      player.dead = false;

      switch(i % 4)
      {
        case 0:
          player.x = 0;
          player.y = firstarena.height/2;
          player.nextDirection = 'right';
          break;
        case 1:
          player.x = firstarena.width;
          player.y = firstarena.height/2;
          player.nextDirection = 'left';
          break;
        case 2:
          player.x = firstarena.width/2;
          player.y = 0;
          player.nextDirection = 'down';
          break;
        case 3:
          player.x = firstarena.width/2;
          player.y = firstarena.height;
          player.nextDirection = 'up';
          break;
      }

      if(i>3){
      switch(i % 4)
      {
        case 0:
          player.y += 10;
          break;
        case 1:
          player.y += 10;
          break;
        case 2:
          player.x += 10;
          break;
        case 3:
          player.x += 10;
          break;
      }

      }

      i++;

      player.arena = startArena;// XXX temporary
      player.emit('removerestart');
    });
  }

  /* The game loop base acions */
  Game.prototype.loop = function(){
    // console.log("game loop")

    var arenas = this.arenas;

    // iterate players
    each(this.players, function(player){
      if(player.dead != true)
      {
        player.advance();
        //update the arena with the new player position
        if(player.arena){
          player.arena.draw(player);
        }
      }
    });

  };

  Game.prototype.restart = function(){

    var playersleft = 0;

    each(this.players, function(player){
      if(player.dead === false)
      {
        playersleft++;
      }
    });

    if(playersleft < 2)
    {
      console.log('game restart')

      each(this.arenas, function(arena){
        arena.emit('notice', 'restarting');
      });

      var g=this;
      setTimeout(function(){
        g.start()
      }, 1500);
    }

  }

  // When a link between arenas is requested - we set this variable
  var linkArena;
  var linkEdge;

  // the controller represention of an arena
  var Arena = function(transport,width,height){
    this.id = ArenaCounter++;
    this.transport = transport;

    this.width = width;
    this.height = height;

    this.links = {};

    this.on('requestLink', function(edge){
      console.log("requested link with", edge);

      if(!linkArena){
        linkArena = this;
        linkEdge = edge;
      } else {
        linkArena.links[linkEdge] = this;
        this.links[edge] = linkArena;

        linkArena.emit('linked', linkEdge);
        this.emit('linked', edge);

        // ready for another link
        linkArena = false;
      }

    });

    this.usedpoints = [];
  };

  // this checks to see if a player is still on this
  // arena and moves them to another one (or null)
  // as appropriate
  Arena.prototype.updatePlayer = function(player){
    if(player.x < 0){
      // left
      if(player.arena = this.links.left)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.x > this.width){
      // right
      if(player.arena = this.links.right)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.y < 0){
      //top
      if(player.arena = this.links.top)
        player.arena.entry(this, player, player.x/this.width);
    } else if(player.y > this.height){
      //bottom
      if(player.arena = this.links.bottom)
        player.arena.entry(this, player,  player.x/this.width);
    }
  };

  // this repositions the player as they have entered from another
  // arena
  Arena.prototype.entry = function(fromarena, player, position){
    for(link in this.links){
      if(this.links[link] === fromarena){
        // update the direction and position
        if(link == 'left'){
          player.x = 0;
          player.y = position * this.height;
          player.nextDirection = 'right';
        };
        if(link == 'right'){
          player.x = this.width;
          player.y = position * this.height;
          player.nextDirection = 'left';
        };
        if(link == 'top'){
          player.x = position * this.width;
          player.y = 0;
          player.nextDirection = 'down';
        };
        if(link == 'bottom'){
          player.x = position * this.width;
          player.y = this.height;
          player.nextDirection = 'up';
        };

        console.log("Switched player" + player.id + ' from arena' + fromarena.id + ' --> arena' + this.id);
      }
    }
  }

  // update the view of the arena
  Arena.prototype.draw = function(player){
    this.emit('updatePlayer', player.id, player.x, player.y)
  }

  Arena.prototype.addUsedPoint = function(points){
    // [x,y,playerid]
    // console.log(points)

    this.usedpoints.push(points);
  }

  Arena.prototype.checkCollision = function(head){
    // console.log('head:' + head)
    // console.log('used: '+ this.usedpoints);
    // [[x,y,playerid],[x,y,playerid]]
    var playerCollision = checkCoordinateInArray(head, this.usedpoints);
    if(playerCollision){
      return true;
    }
  }

  // the controller representation of a player
  var Player = function(transport){
    var id = this.id = PlayerCounter++;

    var dead = this.dead = false;

    this.transport = transport;

    this.on('left', function(){
      console.log("<<<Player"+this.id+" LEFT");
      // increment the direction
      this.nextDirection = {
        'right':'up',
        'up':'left',
        'left':'down',
        'down':'right'
      }[this.nextDirection];
    });

    this.on('right', function(){
      console.log(">>>Player"+id+" RIGHT");
      // increment the direction
      this.nextDirection = {
        'right':'down',
        'down':'left',
        'left':'up',
        'up':'right'
      }[this.nextDirection];
    });

    this.on('restart', function(){
      console.log('>.< Player '+this.id+' says RESTART!!');

      game.restart(); // best way?
    });

  };

  Player.prototype.advance = function(){

    // increment x y appropriately
    this.x += {left:-1, right:1}[this.nextDirection] || 0;
    this.y += {down:1,  up:-1   }[this.nextDirection] || 0;

    // update/transistion the arena based on the position
    if(this.arena){
      this.arena.updatePlayer(this);

      if(!this.arena){
        // we arent on an arena anymore
        console.log("KABOOM player "+this.id);
        this.dead = true;
        this.restart();
      } else {
        this.checkCollision();
        // store the point for collisions
        var nextPosition = [this.x,this.y,this.id];
        this.arena.addUsedPoint(nextPosition);
      }

    }

  }

  Player.prototype.restart = function(){
    //console.log("KABOOM");
    this.emit('gameover', true);
  }

  Player.prototype.checkCollision = function(){

    // actually, we just ask the arena for now
    if(this.arena.checkCollision([this.x,this.y,this.id]) === true)
    {
      console.log('BOOM!');
      this.dead = true;
      this.restart();
    }
  }

  /*
   Link up the models to the transport

   This means that the models don't have to have
   transport code in them,  calling this.on(…) will
   defer to the transport.  Also - the functions
   will be called in the context of the object
   which is handy.
 */
  var transportPrototypes = function(){
    this.prototype.on = function(event,fn){
      var _this = this;
      this.transport.on(event, function(){
        fn.apply(_this,arguments);
      });
    };
    this.prototype.emit = function(){
      this.transport.emit.apply(this.transport,arguments);
    };
  }
  transportPrototypes.call(Player);
  transportPrototypes.call(Arena);

  var game = new Game();

  // Handle connections other windows
  io.sockets.on('connection', function(socket){
    /*
      socket is a local variable, so if they identify themselves
      as a player or an arena - then we can set up handlers for
      to send them the correct information
    */
    var identifed = false;

    console.log("New Socket connection to server!");

    socket.on('arena', function(width,height,oldid){
      if (identifed) {return} identifed = true;

      // this socket comes from an arena
      console.log("This socket comes from AN ARENA!!!");
      var arena;
      if(oldid){
        each(game.arenas, function(a){
          if(a.id == oldid){
            arena = a;
          }
        })
      }
      arena = arena || new Arena(socket,width,height);

      socket.emit('hello', "Hi there Arena " + arena.id);

      socket.emit('id', arena.id);

      game.addArena(arena);

      socket.on('disconnect', function () {
        console.log("Disconnected arena");
        for (var i = game.arenas.length - 1; i >= 0; i--){
          if(game.arenas[i] == arena){
            console.log("found arena to remove", i)
            game.arenas.splice(i,1);
          }
        };

        // dont think this works…
        // each(game.players, function(p){
        //   //kill players on window loss
        //   if(p.arena == arena){
        //     p.dead = true;
        //   }
        // })
      });

    });

    socket.on('player', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a player
      console.log("This socket comes from a PLAYER!!!");

      var player = new Player(socket);

      player.emit('id', player.id);
      socket.emit('hello', "Hi there player " + player.id);

      game.addPlayer(player);

      socket.on('disconnect', function () {
        player.disconnected = true;
      });

      // restart the game
      // game.start();

    });

    socket.on('game', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a game
      console.log("This socket comes from a GAME!!!");

      game.addTransport(socket);

    });

  });

};

// expose for node
if(typeof exports != 'undefined'){
  exports.start = controller;
} else {
  window.controller = controller;
}

<commit_msg>check for less than 0 loop<commit_after>// # The game controller
// This gets a bit trippy because it can be used both by
// the browser or server

var controller = function(io){

  // utils
  var each = function(arr, fn){
    for (var i=0; i < arr.length; i++) {
      fn.call(arr[i], arr[i]);
    };
  };
  var rest = function(arr){
    return Array.prototype.slice.call(arr,1);
  };
  var equalCoordinates = function (coord1, coord2) {
    return coord1[0] === coord2[0] && coord1[1] === coord2[1];
  };
  var checkCoordinateInArray = function (coord, arr) {
    var isInArray = false;
    each(arr, function (item) {
      if (equalCoordinates(coord, item)) {
        isInArray = true;
      }
    });
    return isInArray;
  };

  var ArenaCounter = 0;
  var PlayerCounter = 0;

  // a list of players and arenas
  var arenas = [];
  var players = [];

  var gameover = function(){

  }

  /*
    Arena & Controller logic
  */

  var Game = function(){
    this.players = [];
    this.arenas = [];

    this.transports = [];

    this.start();

    // start the game loop
    var g = this;

    // FPS
    // 50 = 20FPS - acceptable
    // 30 = 30FPS - good
    // 20 = 50FPS - smooth
    var FPS = 30;

    (function l(){

      // A setTimeout period doesn't take in to account execution time
      // We need to know execution time too
      var tLoopExecStart = new Date().getTime();
      g.loop();
      var tLoopExec = new Date().getTime() - tLoopExecStart;

      // loop the time diff from target FPS to game execution time
      var loopOffset = FPS - tLoopExec;

      if (loopOffset < 0) loopOffset = 1;

      setTimeout(l,loopOffset);
    })();
  };

  // Add entities to the game
  Game.prototype.addPlayer = function(player){
    this.players.push(player);

    var playerCount = 0;
    //purge any disconnected players
    for (var i = this.players.length - 1; i >= 0; i--){
      if(!this.players[i].disconnected){
        playerCount++;
      }
    };

    // one connnected player
    if(playerCount == 1){
      this.start();
    }
    this.report();
  };
  Game.prototype.addArena = function(arena){
    this.arenas.push(arena);
    this.start();
    this.report();
  };

  //Hack because games get socks added later and multiple transports
  Game.prototype.addTransport = function(trans){
    this.transports.push(trans);

    var game = this;
    // All ON stuff should be added here, and the context won't be `game`
    // trans.on('button')…
    trans.on('restart', function(){
      game.start();
    })

    this.report();
  };

  Game.prototype.emit = function(){
    var _this = this;
    var args = arguments;
    for(var t in this.transports){
      var trans = this.transports[t];
            console.log(t,this.transports)
      trans.emit.apply(trans,args);
    }
  };

  // report the current game state
  Game.prototype.report = function(){
    this.emit('update:player_count',this.players.length);
    this.emit('update:arena_count',this.arenas.length);
  }

  /* start the game, position the players, etc */
  Game.prototype.start = function(){
    if(this.arenas.length == 0){
      console.log("Couldn't start game,  no arenas."); return;
    }

    // clear the arenas
    each(this.arenas, function(){
      this.usedpoints = [];
      this.emit('clear');
    })

    var startArena = this.arenas[0];

    //purge any disconnected players
    for (var i = this.players.length - 1; i >= 0; i--){
      if(this.players[i].disconnected){
        console.log("Removing player" + this.players[i].id)
        this.players.splice(i,1);
      }
    };

    var y = 0;
    var i = 0;

    var firstarena = this.arenas[0];

    each(this.players, function(player){
      player.dead = false;

      switch(i % 4)
      {
        case 0:
          player.x = 0;
          player.y = firstarena.height/2;
          player.nextDirection = 'right';
          break;
        case 1:
          player.x = firstarena.width;
          player.y = firstarena.height/2;
          player.nextDirection = 'left';
          break;
        case 2:
          player.x = firstarena.width/2;
          player.y = 0;
          player.nextDirection = 'down';
          break;
        case 3:
          player.x = firstarena.width/2;
          player.y = firstarena.height;
          player.nextDirection = 'up';
          break;
      }

      if(i>3){
      switch(i % 4)
      {
        case 0:
          player.y += 10;
          break;
        case 1:
          player.y += 10;
          break;
        case 2:
          player.x += 10;
          break;
        case 3:
          player.x += 10;
          break;
      }

      }

      i++;

      player.arena = startArena;// XXX temporary
      player.emit('removerestart');
    });
  }

  /* The game loop base acions */
  Game.prototype.loop = function(){
    // console.log("game loop")

    var arenas = this.arenas;

    // iterate players
    each(this.players, function(player){
      if(player.dead != true)
      {
        player.advance();
        //update the arena with the new player position
        if(player.arena){
          player.arena.draw(player);
        }
      }
    });

  };

  Game.prototype.restart = function(){

    var playersleft = 0;

    each(this.players, function(player){
      if(player.dead === false)
      {
        playersleft++;
      }
    });

    if(playersleft < 2)
    {
      console.log('game restart')

      each(this.arenas, function(arena){
        arena.emit('notice', 'restarting');
      });

      var g=this;
      setTimeout(function(){
        g.start()
      }, 1500);
    }

  }

  // When a link between arenas is requested - we set this variable
  var linkArena;
  var linkEdge;

  // the controller represention of an arena
  var Arena = function(transport,width,height){
    this.id = ArenaCounter++;
    this.transport = transport;

    this.width = width;
    this.height = height;

    this.links = {};

    this.on('requestLink', function(edge){
      console.log("requested link with", edge);

      if(!linkArena){
        linkArena = this;
        linkEdge = edge;
      } else {
        linkArena.links[linkEdge] = this;
        this.links[edge] = linkArena;

        linkArena.emit('linked', linkEdge);
        this.emit('linked', edge);

        // ready for another link
        linkArena = false;
      }

    });

    this.usedpoints = [];
  };

  // this checks to see if a player is still on this
  // arena and moves them to another one (or null)
  // as appropriate
  Arena.prototype.updatePlayer = function(player){
    if(player.x < 0){
      // left
      if(player.arena = this.links.left)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.x > this.width){
      // right
      if(player.arena = this.links.right)
        player.arena.entry(this, player, player.y/this.height);

    } else if(player.y < 0){
      //top
      if(player.arena = this.links.top)
        player.arena.entry(this, player, player.x/this.width);
    } else if(player.y > this.height){
      //bottom
      if(player.arena = this.links.bottom)
        player.arena.entry(this, player,  player.x/this.width);
    }
  };

  // this repositions the player as they have entered from another
  // arena
  Arena.prototype.entry = function(fromarena, player, position){
    for(link in this.links){
      if(this.links[link] === fromarena){
        // update the direction and position
        if(link == 'left'){
          player.x = 0;
          player.y = position * this.height;
          player.nextDirection = 'right';
        };
        if(link == 'right'){
          player.x = this.width;
          player.y = position * this.height;
          player.nextDirection = 'left';
        };
        if(link == 'top'){
          player.x = position * this.width;
          player.y = 0;
          player.nextDirection = 'down';
        };
        if(link == 'bottom'){
          player.x = position * this.width;
          player.y = this.height;
          player.nextDirection = 'up';
        };

        console.log("Switched player" + player.id + ' from arena' + fromarena.id + ' --> arena' + this.id);
      }
    }
  }

  // update the view of the arena
  Arena.prototype.draw = function(player){
    this.emit('updatePlayer', player.id, player.x, player.y)
  }

  Arena.prototype.addUsedPoint = function(points){
    // [x,y,playerid]
    // console.log(points)

    this.usedpoints.push(points);
  }

  Arena.prototype.checkCollision = function(head){
    // console.log('head:' + head)
    // console.log('used: '+ this.usedpoints);
    // [[x,y,playerid],[x,y,playerid]]
    var playerCollision = checkCoordinateInArray(head, this.usedpoints);
    if(playerCollision){
      return true;
    }
  }

  // the controller representation of a player
  var Player = function(transport){
    var id = this.id = PlayerCounter++;

    var dead = this.dead = false;

    this.transport = transport;

    this.on('left', function(){
      console.log("<<<Player"+this.id+" LEFT");
      // increment the direction
      this.nextDirection = {
        'right':'up',
        'up':'left',
        'left':'down',
        'down':'right'
      }[this.nextDirection];
    });

    this.on('right', function(){
      console.log(">>>Player"+id+" RIGHT");
      // increment the direction
      this.nextDirection = {
        'right':'down',
        'down':'left',
        'left':'up',
        'up':'right'
      }[this.nextDirection];
    });

    this.on('restart', function(){
      console.log('>.< Player '+this.id+' says RESTART!!');

      game.restart(); // best way?
    });

  };

  Player.prototype.advance = function(){

    // increment x y appropriately
    this.x += {left:-1, right:1}[this.nextDirection] || 0;
    this.y += {down:1,  up:-1   }[this.nextDirection] || 0;

    // update/transistion the arena based on the position
    if(this.arena){
      this.arena.updatePlayer(this);

      if(!this.arena){
        // we arent on an arena anymore
        console.log("KABOOM player "+this.id);
        this.dead = true;
        this.restart();
      } else {
        this.checkCollision();
        // store the point for collisions
        var nextPosition = [this.x,this.y,this.id];
        this.arena.addUsedPoint(nextPosition);
      }

    }

  }

  Player.prototype.restart = function(){
    //console.log("KABOOM");
    this.emit('gameover', true);
  }

  Player.prototype.checkCollision = function(){

    // actually, we just ask the arena for now
    if(this.arena.checkCollision([this.x,this.y,this.id]) === true)
    {
      console.log('BOOM!');
      this.dead = true;
      this.restart();
    }
  }

  /*
   Link up the models to the transport

   This means that the models don't have to have
   transport code in them,  calling this.on(…) will
   defer to the transport.  Also - the functions
   will be called in the context of the object
   which is handy.
 */
  var transportPrototypes = function(){
    this.prototype.on = function(event,fn){
      var _this = this;
      this.transport.on(event, function(){
        fn.apply(_this,arguments);
      });
    };
    this.prototype.emit = function(){
      this.transport.emit.apply(this.transport,arguments);
    };
  }
  transportPrototypes.call(Player);
  transportPrototypes.call(Arena);

  var game = new Game();

  // Handle connections other windows
  io.sockets.on('connection', function(socket){
    /*
      socket is a local variable, so if they identify themselves
      as a player or an arena - then we can set up handlers for
      to send them the correct information
    */
    var identifed = false;

    console.log("New Socket connection to server!");

    socket.on('arena', function(width,height,oldid){
      if (identifed) {return} identifed = true;

      // this socket comes from an arena
      console.log("This socket comes from AN ARENA!!!");
      var arena;
      if(oldid){
        each(game.arenas, function(a){
          if(a.id == oldid){
            arena = a;
          }
        })
      }
      arena = arena || new Arena(socket,width,height);

      socket.emit('hello', "Hi there Arena " + arena.id);

      socket.emit('id', arena.id);

      game.addArena(arena);

      socket.on('disconnect', function () {
        console.log("Disconnected arena");
        for (var i = game.arenas.length - 1; i >= 0; i--){
          if(game.arenas[i] == arena){
            console.log("found arena to remove", i)
            game.arenas.splice(i,1);
          }
        };

        // dont think this works…
        // each(game.players, function(p){
        //   //kill players on window loss
        //   if(p.arena == arena){
        //     p.dead = true;
        //   }
        // })
      });

    });

    socket.on('player', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a player
      console.log("This socket comes from a PLAYER!!!");

      var player = new Player(socket);

      player.emit('id', player.id);
      socket.emit('hello', "Hi there player " + player.id);

      game.addPlayer(player);

      socket.on('disconnect', function () {
        player.disconnected = true;
      });

      // restart the game
      // game.start();

    });

    socket.on('game', function(){
      if (identifed) {return} identifed = true;

      // this socket comes from a game
      console.log("This socket comes from a GAME!!!");

      game.addTransport(socket);

    });

  });

};

// expose for node
if(typeof exports != 'undefined'){
  exports.start = controller;
} else {
  window.controller = controller;
}

<|endoftext|>


<commit_before>var PROMISE_TIMEOUT = 5000, // 5 seconds
    ZAF_EVENT       = /^zaf\./,
    version         = require('version'),
    Promise         = window.Promise || require('../vendor/native-promise-only'),
    pendingPromises = {},
    promiseCount    = 0,
    requestCount    = 0;

function rawPostMessage(client, msg, forceReady) {
  if (client.ready || forceReady) {
    client._source.postMessage(msg, client._origin);
  } else {
    client.on('app.registered', rawPostMessage.bind(null, client, msg));
  }
}

function wrappedPostMessage(name) {
  var id = promiseCount++,
      timeoutId,
      params = Array.prototype.slice.call(arguments, 1);

  var promise = new Promise(function(resolve, reject) {
    // timeout the promise to ensure it will be garbage collected if nobody responds
    // we don't need to reject, because if an error is thrown in a promise it automatically rejects.
    timeoutId = setTimeout(function() {
      reject(new Error('Invocation request timeout'));
    }, PROMISE_TIMEOUT);

    pendingPromises[id] = {resolve: resolve, reject: reject};

    var msg = JSON.stringify({ id: id, request: name, params: params, appGuid: this._appGuid });
    rawPostMessage(this, msg);
  }.bind(this));

  // ensure promise is cleaned up when resolved
  return promise.then(removePromise.bind(null, id, timeoutId), removePromise.bind(null, id, timeoutId));
}

function removePromise(id, timeoutId, args) {
  clearTimeout(timeoutId);
  delete pendingPromises[id];
  if (args instanceof Error) throw args;
  return args;
}

function isValidEvent(client, event) {
  return client._origin === event.origin && client._source === event.source;
}

function messageHandler(client, event) {
  if (!isValidEvent(client, event)) { return; }

  var data = event.data;

  if (!data) { return; }

  if (typeof data === 'string') {
    try {
      data = JSON.parse(event.data);
    } catch (e) {
      return e;
    }
  }

  var pendingPromise;
  if (data.id && (pendingPromise = pendingPromises[data.id])) {
    if (data.error) {
      var err = data.error;
      if (err.code) {
        err = new Error(data.error.msg);
        err.name = data.error.code;
        err.stack = data.error.stack;
      }
      pendingPromise.reject(err);
    } else {
      pendingPromise.resolve(data.result);
    }
  } else if (ZAF_EVENT.test(data.key)) {
    var key = data.key.replace(ZAF_EVENT, '');
    if (client) {
      client.trigger(key, data.message);
    }
  }
}

var Client = function(origin, appGuid) {
  this._origin = origin;
  this._source = window.parent;
  this._appGuid = appGuid;
  this._messageHandlers = {};
  this._metadata = null;
  this._context = null;
  this.ready = false;

  this.on('app.registered', function(data) {
    this.ready = true;
    this._metadata = data.metadata;
    this._context = data.context;
  }, this);

  this.on('context.updated', function(context) {
    this._context = context;
  }, this);

  this.postMessage('iframe.handshake', { version: version });

  window.addEventListener('message', messageHandler.bind(null, this));
};

Client.prototype = {

  /// ### Client Object
  ///
  /// #### client.postMessage(name, [data])
  ///
  /// Allows you to send message events to the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the message event. This determines the name of the iframe
  ///     event your app will receive. For example, if you set this to 'hello', your app will receive
  ///     the event 'iframe.hello'
  ///   * `data` (optional) a JSON object with any data that you want to pass along with the
  ///     event
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.postMessage('hello', { awesome: true });
  /// ```
  postMessage: function(name, data) {
    var msg = JSON.stringify({ key: name, message: data, appGuid: this._appGuid });
    rawPostMessage(this, msg, name === 'iframe.handshake');
  },

  /// #### client.on(name, handler, [context])
  ///
  /// Allows you to add handlers to a framework event. You can add as many handler as you wish.
  /// They will be executed in the order they were added.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the framework event you want to listen to. This can be
  ///     [framework](./events.html#framework-events), [request](./events.html#request-events), or
  ///     [custom](./events.html#custom-events) events. Your iframe can listen to any events your app
  ///     receives, apart from DOM events. You don't need to register these events in the app first
  ///   * `handler` a function to be called when this event fires. You can expect to receive the same
  ///     event object your app would receive, parsed as JSON
  ///   * `context` (optional) the value of `this` within your handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.on('app.registered', function(e) {
  ///   // go nuts
  /// });
  /// ```
  ///
  /// Note: As soon as communication with the Zendesk app is established, the SDK triggers an
  /// `app.registered` event. You can add as many handlers to `app.registered` as you like. They're
  /// called immediately after the `init` callback.
  on: function(name, handler, context) {
    if (typeof handler == 'function') {
      this._messageHandlers[name] = this._messageHandlers[name] || [];
      this._messageHandlers[name].push(context ? handler.bind(context) : handler);
    }
  },

  /// #### client.off(name, handler)
  ///
  /// Allows you to remove a handler for a framework event.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the function you attached earlier with `on`
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff then remove the handler
  ///   client.off('app.registered', appRegistered);
  /// });
  /// ```
  off: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    var index = this._messageHandlers[name].indexOf(handler);
    return this._messageHandlers[name].splice(index, 1)[0];
  },

  /// #### client.has(name, handler)
  ///
  /// Returns whether or not an event has the specified handler attached to it.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the handler you want to test
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff
  /// });
  ///
  /// client.has('app.registered', appRegistered); // true
  /// client.has('app.activated', appRegistered); // false
  /// ```
  has: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    return this._messageHandlers[name].indexOf(handler) !== -1;
  },

  /// #### client.trigger(name, [data])
  ///
  /// Triggers the specified event on the client.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event you want to trigger
  ///   * `data` (optional) data you want to pass to the handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('activation', function {
  ///   console.log('activating!')
  /// });
  ///
  /// client.trigger('activation') // activating!
  /// ```
  trigger: function(name, data) {
    if (!this._messageHandlers[name]) { return false; }
    this._messageHandlers[name].forEach(function(handler) {
      handler(data);
    });
  },

  /// #### client.request(options)
  ///
  /// Dispatch [requests](./requests) via the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `options` the url of the request or an options object containing a url key/value
  ///
  /// ##### Returns
  ///
  /// A [Promises/A+](https://promisesaplus.com) conformant `promise` object.
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.request('/api/v2/tickets.json').then(
  ///   function(tickets) {
  ///     console.log(tickets);
  ///   },
  ///   function(response) {
  ///     console.error(response.responseText);
  ///   }
  /// );
  /// ```
  request: function(options) {
    var requestKey = 'request:' + requestCount++;

    return new Promise(function(resolve, reject) {
      if (typeof options === 'string') {
        options = { url: options };
      }

      this.on(requestKey + '.done', function(evt) {
        resolve.apply(this, evt.responseArgs);
      });

      this.on(requestKey + '.fail', function(evt) {
        reject.apply(this, evt.responseArgs);
      });

      this.postMessage(requestKey, options);
    }.bind(this));
  },

  metadata: function() {
    return new Promise(function(resolve) {
      if (this._metadata) {
        resolve(this._metadata);
      } else {
        this.on('app.registered', function() {
          resolve(this._metadata);
        }.bind(this));
      }
    }.bind(this));
  },

  context: function() {
    return new Promise(function(resolve) {
      if (this._context) {
        resolve(this._context);
      } else {
        this.on('app.registered', function() {
          resolve(this._context);
        }.bind(this));
      }
    }.bind(this));
  },

  get: function(path) {
    var paths = Array.isArray(path) ? path : [path];
    return wrappedPostMessage.call(this, 'get', paths);
  },

  set: function(key, val) {
    var obj = key;
    if (typeof key === 'string') {
      obj = {};
      obj[key] = val;
    }
    return wrappedPostMessage.call(this, 'set', obj);
  },

  invoke: function() {
    return wrappedPostMessage.bind(this, 'invoke').apply(null, arguments);
  }
};

module.exports = Client;
<commit_msg>fixed comment<commit_after>var PROMISE_TIMEOUT = 5000, // 5 seconds
    ZAF_EVENT       = /^zaf\./,
    version         = require('version'),
    Promise         = window.Promise || require('../vendor/native-promise-only'),
    pendingPromises = {},
    promiseCount    = 0,
    requestCount    = 0;

function rawPostMessage(client, msg, forceReady) {
  if (client.ready || forceReady) {
    client._source.postMessage(msg, client._origin);
  } else {
    client.on('app.registered', rawPostMessage.bind(null, client, msg));
  }
}

function wrappedPostMessage(name) {
  var id = promiseCount++,
      timeoutId,
      params = Array.prototype.slice.call(arguments, 1);

  var promise = new Promise(function(resolve, reject) {
    // Time out the promise to ensure it will be garbage collected if nobody responds
    timeoutId = setTimeout(function() {
      reject(new Error('Invocation request timeout'));
    }, PROMISE_TIMEOUT);

    pendingPromises[id] = {resolve: resolve, reject: reject};

    var msg = JSON.stringify({ id: id, request: name, params: params, appGuid: this._appGuid });
    rawPostMessage(this, msg);
  }.bind(this));

  // ensure promise is cleaned up when resolved
  return promise.then(removePromise.bind(null, id, timeoutId), removePromise.bind(null, id, timeoutId));
}

function removePromise(id, timeoutId, args) {
  clearTimeout(timeoutId);
  delete pendingPromises[id];
  if (args instanceof Error) throw args;
  return args;
}

function isValidEvent(client, event) {
  return client._origin === event.origin && client._source === event.source;
}

function messageHandler(client, event) {
  if (!isValidEvent(client, event)) { return; }

  var data = event.data;

  if (!data) { return; }

  if (typeof data === 'string') {
    try {
      data = JSON.parse(event.data);
    } catch (e) {
      return e;
    }
  }

  var pendingPromise;
  if (data.id && (pendingPromise = pendingPromises[data.id])) {
    if (data.error) {
      var err = data.error;
      if (err.code) {
        err = new Error(data.error.msg);
        err.name = data.error.code;
        err.stack = data.error.stack;
      }
      pendingPromise.reject(err);
    } else {
      pendingPromise.resolve(data.result);
    }
  } else if (ZAF_EVENT.test(data.key)) {
    var key = data.key.replace(ZAF_EVENT, '');
    if (client) {
      client.trigger(key, data.message);
    }
  }
}

var Client = function(origin, appGuid) {
  this._origin = origin;
  this._source = window.parent;
  this._appGuid = appGuid;
  this._messageHandlers = {};
  this._metadata = null;
  this._context = null;
  this.ready = false;

  this.on('app.registered', function(data) {
    this.ready = true;
    this._metadata = data.metadata;
    this._context = data.context;
  }, this);

  this.on('context.updated', function(context) {
    this._context = context;
  }, this);

  this.postMessage('iframe.handshake', { version: version });

  window.addEventListener('message', messageHandler.bind(null, this));
};

Client.prototype = {

  /// ### Client Object
  ///
  /// #### client.postMessage(name, [data])
  ///
  /// Allows you to send message events to the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the message event. This determines the name of the iframe
  ///     event your app will receive. For example, if you set this to 'hello', your app will receive
  ///     the event 'iframe.hello'
  ///   * `data` (optional) a JSON object with any data that you want to pass along with the
  ///     event
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.postMessage('hello', { awesome: true });
  /// ```
  postMessage: function(name, data) {
    var msg = JSON.stringify({ key: name, message: data, appGuid: this._appGuid });
    rawPostMessage(this, msg, name === 'iframe.handshake');
  },

  /// #### client.on(name, handler, [context])
  ///
  /// Allows you to add handlers to a framework event. You can add as many handler as you wish.
  /// They will be executed in the order they were added.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the framework event you want to listen to. This can be
  ///     [framework](./events.html#framework-events), [request](./events.html#request-events), or
  ///     [custom](./events.html#custom-events) events. Your iframe can listen to any events your app
  ///     receives, apart from DOM events. You don't need to register these events in the app first
  ///   * `handler` a function to be called when this event fires. You can expect to receive the same
  ///     event object your app would receive, parsed as JSON
  ///   * `context` (optional) the value of `this` within your handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.on('app.registered', function(e) {
  ///   // go nuts
  /// });
  /// ```
  ///
  /// Note: As soon as communication with the Zendesk app is established, the SDK triggers an
  /// `app.registered` event. You can add as many handlers to `app.registered` as you like. They're
  /// called immediately after the `init` callback.
  on: function(name, handler, context) {
    if (typeof handler == 'function') {
      this._messageHandlers[name] = this._messageHandlers[name] || [];
      this._messageHandlers[name].push(context ? handler.bind(context) : handler);
    }
  },

  /// #### client.off(name, handler)
  ///
  /// Allows you to remove a handler for a framework event.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the function you attached earlier with `on`
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff then remove the handler
  ///   client.off('app.registered', appRegistered);
  /// });
  /// ```
  off: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    var index = this._messageHandlers[name].indexOf(handler);
    return this._messageHandlers[name].splice(index, 1)[0];
  },

  /// #### client.has(name, handler)
  ///
  /// Returns whether or not an event has the specified handler attached to it.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the handler you want to test
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff
  /// });
  ///
  /// client.has('app.registered', appRegistered); // true
  /// client.has('app.activated', appRegistered); // false
  /// ```
  has: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    return this._messageHandlers[name].indexOf(handler) !== -1;
  },

  /// #### client.trigger(name, [data])
  ///
  /// Triggers the specified event on the client.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event you want to trigger
  ///   * `data` (optional) data you want to pass to the handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('activation', function {
  ///   console.log('activating!')
  /// });
  ///
  /// client.trigger('activation') // activating!
  /// ```
  trigger: function(name, data) {
    if (!this._messageHandlers[name]) { return false; }
    this._messageHandlers[name].forEach(function(handler) {
      handler(data);
    });
  },

  /// #### client.request(options)
  ///
  /// Dispatch [requests](./requests) via the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `options` the url of the request or an options object containing a url key/value
  ///
  /// ##### Returns
  ///
  /// A [Promises/A+](https://promisesaplus.com) conformant `promise` object.
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.request('/api/v2/tickets.json').then(
  ///   function(tickets) {
  ///     console.log(tickets);
  ///   },
  ///   function(response) {
  ///     console.error(response.responseText);
  ///   }
  /// );
  /// ```
  request: function(options) {
    var requestKey = 'request:' + requestCount++;

    return new Promise(function(resolve, reject) {
      if (typeof options === 'string') {
        options = { url: options };
      }

      this.on(requestKey + '.done', function(evt) {
        resolve.apply(this, evt.responseArgs);
      });

      this.on(requestKey + '.fail', function(evt) {
        reject.apply(this, evt.responseArgs);
      });

      this.postMessage(requestKey, options);
    }.bind(this));
  },

  metadata: function() {
    return new Promise(function(resolve) {
      if (this._metadata) {
        resolve(this._metadata);
      } else {
        this.on('app.registered', function() {
          resolve(this._metadata);
        }.bind(this));
      }
    }.bind(this));
  },

  context: function() {
    return new Promise(function(resolve) {
      if (this._context) {
        resolve(this._context);
      } else {
        this.on('app.registered', function() {
          resolve(this._context);
        }.bind(this));
      }
    }.bind(this));
  },

  get: function(path) {
    var paths = Array.isArray(path) ? path : [path];
    return wrappedPostMessage.call(this, 'get', paths);
  },

  set: function(key, val) {
    var obj = key;
    if (typeof key === 'string') {
      obj = {};
      obj[key] = val;
    }
    return wrappedPostMessage.call(this, 'set', obj);
  },

  invoke: function() {
    return wrappedPostMessage.bind(this, 'invoke').apply(null, arguments);
  }
};

module.exports = Client;
<|endoftext|>


<commit_before>"use strict";

const constant = require("./constant.js"),
    exec     = require("./exec.js"),
    helper   = require("./helper.js");

const Bipbip = function(path, env, writter) {
    const write = writter.getWritter(),
        _local = exec.local.bind(undefined, write),
        _remote = exec.remote.bind(undefined, write);

    let kill = function(message) {
        write(message, "red");
        process.exit(1);
    };

    Promise.resolve().then(() => {
        // Prepare current configuration
        let fileConfig = require(path).config,
            config = helper.merge(constant.default, fileConfig.default, fileConfig[env]);

        if (config.servers === undefined) {
            kill("servers is required.");
        } else if (Array.isArray(config.servers) === false) {
            kill("servers must be an array");
        }

        for (let i = config.servers.length - 1; i >= 0; i--) {
            let server = config.servers[i];

            if (server.user === undefined || typeof server.user != "string") {
                kill("servers." + i.toString() + ".user must be a string");
            } else if (server.host === undefined || typeof server.host != "string") {
                kill("servers." + i.toString() + ".host must be a string");
            } else if (server.to === undefined || typeof server.to != "string") {
                kill("servers." + i.toString() + ".to must be a string");
            }
        }

        if (typeof config.repository === "object" && config.repository !== null && config.repository.url !== null && (config.repository.branch === null && config.repository.tag === null)) {
            kill("repository.branch or repository.tag is required.");
        }

        config.requirements.local = config.requirements.local.concat(["git", "rsync", "ssh"]);
        config.requirements.remote = config.requirements.remote.concat(["rsync"]);

        return {
            config: config,
            file: path
        };
    }).then((options) => {
        const promises = [];

        for (let i = 0; i < options.config.requirements.remote.length; i++) {
            for (let j = options.config.servers.length - 1; j >= 0; j--) {
                promises.push(_remote(
                    options.config.servers[j],
                    `which ${options.config.requirements.remote[i]} || exit 1`
                ));
            }
        }

        for (let i = 0; i < options.config.requirements.local.length; i++) {
            promises.push(_local(`which ${options.config.requirements.local[i]} || exit 1`));
        }

        return Promise.all(promises).then(() => options);
    }).then((options) => {
        return _local('date +"%Y%d%m%H%M%S"')
            .then((response) => {
                options.config.internal = {
                    release: response.split("\n")[0],
                    write: write,
                    writter: writter,
                };

                return options;
            });
    })
    .then(_run)
    .then(() => {
        writter.getEmitter().emit("end");
    })
    .catch((error) => {
        writter.getEmitter().emit("error", error);
        process.exit(1);
    });
};

function _run(options) {
    return Promise.resolve(options)
        .then(_one.bind(null, "init"))
        .then(_one.bind(null, "local"))
        .then((config) => {
            let configs = [];
            for (let i = 0; i < config.servers.length; i++) {
                let configCloned = JSON.parse(JSON.stringify(config));

                for (let key in config.internal) {
                    configCloned.internal[key] = config.internal[key];
                }
                configCloned.internal.server = config.servers[i];

                configs.push(configCloned);
            }

            return configs;
        })
        .then(_all.bind(null, "send"))
        .then(_all.bind(null, "shared"))
        .then(_all.bind(null, "remote"))
        .then(_all.bind(null, "deploy"))
        .then(_all.bind(null, "postDeploy"))
        .then(_all.bind(null, "clean"));
}

function _all(workflow, configs) {
    let _ = require(`${__dirname}/workflow/${workflow}.js`),
        promises = [];

    for (let i in configs) {
        promises.push(_(configs[i]));
    }

    return Promise.all(promises);
}

function _one(workflow, config) {
    return require(`${__dirname}/workflow/${workflow}.js`)(config);
}

module.exports = Bipbip;
<commit_msg>[WIP] remote tests are executed when local test succeed<commit_after>"use strict";

const constant = require("./constant.js"),
    exec     = require("./exec.js"),
    helper   = require("./helper.js");

const Bipbip = function(path, env, writter) {
    const write = writter.getWritter(),
        _local = exec.local.bind(undefined, write),
        _remote = exec.remote.bind(undefined, write);

    let kill = function(message) {
        write(message, "red");
        process.exit(1);
    };

    Promise.resolve().then(() => {
        // Prepare current configuration
        let fileConfig = require(path).config,
            config = helper.merge(constant.default, fileConfig.default, fileConfig[env]);

        if (config.servers === undefined) {
            kill("servers is required.");
        } else if (Array.isArray(config.servers) === false) {
            kill("servers must be an array");
        }

        for (let i = config.servers.length - 1; i >= 0; i--) {
            let server = config.servers[i];

            if (server.user === undefined || typeof server.user != "string") {
                kill("servers." + i.toString() + ".user must be a string");
            } else if (server.host === undefined || typeof server.host != "string") {
                kill("servers." + i.toString() + ".host must be a string");
            } else if (server.to === undefined || typeof server.to != "string") {
                kill("servers." + i.toString() + ".to must be a string");
            }
        }

        if (typeof config.repository === "object" && config.repository !== null && config.repository.url !== null && (config.repository.branch === null && config.repository.tag === null)) {
            kill("repository.branch or repository.tag is required.");
        }

        config.requirements.local = config.requirements.local.concat(["git", "rsync", "ssh"]);
        config.requirements.remote = config.requirements.remote.concat(["rsync"]);

        return {
            config: config,
            file: path
        };
    }).then((options) => {
        const promises = [];

        for (let i = 0; i < options.config.requirements.local.length; i++) {
            promises.push(_local(`which ${options.config.requirements.local[i]} || exit 1`));
        }

        return Promise.all(promises).then(() => options);
    }).then((options) => {
        const promises = [];

        for (let i = 0; i < options.config.requirements.remote.length; i++) {
            for (let j = options.config.servers.length - 1; j >= 0; j--) {
                promises.push(_remote(
                    options.config.servers[j],
                    `which ${options.config.requirements.remote[i]} || exit 1`
                ));
            }
        }

        return Promise.all(promises).then(() => options);
    }).then((options) => {
        return _local('date +"%Y%d%m%H%M%S"')
            .then((response) => {
                options.config.internal = {
                    release: response.split("\n")[0],
                    write: write,
                    writter: writter,
                };

                return options;
            });
    })
    .then(_run)
    .then(() => {
        writter.getEmitter().emit("end");
    })
    .catch((error) => {
        writter.getEmitter().emit("error", error);
        process.exit(1);
    });
};

function _run(options) {
    return Promise.resolve(options)
        .then(_one.bind(null, "init"))
        .then(_one.bind(null, "local"))
        .then((config) => {
            let configs = [];
            for (let i = 0; i < config.servers.length; i++) {
                let configCloned = JSON.parse(JSON.stringify(config));

                for (let key in config.internal) {
                    configCloned.internal[key] = config.internal[key];
                }
                configCloned.internal.server = config.servers[i];

                configs.push(configCloned);
            }

            return configs;
        })
        .then(_all.bind(null, "send"))
        .then(_all.bind(null, "shared"))
        .then(_all.bind(null, "remote"))
        .then(_all.bind(null, "deploy"))
        .then(_all.bind(null, "postDeploy"))
        .then(_all.bind(null, "clean"));
}

function _all(workflow, configs) {
    let _ = require(`${__dirname}/workflow/${workflow}.js`),
        promises = [];

    for (let i in configs) {
        promises.push(_(configs[i]));
    }

    return Promise.all(promises);
}

function _one(workflow, config) {
    return require(`${__dirname}/workflow/${workflow}.js`)(config);
}

module.exports = Bipbip;
<|endoftext|>


<commit_before><commit_msg>Derpo the clown<commit_after><|endoftext|>


<commit_before>$(function () {
  'use strict';

  QUnit.module('modal plugin')

  QUnit.test('should be defined on jquery object', function (assert) {
    assert.ok($(document.body).modal, 'modal method is defined')
  })

  QUnit.module('modal', {
    setup: function () {
      // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
      $.fn.bootstrapModal = $.fn.modal.noConflict()
    },
    teardown: function () {
      $.fn.modal = $.fn.bootstrapModal
      delete $.fn.bootstrapModal
    }
  })

  QUnit.test('should provide no conflict', function (assert) {
    assert.strictEqual($.fn.modal, undefined, 'modal was set back to undefined (orig value)')
  })

  QUnit.test('should return jquery collection containing the element', function (assert) {
    var $el = $('
')
    var $modal = $el.bootstrapModal()
    assert.ok($modal instanceof $, 'returns jquery collection')
    assert.strictEqual($modal[0], $el[0], 'collection contains element')
  })

  QUnit.test('should expose defaults var for settings', function (assert) {
    assert.ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
  })

  QUnit.test('should insert into dom when show method is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should set aria-hidden to false when show method is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.strictEqual($('#modal-test').attr('aria-hidden'), 'false', 'aria-hidden is set to string "false" when modal shown')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should fire show event', function (assert) {
    var done = assert.async()

    $('
')
      .on('show.bs.modal', function () {
        assert.ok(true, 'show event fired')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should not fire shown when show was prevented', function (assert) {
    var done = assert.async()

    $('
')
      .on('show.bs.modal', function (e) {
        e.preventDefault()
        assert.ok(true, 'show event fired')
        done()
      })
      .on('shown.bs.modal', function () {
        assert.ok(false, 'shown event fired')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should hide modal when hide is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should set aria-hidden to true when hide is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.strictEqual($('#modal-test').length, 1, 'modal has been inserted into the dom')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.strictEqual($('#modal-test').attr('aria-hidden'), 'true', 'aria-hidden is set to string "true" when modal shown')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should toggle when toggle is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).bootstrapModal('toggle')
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('toggle')
  })

  QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
    var done = assert.async()

    $('

')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).find('.close').click()
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('toggle')
  })

  QUnit.test('should allow modal close with "backdrop:false"', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should close modal when clicking outside of modal-content', function (assert) {
    var done = assert.async()

    $('

')
      .on('shown.bs.modal', function () {
        assert.notEqual($('#modal-test').length, 0, 'modal insterted into dom')
        $('.contents').click()
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $('#modal-test .modal-backdrop').click()
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
    var done = assert.async()

    var div = $('
')
    div
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').length, 'modal insterted into dom')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        div.trigger($.Event('keydown', { which: 27 }))

        setTimeout(function () {
          assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
          div.remove()
          done()
        }, 0)
      })
      .bootstrapModal('show')
  })

  QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
    var done = assert.async()

    var div = $('
')
    div
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').length, 'modal insterted into dom')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        div.trigger($.Event('keyup', { which: 27 }))

        setTimeout(function () {
          assert.ok($('#modal-test').is(':visible'), 'modal still visible')
          div.remove()
          done()
        }, 0)
      })
      .bootstrapModal('show')
  })

  QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
    var done = assert.async()

    var triggered

    $('

')
      .on('shown.bs.modal', function () {
        triggered = 0
        $('#modal-test .modal-backdrop').click()
      })
      .on('hide.bs.modal', function () {
        triggered += 1
        assert.strictEqual(triggered, 1, 'modal hide triggered once')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
    var done = assert.async()

    $('

')
      .one('shown.bs.modal', function () {
        $('#close').click()
      })
      .one('hidden.bs.modal', function () {
        // after one open-close cycle
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        $(this)
          .one('shown.bs.modal', function () {
            $('#close').click()
          })
          .one('hidden.bs.modal', function () {
            assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
            done()
          })
          .bootstrapModal('show')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
    var done = assert.async()

    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')

    $('

')
      .on('hidden.bs.modal', function () {
        setTimeout(function () {
          assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
          done()
        }, 0)
      })
      .on('shown.bs.modal', function () {
        $('#close').click()
      })
      .appendTo('#qunit-fixture')

    $toggleBtn.click()
  })

  QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
    var done = assert.async()
    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
    var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')

    $('

')
      .one('show.bs.modal', function (e) {
        e.preventDefault()
        $otherBtn.focus()
        setTimeout($.proxy(function () {
          $(this).bootstrapModal('show')
        }, this), 0)
      })
      .on('hidden.bs.modal', function () {
        setTimeout(function () {
          assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
          done()
        }, 0)
      })
      .on('shown.bs.modal', function () {
        $('#close').click()
      })
      .appendTo('#qunit-fixture')

    $toggleBtn.click()
  })
})
<commit_msg>fix 2 typos in modal unit test assertion messages<commit_after>$(function () {
  'use strict';

  QUnit.module('modal plugin')

  QUnit.test('should be defined on jquery object', function (assert) {
    assert.ok($(document.body).modal, 'modal method is defined')
  })

  QUnit.module('modal', {
    setup: function () {
      // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
      $.fn.bootstrapModal = $.fn.modal.noConflict()
    },
    teardown: function () {
      $.fn.modal = $.fn.bootstrapModal
      delete $.fn.bootstrapModal
    }
  })

  QUnit.test('should provide no conflict', function (assert) {
    assert.strictEqual($.fn.modal, undefined, 'modal was set back to undefined (orig value)')
  })

  QUnit.test('should return jquery collection containing the element', function (assert) {
    var $el = $('
')
    var $modal = $el.bootstrapModal()
    assert.ok($modal instanceof $, 'returns jquery collection')
    assert.strictEqual($modal[0], $el[0], 'collection contains element')
  })

  QUnit.test('should expose defaults var for settings', function (assert) {
    assert.ok($.fn.bootstrapModal.Constructor.DEFAULTS, 'default object exposed')
  })

  QUnit.test('should insert into dom when show method is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should set aria-hidden to false when show method is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.strictEqual($('#modal-test').attr('aria-hidden'), 'false', 'aria-hidden is set to string "false" when modal shown')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should fire show event', function (assert) {
    var done = assert.async()

    $('
')
      .on('show.bs.modal', function () {
        assert.ok(true, 'show event fired')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should not fire shown when show was prevented', function (assert) {
    var done = assert.async()

    $('
')
      .on('show.bs.modal', function (e) {
        e.preventDefault()
        assert.ok(true, 'show event fired')
        done()
      })
      .on('shown.bs.modal', function () {
        assert.ok(false, 'shown event fired')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should hide modal when hide is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should set aria-hidden to true when hide is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.strictEqual($('#modal-test').length, 1, 'modal has been inserted into the dom')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.strictEqual($('#modal-test').attr('aria-hidden'), 'true', 'aria-hidden is set to string "true" when modal shown')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should toggle when toggle is called', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).bootstrapModal('toggle')
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('toggle')
  })

  QUnit.test('should remove from dom when click [data-dismiss="modal"]', function (assert) {
    var done = assert.async()

    $('

')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $(this).find('.close').click()
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('toggle')
  })

  QUnit.test('should allow modal close with "backdrop:false"', function (assert) {
    var done = assert.async()

    $('
')
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $(this).bootstrapModal('hide')
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should close modal when clicking outside of modal-content', function (assert) {
    var done = assert.async()

    $('

')
      .on('shown.bs.modal', function () {
        assert.notEqual($('#modal-test').length, 0, 'modal inserted into dom')
        $('.contents').click()
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        $('#modal-test .modal-backdrop').click()
      })
      .on('hidden.bs.modal', function () {
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should close modal when escape key is pressed via keydown', function (assert) {
    var done = assert.async()

    var div = $('
')
    div
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').length, 'modal insterted into dom')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        div.trigger($.Event('keydown', { which: 27 }))

        setTimeout(function () {
          assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
          div.remove()
          done()
        }, 0)
      })
      .bootstrapModal('show')
  })

  QUnit.test('should not close modal when escape key is pressed via keyup', function (assert) {
    var done = assert.async()

    var div = $('
')
    div
      .on('shown.bs.modal', function () {
        assert.ok($('#modal-test').length, 'modal inserted into dom')
        assert.ok($('#modal-test').is(':visible'), 'modal visible')
        div.trigger($.Event('keyup', { which: 27 }))

        setTimeout(function () {
          assert.ok($('#modal-test').is(':visible'), 'modal still visible')
          div.remove()
          done()
        }, 0)
      })
      .bootstrapModal('show')
  })

  QUnit.test('should trigger hide event once when clicking outside of modal-content', function (assert) {
    var done = assert.async()

    var triggered

    $('

')
      .on('shown.bs.modal', function () {
        triggered = 0
        $('#modal-test .modal-backdrop').click()
      })
      .on('hide.bs.modal', function () {
        triggered += 1
        assert.strictEqual(triggered, 1, 'modal hide triggered once')
        done()
      })
      .bootstrapModal('show')
  })

  QUnit.test('should close reopened modal with [data-dismiss="modal"] click', function (assert) {
    var done = assert.async()

    $('

')
      .one('shown.bs.modal', function () {
        $('#close').click()
      })
      .one('hidden.bs.modal', function () {
        // after one open-close cycle
        assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
        $(this)
          .one('shown.bs.modal', function () {
            $('#close').click()
          })
          .one('hidden.bs.modal', function () {
            assert.ok(!$('#modal-test').is(':visible'), 'modal hidden')
            done()
          })
          .bootstrapModal('show')
      })
      .bootstrapModal('show')
  })

  QUnit.test('should restore focus to toggling element when modal is hidden after having been opened via data-api', function (assert) {
    var done = assert.async()

    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')

    $('

')
      .on('hidden.bs.modal', function () {
        setTimeout(function () {
          assert.ok($(document.activeElement).is($toggleBtn), 'toggling element is once again focused')
          done()
        }, 0)
      })
      .on('shown.bs.modal', function () {
        $('#close').click()
      })
      .appendTo('#qunit-fixture')

    $toggleBtn.click()
  })

  QUnit.test('should not restore focus to toggling element if the associated show event gets prevented', function (assert) {
    var done = assert.async()
    var $toggleBtn = $('<button data-toggle="modal" data-target="#modal-test"/>').appendTo('#qunit-fixture')
    var $otherBtn = $('<button id="other-btn"/>').appendTo('#qunit-fixture')

    $('

')
      .one('show.bs.modal', function (e) {
        e.preventDefault()
        $otherBtn.focus()
        setTimeout($.proxy(function () {
          $(this).bootstrapModal('show')
        }, this), 0)
      })
      .on('hidden.bs.modal', function () {
        setTimeout(function () {
          assert.ok($(document.activeElement).is($otherBtn), 'focus returned to toggling element')
          done()
        }, 0)
      })
      .on('shown.bs.modal', function () {
        $('#close').click()
      })
      .appendTo('#qunit-fixture')

    $toggleBtn.click()
  })
})
<|endoftext|>