<commit_before>'use strict';
var fs = require('fs'),
	readline = require('readline'),
	MongoClient = require('mongodb').MongoClient,
	assert = require('assert');

function getDictSize(dict){
	var dictSize = 0;
	var rl = readline.createInterface({
		input: fs.createReadStream(dict),
		output: process.stdout,
		terminal: false
	});
	rl.on('line',function(line){
		dictSize += 1;
	});
	return dictSize;
}

function getCurrCode(mongoURI){
	return new Promise(function (resolve, rejedct){
		MongoClient.connect(mongoURI , function(err, db){
			if(err){
				console.log("GET CURRCODE: OPENING", err);
				reject(err);
			} else {
					var collection = db.collection('codeCounter');
					collection.find().toArray(function(err, res) {
					if(err){
						console.log("GET CURRCODE: READING", err);
					} else{
						console.log(res[0]);
						resolve(res[0].codeCounter);
					}
					})
				}
			});
	});
}

function makeNewCode(currCode, dictSize){

}

/*needs the mongoURI and the local dictionary JSON file path
chose to take in mongoURI rather than get from process because this is module*/
module.exports.genCode = function(dict, mongoURI){
	var dictSize = getDictSize(dict);
	var currCode = getCurrCode(mongoURI);
	console.log(currCode);
	var newCode = makeNewCode(currCode, dictSize);
	getCurrCode(mongoURI).then(function (fulfilled){
		return fulfilled;
	});
}
<commit_msg>Fixed Danny's code<commit_after>'use strict';
var fs = require('fs'),
	readline = require('readline'),
	MongoClient = require('mongodb').MongoClient,
	assert = require('assert');

function getDictSize(dict){
	var dictSize = 0;
	var rl = readline.createInterface({
		input: fs.createReadStream(dict),
		output: process.stdout,
		terminal: false
	});
	rl.on('line',function(line){
		dictSize += 1;
	});
	return dictSize;
}

function getCurrCode(mongoURI){
	return new Promise(function (resolve, rejedct){
		MongoClient.connect(mongoURI , function(err, db){
			if(err){
				console.log("GET CURRCODE: OPENING", err);
				reject(err);
			} else {
					var collection = db.collection('codeCounter');
					collection.find().toArray(function(err, res) {
					if(err){
						console.log("GET CURRCODE: READING", err);
					} else{
						console.log(res[0]);
						resolve(res[0].codeCounter);
					}
					})
				}
			});
	});
}

function makeNewCode(currCode, dictSize){

}

/*needs the mongoURI and the local dictionary JSON file path
chose to take in mongoURI rather than get from process because this is module*/
module.exports.genCode = function(dict, mongoURI){
	var dictSize = getDictSize(dict);
	var currCode = getCurrCode(mongoURI);
	console.log("WORDLS" + currCode);
	var newCode = makeNewCode(currCode, dictSize);
	getCurrCode(mongoURI).then(function (fulfilled){
		return fulfilled;
	});
}
<|endoftext|>


<commit_before>var gamestate = {};
gamestate.planetData = [];
gamestate.planetData[0] = {name:"Earth"  ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[1] = {name:"Mars"   ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[2] = {name:"Jupiter",technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[3] = {name:"Saturn" ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[4] = {name:"Uranus" ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[5] = {name:"Neptune",technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.currentPlanet = 0;
gamestate.costCache = [];

function onStart(){
  //random gen gamestate.planetData
  //gen player stats
  onArrive();
}
function onLoadGame(){
  //Load gamestate.planetData
  //load player stats
  onArrive();
}
function onArrive(){
  timeStep();
  gamestate.costCache = generateCosts(gamestate.currentPlanet);
  loadPlanetInterface(gamestate.currentPlanet);
}

function onLeave(){
  clearPlanetInterface();
}

function generateWorlds(){

}

function timeStep(){

}

function generateCosts(planet){
  var out = {};
  for(var item in itemConfig){
    out[item] = itemConfig[item](gamestate.planetData[planet]); //returns [supply, demand]
    out[item] = out[item][1]/out[item][0];
  }
  return out;
}

var itemConfig =
{
  computers:function(traits){return [traits.production*traits.technology,traits.population/traits.minerals]},
  fuel:function(traits){return [traits.production*traits.fuel,traits.population/traits.technology]}
};

gamestate.playerData = {reputation:[0,0,0,0,0,0]};
gamestate.playerData.inventory = {"credits":1000};
gamestate.playerData.ship = {engine:{},cargobay:{}};

function jump(to){
  var d = getDistance(to,gamestate.currentPlanet);
  var cost = ((d/30) | 0);
  if(gamestate.playerData.inventory.fuel - cost >= 0){
    gamestate.playerData.inventory.fuel -= cost;
    fly(to,gamestate.currentPlanet);
    onLeave();
    gamestate.currentPlanet = to;
  }
}<commit_msg>Added transaction function<commit_after>var gamestate = {};
gamestate.planetData = [];
gamestate.planetData[0] = {name:"Earth"  ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[1] = {name:"Mars"   ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[2] = {name:"Jupiter",technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[3] = {name:"Saturn" ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[4] = {name:"Uranus" ,technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.planetData[5] = {name:"Neptune",technology:0,minerals:0,fuel:0,food:0,antimatter:0,production:0,population:0,society:0};
gamestate.currentPlanet = 0;
gamestate.costCache = [];

function onStart(){
  //random gen gamestate.planetData
  //gen player stats
  onArrive();
}
function onLoadGame(){
  //Load gamestate.planetData
  //load player stats
  onArrive();
}
function onArrive(){
  timeStep();
  gamestate.costCache = generateCosts(gamestate.currentPlanet);
  loadPlanetInterface(gamestate.currentPlanet);
}

function onLeave(){
  clearPlanetInterface();
}

function generateWorlds(){

}

function timeStep(){

}

function generateCosts(planet){
  var out = {};
  for(var item in itemConfig){
    out[item] = itemConfig[item](gamestate.planetData[planet]); //returns [supply, demand]
    out[item] = out[item][1]/out[item][0];
  }
  return out;
}

var itemConfig =
{
  computers:function(traits){return [traits.production*traits.technology,traits.population/traits.minerals]},
  fuel:function(traits){return [traits.production*traits.fuel,traits.population/traits.technology]}
};

gamestate.playerData = {reputation:[0,0,0,0,0,0]};
gamestate.playerData.inventory = {"credits":1000};
gamestate.playerData.ship = {engine:{},cargobay:{}};

function jump(to){
  var d = getDistance(to,gamestate.currentPlanet);
  var cost = ((d/30) | 0);
  if(gamestate.playerData.inventory.fuel - cost >= 0){
    gamestate.playerData.inventory.fuel -= cost;
    fly(to,gamestate.currentPlanet);
    onLeave();
    gamestate.currentPlanet = to;
  }
}

function transaction(itemName,itemQuantity) {
  // item quantity + inventory quantity >= 0 && player credits + (itemQuantity * item cost on planet) >= 0
  if (itemQuantity + gamestate.playerData.inventory[itemName] >= 0 && gamestate.playerData.inventory.credits + (itemQuantity * gamestate.costCache[itemName]) >= 0 ) {
    gamestate.playerData.inventory[itemName] += itemQuantity;
    gamestate.playerData.inventory.credits += (itemQuantity * gamestate.costCache[itemName]);
  };
}<|endoftext|>


<commit_before>$(document).ready(function() {
    //Initialize a loader
    if (loader == undefined) {
        var loader = '

';
    }
    $('body').prepend(loader);

    /**
     * The clicked link or button is tagged with data-trigger=true. When a form is submitted,
     * we find the tagged element in case it has some data to submit.
     */
    $(document).on('click', 'form[data-toggle="ajax"] *[type="submit"]', function (event) {
        $(this).parents('form[data-toggle="ajax"]').first().children('*[type="submit"]').each(function () {
            $(this).attr('data-trigger', false);
        });
        $(this).attr('data-trigger', true);
    });

    $(document).trigger('ajax_button_listener_initialized');
    $('*[data-toggle="ajax"]').each(function() {
        $(this).css('pointer-events', 'auto');
    });
});
$(document).ajaxComplete(function() {
    $('*[data-toggle="ajax"]').each(function() {
        $(this).css('pointer-events', 'auto');
    });
});
    $(document).on('submit', 'form[data-toggle="ajax"]', function(event) {
        if($(this).hasClass('confirm') || $(this).hasClass('confirm-waiting')){
            return false;
        }
        $('#canvasloader-container').fadeIn();
        event.preventDefault();
        $(this).trigger('ajax.form.initialize');
        //Guess what is the target to update
        if ($(this).attr('data-target')) {
            var update = $(this).attr('data-target');
        } else if ($(this).data('update')) {
            console.info('The use of data-update will be deprecated in next version, please use data-target instead.');
            var update = $(this).data('update');
        }
        var updateStrategy = $(this).data('updateStrategy') ? $(this).data('updateStrategy') : 'html';
        var form = $(this);
        var effect = guessEffect(this, "#" + update);
        ajaxFormSubmit(form, $(form).attr('action'), update, updateStrategy, effect);

        return false;
    }).on('click', 'a[data-toggle="ajax"]', function(event) {
        if($(this).hasClass('confirm') || $(this).hasClass('confirm-waiting')){
            return false;
        }
        $('#canvasloader-container').fadeIn();
        event.preventDefault();

        //is the link linked ot a form
        var formSelector = $(this).data('form');

        $(this).trigger('ajax.link.initialize');
        //Guess what is the target to update
        if ($(this).attr('data-target')) {
            var update = $(this).attr('data-target');
        } else if ($(this).data('update')) {
            console.info('The use of data-update will be deprecated in next version, please use data-target instead.');
            var update = $(this).data('update');
        }
        var updateStrategy = $(this).data('updateStrategy') ? $(this).data('updateStrategy') : 'html';
        var link = $(this).attr('href');
        var effect = guessEffect(this, "#" + update);

        //if there is a form we submit this one with the href of the link
        if (formSelector === undefined) {
            ajaxLink(link, update, updateStrategy);
        } else {
            var form = $(formSelector);
            ajaxFormSubmit(form, link, update, updateStrategy, effect);
        }

        return false;
    });

function ajaxFormSubmit(form, action, update, updateStrategy, effect) {
    $(form).trigger('ajax.ajaxFormSubmit.before');
    var button = $('[type="submit"][data-trigger=true]');
    //grab all form data
    var formData = $(form).serializeArray();
    formData.push({ name: button.attr('name'), value: button.attr('value') });
    formData = $.param(formData);
    var contentType = 'application/x-www-form-urlencoded; charset=UTF-8';
    if ($(form).attr('enctype') == 'multipart/form-data') {
        var formData = new FormData($(form)[0]);
        formData.append(button.name, button.value);
        var contentType = false;
    }

    $.ajax({
        url         : action,
        context     : document.body,
        data        : formData,
        type        : $(form).attr('method'),
        contentType : contentType,
        processData : false,
        async       : true,
        cache       : false,
        success     : function(jsonResponse) {
            ajaxify(jsonResponse, update, updateStrategy, effect);
        }
    });

    $(form).trigger('ajax.ajaxFormSubmit.after');
}

function ajaxLink(link,update, updateStrategy, effect) {
    $(link).trigger('ajax.ajaxLink.before');
    $.ajax({
        url     : link,
        context : document.body,
        type    : "GET",
        success : function(jsonResponse) {
            ajaxify(jsonResponse, update, updateStrategy, effect);
        },
        error: function(jsonResponse) {
            if (typeof toastr === 'undefined') {
                alert("Il semble s'êre produit une erreur");
            } else {
                toastr.options = {
                  "positionClass": "toast-bottom-left",
                }
                toastr.error("Il semble s'êre produit une erreur");
            }
            $('#canvasloader-container').fadeOut();
        }
    });

    $(link).trigger('ajax.ajaxLink.after');
}

function ajaxify(jsonResponse, update, updateStrategy, effect) {

    if (typeof jsonResponse === 'object') {
        handleJson(jsonResponse, update, updateStrategy);
    } else {
        if (effect != undefined && effect != null){
            $("#" + update).hide();
        }
        //By default, the updateStrategy is html (a simple replace) but you can set your own function
        //for example, append, after etc or even a custom one.
        eval('$("#" + update).' + updateStrategy + '(jsonResponse)');

        if (effect != undefined && effect != null){
            eval('$("#" + update).' + effect + '()');
        }
    }

    $('#canvasloader-container').fadeOut();
    $('*[data-toggle="ajax"]').each(function() {
        $(this).css('pointer-events', 'auto');
    });

}

function handleJson(json, update, updateStrategy, effect) {

    if (json.hasOwnProperty("update")) {
        update = json.update;
    }

    if (effect != undefined && effect != null){
        $("#" + update).hide();
    }

    // check if an ajax callback is given in the response, execute it
    if (json.hasOwnProperty("ajax-callback")) {
        $.post(json.callback,
        {
            params : json.data
        },
        function(data){
            eval('$("#" + update).' + updateStrategy + '(data)');
        });
    }
    // a callback is javascript code
    if (json.hasOwnProperty("callback")) {
        eval(json.callback);
    }
    // html is the html part to be inserted in the "update"
    if (json.hasOwnProperty("html")) {
        eval('$("#" + update).' + updateStrategy + '(json.html)');

        if (effect != undefined && effect != null){
            eval('$("#" + update).'+effect+'()');
        }
    }
    // redirect is an url
    if (json.hasOwnProperty("redirect")) {
        window.location = json.redirect;
    }
}

function guessEffect(link, targetId) {
    var effect = "show";
    if ($(targetId).html() != undefined) {
        if ($(targetId).html() == "") {
            effect = "slideDown";
            if ($(link).attr('data-new-effect') != undefined && $(link).attr('data-new-effect') != "") {
                effect = $(link).attr('data-new-effect');
            } else if ($(targetId).attr('data-new-effect') != undefined && $(targetId).attr('data-new-effect') != "") {
                effect = $(targetId).attr('data-new-effect');
            }
        } else {
            effect = "fadeIn";
            if ($(link).attr('data-update-effect') != undefined && $(link).attr('data-update-effect') != "") {
                effect = $(link).attr('data-update-effect');
            } else if ($(targetId).attr('data-update-effect') != undefined && $(targetId).attr('data-update-effect') != "") {
                effect = $(targetId).attr('data-update-effect');
            }
        }
    } else if ($(link).data('no-effect')) {
        effect = null;
    }

    return effect;
}
<commit_msg>Fix bad event triggers (on string)<commit_after>$(document).ready(function() {
    //Initialize a loader
    if (loader == undefined) {
        var loader = '

';
    }
    $('body').prepend(loader);

    /**
     * The clicked link or button is tagged with data-trigger=true. When a form is submitted,
     * we find the tagged element in case it has some data to submit.
     */
    $(document).on('click', 'form[data-toggle="ajax"] *[type="submit"]', function (event) {
        $(this).parents('form[data-toggle="ajax"]').first().children('*[type="submit"]').each(function () {
            $(this).attr('data-trigger', false);
        });
        $(this).attr('data-trigger', true);
    });

    $(document).trigger('ajax_button_listener_initialized');
    $('*[data-toggle="ajax"]').each(function() {
        $(this).css('pointer-events', 'auto');
    });
});
$(document).ajaxComplete(function() {
    $('*[data-toggle="ajax"]').each(function() {
        $(this).css('pointer-events', 'auto');
    });
});
    $(document).on('submit', 'form[data-toggle="ajax"]', function(event) {
        if($(this).hasClass('confirm') || $(this).hasClass('confirm-waiting')){
            return false;
        }
        $('#canvasloader-container').fadeIn();
        event.preventDefault();
        $(this).trigger('ajax.form.initialize');
        //Guess what is the target to update
        if ($(this).attr('data-target')) {
            var update = $(this).attr('data-target');
        } else if ($(this).data('update')) {
            console.info('The use of data-update will be deprecated in next version, please use data-target instead.');
            var update = $(this).data('update');
        }
        var updateStrategy = $(this).data('updateStrategy') ? $(this).data('updateStrategy') : 'html';
        var form = $(this);
        var effect = guessEffect(this, "#" + update);
        ajaxFormSubmit(form, $(form).attr('action'), update, updateStrategy, effect);

        return false;
    }).on('click', 'a[data-toggle="ajax"]', function(event) {
        if($(this).hasClass('confirm') || $(this).hasClass('confirm-waiting')){
            return false;
        }
        $('#canvasloader-container').fadeIn();
        event.preventDefault();

        //is the link linked ot a form
        var formSelector = $(this).data('form');

        $(this).trigger('ajax.link.initialize');
        //Guess what is the target to update
        if ($(this).attr('data-target')) {
            var update = $(this).attr('data-target');
        } else if ($(this).data('update')) {
            console.info('The use of data-update will be deprecated in next version, please use data-target instead.');
            var update = $(this).data('update');
        }
        var updateStrategy = $(this).data('updateStrategy') ? $(this).data('updateStrategy') : 'html';
        var link = $(this).attr('href');
        var effect = guessEffect(this, "#" + update);

        //if there is a form we submit this one with the href of the link
        if (formSelector === undefined) {
            ajaxLink(link, update, updateStrategy);
        } else {
            var form = $(formSelector);
            ajaxFormSubmit(form, link, update, updateStrategy, effect);
        }

        return false;
    });

function ajaxFormSubmit(form, action, update, updateStrategy, effect) {
    $(form).trigger('ajax.ajaxFormSubmit.before');
    var button = $('[type="submit"][data-trigger=true]');
    //grab all form data
    var formData = $(form).serializeArray();
    formData.push({ name: button.attr('name'), value: button.attr('value') });
    formData = $.param(formData);
    var contentType = 'application/x-www-form-urlencoded; charset=UTF-8';
    if ($(form).attr('enctype') == 'multipart/form-data') {
        var formData = new FormData($(form)[0]);
        formData.append(button.name, button.value);
        var contentType = false;
    }

    $.ajax({
        url         : action,
        context     : document.body,
        data        : formData,
        type        : $(form).attr('method'),
        contentType : contentType,
        processData : false,
        async       : true,
        cache       : false,
        success     : function(jsonResponse) {
            ajaxify(jsonResponse, update, updateStrategy, effect);
        }
    });

    $(form).trigger('ajax.ajaxFormSubmit.after');
}

function ajaxLink(link,update, updateStrategy, effect) {
    $.ajax({
        url     : link,
        context : document.body,
        type    : "GET",
        success : function(jsonResponse) {
            ajaxify(jsonResponse, update, updateStrategy, effect);
        },
        error: function(jsonResponse) {
            if (typeof toastr === 'undefined') {
                alert("Il semble s'êre produit une erreur");
            } else {
                toastr.options = {
                  "positionClass": "toast-bottom-left",
                }
                toastr.error("Il semble s'êre produit une erreur");
            }
            $('#canvasloader-container').fadeOut();
        }
    });
}

function ajaxify(jsonResponse, update, updateStrategy, effect) {

    if (typeof jsonResponse === 'object') {
        handleJson(jsonResponse, update, updateStrategy);
    } else {
        if (effect != undefined && effect != null){
            $("#" + update).hide();
        }
        //By default, the updateStrategy is html (a simple replace) but you can set your own function
        //for example, append, after etc or even a custom one.
        eval('$("#" + update).' + updateStrategy + '(jsonResponse)');

        if (effect != undefined && effect != null){
            eval('$("#" + update).' + effect + '()');
        }
    }

    $('#canvasloader-container').fadeOut();
    $('*[data-toggle="ajax"]').each(function() {
        $(this).css('pointer-events', 'auto');
    });

}

function handleJson(json, update, updateStrategy, effect) {

    if (json.hasOwnProperty("update")) {
        update = json.update;
    }

    if (effect != undefined && effect != null){
        $("#" + update).hide();
    }

    // check if an ajax callback is given in the response, execute it
    if (json.hasOwnProperty("ajax-callback")) {
        $.post(json.callback,
        {
            params : json.data
        },
        function(data){
            eval('$("#" + update).' + updateStrategy + '(data)');
        });
    }
    // a callback is javascript code
    if (json.hasOwnProperty("callback")) {
        eval(json.callback);
    }
    // html is the html part to be inserted in the "update"
    if (json.hasOwnProperty("html")) {
        eval('$("#" + update).' + updateStrategy + '(json.html)');

        if (effect != undefined && effect != null){
            eval('$("#" + update).'+effect+'()');
        }
    }
    // redirect is an url
    if (json.hasOwnProperty("redirect")) {
        window.location = json.redirect;
    }
}

function guessEffect(link, targetId) {
    var effect = "show";
    if ($(targetId).html() != undefined) {
        if ($(targetId).html() == "") {
            effect = "slideDown";
            if ($(link).attr('data-new-effect') != undefined && $(link).attr('data-new-effect') != "") {
                effect = $(link).attr('data-new-effect');
            } else if ($(targetId).attr('data-new-effect') != undefined && $(targetId).attr('data-new-effect') != "") {
                effect = $(targetId).attr('data-new-effect');
            }
        } else {
            effect = "fadeIn";
            if ($(link).attr('data-update-effect') != undefined && $(link).attr('data-update-effect') != "") {
                effect = $(link).attr('data-update-effect');
            } else if ($(targetId).attr('data-update-effect') != undefined && $(targetId).attr('data-update-effect') != "") {
                effect = $(targetId).attr('data-update-effect');
            }
        }
    } else if ($(link).data('no-effect')) {
        effect = null;
    }

    return effect;
}
<|endoftext|>


<commit_before>const fs = require('fs');
const jsdom = require('jsdom');

const htmlSource = fs.readFileSync('layout.html', 'utf8');

// ==== Helper functions ====

function getOptions(id) {
  // if (id >= 26) {
  //   return ['fairing', 'legs'];
  // }
  return [];
}

function getDestination(destination, next) {
  return `<svg
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    viewBox="${next ? '70 310 59 40" width="2.6em" height="1.2em"' : '30 300 139 67"'}
  >
    <use xlink:href="img/destinations.svg#${destination}"></use>
  </svg>`;
}

function getSvgRocketId(rocket, payloads) {
  const dragon = payloads.some(payload => payload.payload_type.toLowerCase().includes('dragon'));
  const res = `${rocket.rocket_id}_${rocket.rocket_type}${(dragon && '_dragon') || ''}`;
  return res;
}

function getRocket(ele) {
  const id = getSvgRocketId(ele.rocket, ele.rocket.second_stage.payloads);
  const options = getOptions(ele.flight_number);
  return `<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    viewBox="0 0 50 535"
  >
    <use xlink:href="img/rockets.svg#${id}"></use>
    ${options.map(
    option =>
      `<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="img/rockets.svg#${
        option
      }"></use>`,
  )}
  </svg>`;
}

function getRocketClass(rocket) {
  let r = rocket.split('-')[0];
  r = r.split('_');
  if (r.length > 1) {
    r = r[0] + r[1];
  } else {
    r = r[0];
  }
  return r.replace(/_|\./g, '');
}

function getLandingOutcome(launch, showMissionFail) {
  const res = { class: 'outcome' };

  if (!launch.launch_success && showMissionFail) {
    res.html = '';
    res.html += '// Failure \\\\';
    res.class += ' fail';
    res.style = 'font-weight: 800;';
    res.html += '';
  } else if (launch.landing_type != null) {
    res.class += ' landing';
    res.html = '';
    if (launch.landing_type === 'Ocean') {
      res.html += 'Water landing';
      res.class += ' neutral';
    } else if (launch.land_success) {
      res.html += 'Landed';
      res.class += ' success';
    } else {
      res.html += 'Landing fail';
      res.class += ' fail';
    }
    res.html += '';
  } else if (!showMissionFail) {
    res.html = '';
    res.html += 'No attempt';
    res.html += '';
    res.class += ' grey';
  }
  return res;
}

function getMissionOutcome(launch) {
  const res = {};
  if (launch.launch_success) {
    res.class = 'success';
    res.html = 'Success';
  } else {
    res.class = 'fail';
    res.html = 'Failure';
  }
  return res;
}

// ====== Main ======

function printDate(stdate) {
  const date = new Date(stdate);
  const day = `0${date.getDate()}`.slice(-2);
  const month = `0${date.getMonth() + 1}`.slice(-2);
  const year = date.getFullYear();
  return `${day}/${month}/${year}`;
}

function getYear(stdate) {
  const date = new Date(stdate);
  return date.getFullYear();
}

function getsrcset(patchFileName) {
  let res = '';
  [100, 200].forEach((val) => {
    res += `patches/${val}px/${patchFileName} ${val}w, `;
  });
  return res.slice(0, -2);
}

jsdom.env(htmlSource, ['https://code.jquery.com/jquery.js'], async (err, window) => {
  const $ = require('jquery')(window); // eslint-disable-line

  // Previous launches
  const years = {};

  const data = await $.getJSON('https://api.spacexdata.com/v2/launches');
  data.reverse();
  data.forEach((ele) => {
    years[ele.launch_year] = (years[ele.launch_year] || 0) + 1;
    /* let patchFileName = ele.rocket.second_stage.payloads[0].name
      .replace(/([^a-z0-9]+)/gi, '-')
      .replace(/-$/, '')
      .toLowerCase();
    patchFileName += '.png'; */
    const patchFileName = ele.links.mission_patch;
    // List
    $('#flights').append(
      // $('
', { class: `flight ${getRocketClass(ele.rocket, false)}` })
      $('
', { class: 'flight' })
        .append(
          $('', {
            class: `destination ${ele.rocket.second_stage.payloads[0].orbit}`,
            title: ele.rocket.second_stage.payloads[0].orbit,
          }).html(getDestination(ele.rocket.second_stage.payloads[0].orbit, false)),
        )
        .append(
          $('
', {
            class: 'rocket',
            title: getSvgRocketId(ele.rocket, ele.rocket.second_stage.payloads),
          }).html(getRocket(ele)),
        )
        .append(
          $('
', { class: 'caption' })
            .append(
              $('', { class: 'name', text: ele.rocket.second_stage.payloads[0].payload_id }),
            )
            .append($('
', getLandingOutcome(ele, true))),
        )
        .append(
          $('', {
            class: 'patch',
            src: patchFileName,
            width: '5em',
            height: '5em',
            alt: `${ele.rocket.second_stage.payloads[0].name} patch`,
          }),
        ),
    );
    // Table
    $('#flights-table')
      .first()
      .append(
        $('
')
          .append($('').html(printDate(ele.launch_date_utc)))
          .append($('').html(getSvgRocketId(ele.rocket, ele.rocket.second_stage.payloads)))
          .append(
            $('', { style: 'text-align: left' }).html(
              ele.rocket.second_stage.payloads[0].payload_id,
            ),
          )
          .append($('').html(ele.rocket.second_stage.payloads[0].orbit))
          .append($('', getMissionOutcome(ele)))
          .append($('', getLandingOutcome(ele, false))),
      );
  });
  $.each(years, (index, value) => {
    $('#years').prepend(
      $('', {
        style: `width: ${(value * 7).toFixed(1)}em`,
      }).html(index),
    );
  });
  $('#launches').html(`(${data.length})`);

  // Remove jsdom scripts (jquery)
  const scripts = window.document.getElementsByTagName('script');
  let i = scripts.length;
  while (i--) {
    if (scripts[i].className === 'jsdom') {
      scripts[i].parentNode.removeChild(scripts[i]);
    }
  }

  // Write file
  fs.writeFile(
    '_site/index.html',
    `<!DOCTYPE html>${window.document.documentElement.outerHTML}`,
    (error) => {
      if (error) {
        throw error;
      }
    },
  );
  console.log('Done');
});
<commit_msg>Image resize and cache<commit_after>const fs = require('fs');
const jsdom = require('jsdom');

const htmlSource = fs.readFileSync('layout.html', 'utf8');

// ==== Helper functions ====

function generateSrcset(org, sizes) {
  return sizes.map(size => `${org}&w=${size}&h=${size} ${size}w`).join(',');
}

function getOptions(id) {
  // if (id >= 26) {
  //   return ['fairing', 'legs'];
  // }
  return [];
}

function getDestination(destination, next) {
  return `<svg
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    viewBox="${next ? '70 310 59 40" width="2.6em" height="1.2em"' : '30 300 139 67"'}
  >
    <use xlink:href="img/destinations.svg#${destination}"></use>
  </svg>`;
}

function getSvgRocketId(rocket, payloads) {
  const dragon = payloads.some(payload => payload.payload_type.toLowerCase().includes('dragon'));
  const res = `${rocket.rocket_id}_${rocket.rocket_type}${(dragon && '_dragon') || ''}`;
  return res;
}

function getRocket(ele) {
  const id = getSvgRocketId(ele.rocket, ele.rocket.second_stage.payloads);
  const options = getOptions(ele.flight_number);
  return `<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    viewBox="0 0 50 535"
  >
    <use xlink:href="img/rockets.svg#${id}"></use>
    ${options.map(
    option =>
      `<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="img/rockets.svg#${
        option
      }"></use>`,
  )}
  </svg>`;
}

function getRocketClass(rocket) {
  let r = rocket.split('-')[0];
  r = r.split('_');
  if (r.length > 1) {
    r = r[0] + r[1];
  } else {
    r = r[0];
  }
  return r.replace(/_|\./g, '');
}

function getLandingOutcome(launch, showMissionFail) {
  const res = { class: 'outcome' };

  if (!launch.launch_success && showMissionFail) {
    res.html = '';
    res.html += '// Failure \\\\';
    res.class += ' fail';
    res.style = 'font-weight: 800;';
    res.html += '';
  } else if (launch.landing_type != null) {
    res.class += ' landing';
    res.html = '';
    if (launch.landing_type === 'Ocean') {
      res.html += 'Water landing';
      res.class += ' neutral';
    } else if (launch.land_success) {
      res.html += 'Landed';
      res.class += ' success';
    } else {
      res.html += 'Landing fail';
      res.class += ' fail';
    }
    res.html += '';
  } else if (!showMissionFail) {
    res.html = '';
    res.html += 'No attempt';
    res.html += '';
    res.class += ' grey';
  }
  return res;
}

function getMissionOutcome(launch) {
  const res = {};
  if (launch.launch_success) {
    res.class = 'success';
    res.html = 'Success';
  } else {
    res.class = 'fail';
    res.html = 'Failure';
  }
  return res;
}

// ====== Main ======

function printDate(stdate) {
  const date = new Date(stdate);
  const day = `0${date.getDate()}`.slice(-2);
  const month = `0${date.getMonth() + 1}`.slice(-2);
  const year = date.getFullYear();
  return `${day}/${month}/${year}`;
}

function getYear(stdate) {
  const date = new Date(stdate);
  return date.getFullYear();
}

jsdom.env(htmlSource, ['https://code.jquery.com/jquery.js'], async (err, window) => {
  const $ = require('jquery')(window); // eslint-disable-line

  // Previous launches
  const years = {};

  const data = await $.getJSON('https://api.spacexdata.com/v2/launches');
  data.reverse();
  data.forEach((ele) => {
    years[ele.launch_year] = (years[ele.launch_year] || 0) + 1;
    /* let patchFileName = ele.rocket.second_stage.payloads[0].name
      .replace(/([^a-z0-9]+)/gi, '-')
      .replace(/-$/, '')
      .toLowerCase();
    patchFileName += '.png'; */
    const patchFileName = `https://images.weserv.nl/?url=${encodeURIComponent(
      ele.links.mission_patch.replace(/(^\w+:|^)\/\//, ''),
    )}`;
    console.log(patchFileName);
    // List
    $('#flights').append(
      // $('
', { class: `flight ${getRocketClass(ele.rocket, false)}` })
      $('
', { class: 'flight' })
        .append(
          $('', {
            class: `destination ${ele.rocket.second_stage.payloads[0].orbit}`,
            title: ele.rocket.second_stage.payloads[0].orbit,
          }).html(getDestination(ele.rocket.second_stage.payloads[0].orbit, false)),
        )
        .append(
          $('
', {
            class: 'rocket',
            title: getSvgRocketId(ele.rocket, ele.rocket.second_stage.payloads),
          }).html(getRocket(ele)),
        )
        .append(
          $('
', { class: 'caption' })
            .append(
              $('', { class: 'name', text: ele.rocket.second_stage.payloads[0].payload_id }),
            )
            .append($('
', getLandingOutcome(ele, true))),
        )
        .append(
          $('', {
            class: 'patch',
            src: `${patchFileName}&w=200&h=200`,
            width: '5em',
            height: '5em',
            alt: `${ele.rocket.second_stage.payloads[0].name} patch`,
          }),
        ),
    );
    // Table
    $('#flights-table')
      .first()
      .append(
        $('
')
          .append($('').html(printDate(ele.launch_date_utc)))
          .append($('').html(getSvgRocketId(ele.rocket, ele.rocket.second_stage.payloads)))
          .append(
            $('', { style: 'text-align: left' }).html(
              ele.rocket.second_stage.payloads[0].payload_id,
            ),
          )
          .append($('').html(ele.rocket.second_stage.payloads[0].orbit))
          .append($('', getMissionOutcome(ele)))
          .append($('', getLandingOutcome(ele, false))),
      );
  });
  $.each(years, (index, value) => {
    $('#years').prepend(
      $('', {
        style: `width: ${(value * 7).toFixed(1)}em`,
      }).html(index),
    );
  });
  $('#launches').html(`(${data.length})`);

  // Remove jsdom scripts (jquery)
  const scripts = window.document.getElementsByTagName('script');
  let i = scripts.length;
  while (i--) {
    if (scripts[i].className === 'jsdom') {
      scripts[i].parentNode.removeChild(scripts[i]);
    }
  }

  // Write file
  fs.writeFile(
    '_site/index.html',
    `<!DOCTYPE html>${window.document.documentElement.outerHTML}`,
    (error) => {
      if (error) {
        throw error;
      }
    },
  );
  console.log('Done');
});
<|endoftext|>


<commit_before>/** @module deliteful/Select */
define([
	"dcl/dcl",
	"dojo/dom-class", // TODO: replace (when replacement confirmed)
	"dstore/Memory",
	"dstore/Observable",
	"delite/register",
	"delite/FormWidget",
	"delite/StoreMap",
	"delite/Selection",
	"delite/handlebars!./Select/Select.html",
	"delite/theme!./Select/themes/{{theme}}/Select.css"
], function (dcl, domClass, Memory, Observable, register,
	FormWidget, StoreMap, Selection, template) {

	/**
	 * A form-aware and store-aware widget leveraging the native HTML5 `<select>`
	 * element.
	 * It has the following characteristics:
	 * * The corresponding custom tag is `<d-select>`.
	 * * Store support (limitation: to avoid graphic glitches, the updates to the
	 * store should not be done while the native dropdown of the select is open).
	 * * The item rendering has the limitations of the `<option>` elements of the
	 * native `<select>`, in particular it is text-only.
	 *
	 * TODO: improve doc.
	 *
	 * Remarks:
	 * * The option items must be added, removed or updated exclusively using
	 * the store API. Direct operations using the DOM API are not supported.
	 * * The handling of the selected options of the underlying native `<select>`
	 * must be done using the API inherited by deliteful/Select from delite/Selection.
	 *
	 * @example
Using the default store

	 * JS:
	 * require(["delite/register", "deliteful/Select", "requirejs-domready/domReady!"],
	 *   function (register) {
	 *     register.parse();
	 *     select1.store.add({text: "Option 1", value: "1"});
	 *     ...
	 *   });
	 * HTML:
	 * <d-select id="select1"></d-select>
	 * @example
Using user's store

	 * JS:
	 * require(["delite/register", "dstore/Memory", "dstore/Observable",
	 *         "deliteful/Select", "requirejs-domready/domReady!"],
	 *   function (register, Memory, Observable) {
	 *     register.parse();
	 *     var store = new (Memory.createSubclass(Observable))({});
	 *     select1.store = store;
	 *     store.add({text: "Option 1", value: "1"});
	 *     ...
	 *   });
	 * HTML:
	 * <d-select selectionMode="multiple" id="select1"></d-select>
	 *
	 * @class module:deliteful/Select
	 * @augments {module:delite/FormWidget}
	 * @augments {module:delite/Store}
	 */
	return register("d-select", [HTMLElement, FormWidget, StoreMap, Selection],
		/** @lends module:deliteful/Select# */ {

		// Note: the properties `store` and `query` are inherited from delite/Store, and
		// the property `disabled` is inherited from delite/FormWidget.

		/**
		 * The number of rows that should be visible at one time when the widget
		 * is presented as a scrollable list box. Corresponds to the `size` attribute
		 * of the underlying native HTML `<select>`.
		 * @member {number}
		 * @default 0
		 */
		size: 0,

		/**
		 * The name of the property of store items which contains the text
		 * of Select's options.
		 * @member {string}
		 * @default "text"
		 */
		textAttr: "text",

		/**
		 * The name of the property of store items which contains the value
		 * of Select's options.
		 * @member {string}
		 * @default "value"
		 */
		valueAttr: "value",

		/**
		 * The name of the property of store items which contains the disabled
		 * value of Select's options. To disable a given option, the `disabled`
		 * property of the corresponding data item must be set to a truthy value.
		 * Otherwise, the option is enabled if data item property is absent, or
		 * its value is falsy or the string "false".
		 * @member {string}
		 * @default "disabled"
		 */
		disabledAttr: "disabled",

		baseClass: "d-select",

		/**
		 * The chosen selection mode.
		 *
		 * Valid values are:
		 *
		 * 1. "single": Only one option can be selected at a time.
		 * 2. "multiple": Several options can be selected (by taping or using the
		 * control key modifier).
		 *
		 * Changing this value impacts the currently selected items to adapt the
		 * selection to the new mode. However, regardless of the selection mode,
		 * it is always possible to set several selected items using the
		 * `selectedItem` or `selectedItems` properties.
		 * The mode will be enforced only when using `setSelected` and/or
		 * `selectFromEvent` APIs.
		 *
		 * @member {string} module:deliteful/Select#selectionMode
		 * @default "single"
		 */
		// The purpose of the above pseudo-property is to adjust the documentation
		// of selectionMode as provided by delite/Selection.

		template: template,

		startup: function () {
			if (!this.store) { // If not specified by the user
				this.store = new (Memory.createSubclass(Observable))({});
			}

			this.focusNode = this.valueNode; // for delite/FormWidget's sake

			// To provide graphic feedback for focus, react to focus/blur events
			// on the underlying native select. The CSS class is used instead
			// of the focus pseudo-class because the browsers give the focus
			// to the underlying select, not to the widget.
			this.on("focus", function (evt) {
				domClass.toggle(this, "d-select-focus", evt.type === "focus");
			}.bind(this), this.valueNode);
			this.on("blur", function (evt) {
				domClass.toggle(this, "d-select-focus", evt.type === "focus");
			}.bind(this), this.valueNode);

			// Keep delite/Selection's selectedItem/selectedItems in sync after
			// interactive selection of options.
			this.on("change", function (event) {
				var selectedItems = this.selectedItems,
					selectedOptions = this.valueNode.selectedOptions;
				// HTMLSelectElement.selectedOptions is not present in all browsers...
				// At least IE10/Win misses it. Hence:
				if (selectedOptions === undefined) {
					// Convert to array
					var options = Array.prototype.slice.call(this.valueNode.options);
					selectedOptions = options.filter(function (option) {
						return option.selected;
					});
				} else {
					// convert HTMLCollection into array (to be able to use array.indexOf)
					selectedOptions = Array.prototype.slice.call(selectedOptions);
				}
				var nSelectedItems = selectedItems ? selectedItems.length : 0,
					nSelectedOptions = selectedOptions ? selectedOptions.length : 0;
				var i;
				var selectedOption, selectedItem;
				// Identify the options which changed their selection state. Two steps:
				// Step 1. Search options previously selected (currently in widget.selectedItems)
				// which are no longer selected in the native select.
				for (i = 0; i < nSelectedItems; i++) {
					selectedItem = selectedItems[i];
					if (selectedOptions.indexOf(selectedItem.visualItem) === -1) {
						this.selectFromEvent(event, selectedItem, selectedItem.visualItem, true);
					}
				}
				// Step 2. Search options newly selected in the native select which are not
				// present in the current selection (widget.selectedItems).
				for (i = 0; i < nSelectedOptions; i++) {
					selectedOption = selectedOptions[i];
					if (selectedItems.indexOf(selectedOption.renderItem) === - 1) {
						this.selectFromEvent(event, selectedOption.renderItem, selectedOption, true);
					}
				}

				// Update widget's value after interactive selection
				this._set("value", this.valueNode.value);
			}.bind(this), this.valueNode);

			// Thanks to the custom getter defined in deliteful/Select for widget's
			// `value` property, there is no need to add code for keeping the
			// property in sync after a form reset.
		},

		hasSelectionModifier: function () {
			// Override of the method from delite/Selection because the
			// default implementation is inappropriate: the "change" event
			// has no key modifier.
			return this.selectionMode === "multiple";
		},

		refreshRendering: function (props) {
			if ("renderItems" in props) {
				// Populate the select with the items retrieved from the store.
				var renderItems = this.renderItems;
				var n = renderItems ? renderItems.length : 0;
				// TODO: CHECKME/IMPROVEME. Also called after adding, deleting or updating just one item.
				// Worth optimizing to avoid recreating from scratch?
				this.valueNode.innerHTML = ""; // Remove the existing options from the DOM
				if (n > 0) {
					var fragment = this.ownerDocument.createDocumentFragment();
					var renderItem, option;
					for (var i = 0; i < n; i++) {
						renderItem = renderItems[i];
						option = this.ownerDocument.createElement("option");
						option.renderItem = renderItem;
						renderItem.visualItem = option;

						// According to http://www.w3.org/TR/html5/forms.html#the-option-element, we
						// could use equivalently the label or the text IDL attribute of the option element.
						// However, using the label attr. breaks the rendering in FF29/Win7!
						// This is https://bugzilla.mozilla.org/show_bug.cgi?id=40545.
						// Hence don't do
						// option.label = renderItem.label;
						// Instead:
						if (renderItem.text !== undefined) { // optional
							option.text = renderItem.text;
						}
						if (renderItem.value !== undefined) { // optional
							option.setAttribute("value", renderItem.value);
						}
						if (this.isSelected(renderItem)) { // delite/Selection's API
							option.setAttribute("selected", "true");
						}
						if (renderItem.disabled !== undefined &&
							!!renderItem.disabled && renderItem.disabled !== "false") { // optional
							// Note that for an enabled option the attribute must NOT be set
							// (<option disabled="false"> is a disabled option!)
							option.setAttribute("disabled", "true");
						}

						fragment.appendChild(option);
					}
					this.valueNode.appendChild(fragment);

					if (this.selectionMode === "single") {
						// Since there is no native "change" event initially, initialize
						// the delite/Selection's selectedItem property with the currently
						// selected option of the native select.
						this.selectedItem =
							this.valueNode.options[this.valueNode.selectedIndex].renderItem;
					} // else for the native multi-select: it does not have any
					// option selected by default.

					// Initialize widget's value
					this._set("value", this.valueNode.value);
				}
			}
		},

		getIdentity: function (renderItem) {
			// Override of delite/Selection's method
			return renderItem.id;
		},

		updateRenderers: function () {
			// Override of delite/Selection's method
			// Trigger rerendering from scratch:
			this.notifyCurrentValue("renderItems");
		},

		_setValueAttr: function (value) {
			if (this.valueNode) {
				this.valueNode.value = value;
			}
			this._set("value", value);
		},

		_setSelectionModeAttr: dcl.superCall(function (sup) {
			// Override of the setter from delite/Selection to forbid the value "none"
			return function (value) {
				if (value !== "single" && value !== "multiple") {
					throw new TypeError("'" + value +
						"' not supported for selectionMode; keeping the previous value of '" +
						this.selectionMode + "'");
				} else {
					this._set("selectionMode", value);
				}
				sup.call(this, value);
			};
		})
	});
});
<commit_msg>jsdoc of Select: stick with standard syntax of @augments<commit_after>/** @module deliteful/Select */
define([
	"dcl/dcl",
	"dojo/dom-class", // TODO: replace (when replacement confirmed)
	"dstore/Memory",
	"dstore/Observable",
	"delite/register",
	"delite/FormWidget",
	"delite/StoreMap",
	"delite/Selection",
	"delite/handlebars!./Select/Select.html",
	"delite/theme!./Select/themes/{{theme}}/Select.css"
], function (dcl, domClass, Memory, Observable, register,
	FormWidget, StoreMap, Selection, template) {

	/**
	 * A form-aware and store-aware widget leveraging the native HTML5 `<select>`
	 * element.
	 * It has the following characteristics:
	 * * The corresponding custom tag is `<d-select>`.
	 * * Store support (limitation: to avoid graphic glitches, the updates to the
	 * store should not be done while the native dropdown of the select is open).
	 * * The item rendering has the limitations of the `<option>` elements of the
	 * native `<select>`, in particular it is text-only.
	 *
	 * TODO: improve doc.
	 *
	 * Remarks:
	 * * The option items must be added, removed or updated exclusively using
	 * the store API. Direct operations using the DOM API are not supported.
	 * * The handling of the selected options of the underlying native `<select>`
	 * must be done using the API inherited by deliteful/Select from delite/Selection.
	 *
	 * @example
Using the default store

	 * JS:
	 * require(["delite/register", "deliteful/Select", "requirejs-domready/domReady!"],
	 *   function (register) {
	 *     register.parse();
	 *     select1.store.add({text: "Option 1", value: "1"});
	 *     ...
	 *   });
	 * HTML:
	 * <d-select id="select1"></d-select>
	 * @example
Using user's store

	 * JS:
	 * require(["delite/register", "dstore/Memory", "dstore/Observable",
	 *         "deliteful/Select", "requirejs-domready/domReady!"],
	 *   function (register, Memory, Observable) {
	 *     register.parse();
	 *     var store = new (Memory.createSubclass(Observable))({});
	 *     select1.store = store;
	 *     store.add({text: "Option 1", value: "1"});
	 *     ...
	 *   });
	 * HTML:
	 * <d-select selectionMode="multiple" id="select1"></d-select>
	 *
	 * @class module:deliteful/Select
	 * @augments module:delite/FormWidget
	 * @augments module:delite/Store
	 */
	return register("d-select", [HTMLElement, FormWidget, StoreMap, Selection],
		/** @lends module:deliteful/Select# */ {

		// Note: the properties `store` and `query` are inherited from delite/Store, and
		// the property `disabled` is inherited from delite/FormWidget.

		/**
		 * The number of rows that should be visible at one time when the widget
		 * is presented as a scrollable list box. Corresponds to the `size` attribute
		 * of the underlying native HTML `<select>`.
		 * @member {number}
		 * @default 0
		 */
		size: 0,

		/**
		 * The name of the property of store items which contains the text
		 * of Select's options.
		 * @member {string}
		 * @default "text"
		 */
		textAttr: "text",

		/**
		 * The name of the property of store items which contains the value
		 * of Select's options.
		 * @member {string}
		 * @default "value"
		 */
		valueAttr: "value",

		/**
		 * The name of the property of store items which contains the disabled
		 * value of Select's options. To disable a given option, the `disabled`
		 * property of the corresponding data item must be set to a truthy value.
		 * Otherwise, the option is enabled if data item property is absent, or
		 * its value is falsy or the string "false".
		 * @member {string}
		 * @default "disabled"
		 */
		disabledAttr: "disabled",

		baseClass: "d-select",

		/**
		 * The chosen selection mode.
		 *
		 * Valid values are:
		 *
		 * 1. "single": Only one option can be selected at a time.
		 * 2. "multiple": Several options can be selected (by taping or using the
		 * control key modifier).
		 *
		 * Changing this value impacts the currently selected items to adapt the
		 * selection to the new mode. However, regardless of the selection mode,
		 * it is always possible to set several selected items using the
		 * `selectedItem` or `selectedItems` properties.
		 * The mode will be enforced only when using `setSelected` and/or
		 * `selectFromEvent` APIs.
		 *
		 * @member {string} module:deliteful/Select#selectionMode
		 * @default "single"
		 */
		// The purpose of the above pseudo-property is to adjust the documentation
		// of selectionMode as provided by delite/Selection.

		template: template,

		startup: function () {
			if (!this.store) { // If not specified by the user
				this.store = new (Memory.createSubclass(Observable))({});
			}

			this.focusNode = this.valueNode; // for delite/FormWidget's sake

			// To provide graphic feedback for focus, react to focus/blur events
			// on the underlying native select. The CSS class is used instead
			// of the focus pseudo-class because the browsers give the focus
			// to the underlying select, not to the widget.
			this.on("focus", function (evt) {
				domClass.toggle(this, "d-select-focus", evt.type === "focus");
			}.bind(this), this.valueNode);
			this.on("blur", function (evt) {
				domClass.toggle(this, "d-select-focus", evt.type === "focus");
			}.bind(this), this.valueNode);

			// Keep delite/Selection's selectedItem/selectedItems in sync after
			// interactive selection of options.
			this.on("change", function (event) {
				var selectedItems = this.selectedItems,
					selectedOptions = this.valueNode.selectedOptions;
				// HTMLSelectElement.selectedOptions is not present in all browsers...
				// At least IE10/Win misses it. Hence:
				if (selectedOptions === undefined) {
					// Convert to array
					var options = Array.prototype.slice.call(this.valueNode.options);
					selectedOptions = options.filter(function (option) {
						return option.selected;
					});
				} else {
					// convert HTMLCollection into array (to be able to use array.indexOf)
					selectedOptions = Array.prototype.slice.call(selectedOptions);
				}
				var nSelectedItems = selectedItems ? selectedItems.length : 0,
					nSelectedOptions = selectedOptions ? selectedOptions.length : 0;
				var i;
				var selectedOption, selectedItem;
				// Identify the options which changed their selection state. Two steps:
				// Step 1. Search options previously selected (currently in widget.selectedItems)
				// which are no longer selected in the native select.
				for (i = 0; i < nSelectedItems; i++) {
					selectedItem = selectedItems[i];
					if (selectedOptions.indexOf(selectedItem.visualItem) === -1) {
						this.selectFromEvent(event, selectedItem, selectedItem.visualItem, true);
					}
				}
				// Step 2. Search options newly selected in the native select which are not
				// present in the current selection (widget.selectedItems).
				for (i = 0; i < nSelectedOptions; i++) {
					selectedOption = selectedOptions[i];
					if (selectedItems.indexOf(selectedOption.renderItem) === - 1) {
						this.selectFromEvent(event, selectedOption.renderItem, selectedOption, true);
					}
				}

				// Update widget's value after interactive selection
				this._set("value", this.valueNode.value);
			}.bind(this), this.valueNode);

			// Thanks to the custom getter defined in deliteful/Select for widget's
			// `value` property, there is no need to add code for keeping the
			// property in sync after a form reset.
		},

		hasSelectionModifier: function () {
			// Override of the method from delite/Selection because the
			// default implementation is inappropriate: the "change" event
			// has no key modifier.
			return this.selectionMode === "multiple";
		},

		refreshRendering: function (props) {
			if ("renderItems" in props) {
				// Populate the select with the items retrieved from the store.
				var renderItems = this.renderItems;
				var n = renderItems ? renderItems.length : 0;
				// TODO: CHECKME/IMPROVEME. Also called after adding, deleting or updating just one item.
				// Worth optimizing to avoid recreating from scratch?
				this.valueNode.innerHTML = ""; // Remove the existing options from the DOM
				if (n > 0) {
					var fragment = this.ownerDocument.createDocumentFragment();
					var renderItem, option;
					for (var i = 0; i < n; i++) {
						renderItem = renderItems[i];
						option = this.ownerDocument.createElement("option");
						option.renderItem = renderItem;
						renderItem.visualItem = option;

						// According to http://www.w3.org/TR/html5/forms.html#the-option-element, we
						// could use equivalently the label or the text IDL attribute of the option element.
						// However, using the label attr. breaks the rendering in FF29/Win7!
						// This is https://bugzilla.mozilla.org/show_bug.cgi?id=40545.
						// Hence don't do
						// option.label = renderItem.label;
						// Instead:
						if (renderItem.text !== undefined) { // optional
							option.text = renderItem.text;
						}
						if (renderItem.value !== undefined) { // optional
							option.setAttribute("value", renderItem.value);
						}
						if (this.isSelected(renderItem)) { // delite/Selection's API
							option.setAttribute("selected", "true");
						}
						if (renderItem.disabled !== undefined &&
							!!renderItem.disabled && renderItem.disabled !== "false") { // optional
							// Note that for an enabled option the attribute must NOT be set
							// (<option disabled="false"> is a disabled option!)
							option.setAttribute("disabled", "true");
						}

						fragment.appendChild(option);
					}
					this.valueNode.appendChild(fragment);

					if (this.selectionMode === "single") {
						// Since there is no native "change" event initially, initialize
						// the delite/Selection's selectedItem property with the currently
						// selected option of the native select.
						this.selectedItem =
							this.valueNode.options[this.valueNode.selectedIndex].renderItem;
					} // else for the native multi-select: it does not have any
					// option selected by default.

					// Initialize widget's value
					this._set("value", this.valueNode.value);
				}
			}
		},

		getIdentity: function (renderItem) {
			// Override of delite/Selection's method
			return renderItem.id;
		},

		updateRenderers: function () {
			// Override of delite/Selection's method
			// Trigger rerendering from scratch:
			this.notifyCurrentValue("renderItems");
		},

		_setValueAttr: function (value) {
			if (this.valueNode) {
				this.valueNode.value = value;
			}
			this._set("value", value);
		},

		_setSelectionModeAttr: dcl.superCall(function (sup) {
			// Override of the setter from delite/Selection to forbid the value "none"
			return function (value) {
				if (value !== "single" && value !== "multiple") {
					throw new TypeError("'" + value +
						"' not supported for selectionMode; keeping the previous value of '" +
						this.selectionMode + "'");
				} else {
					this._set("selectionMode", value);
				}
				sup.call(this, value);
			};
		})
	});
});
<|endoftext|>


<commit_before>var TURTED = function (sockjs_url) {
    var me = this;
    this.callbacks = {};
    this.nativeConnection = {};
    this.callbackQueue = [];
    this.reconnectQueue = [];
    this.isConnected = false;

    this.connect = function (isReconnect) {
        var sockjs = new SockJS(sockjs_url);
        sockjs.onopen = function () {
            //fire queued events here
            me.isConnected = true;
            me.processQueue();
            if (isReconnect) {
                me.processReconnectQueue()
            }
        }
        sockjs.onmessage = function (e) {
            //console.log("Sock gives me ", e);
            data = JSON.parse(e.data);
            //console.log("After parsing I have ", data);
            var type = data.type;
            var data = data.data;

            //console.log(type, data);
            if (typeof me.callbacks[type] === "object") {
                var l = me.callbacks[type].length;
                console.log("Triggering callbacks on ", type);
                for (var i = 0; i < l; i++) {
                    me.callbacks[type][i].call(me, data);
                }
            }
        };

        sockjs.onerror = function (e) {
            console.log("Error", e)
        }
        sockjs.onclose = function () {
            this.isConnected = false;
            setTimeout(this.reconnect.bind(this), 1000);
        }.bind(this);
        ;

        this.nativeConnection = sockjs;
    }

    this.reconnect = function() {
        this.connect(true);
    }.bind(this);

    this.connect();
}

TURTED.prototype.on = function (on, f) {
    if (typeof this.callbacks[on] === "undefined") {
        this.callbacks[on] = [];
    }
    this.callbacks[on].push(f);
};

TURTED.prototype.ident = function (id, username, token) {
    //enqueue the "ident" action until connected
    var f = function () {
        this.nativeConnection.send(this.encode("ident", {
            id: id,
            username: username,
            token: token
        }));
    }.bind(this);
    this.enqueue(f);
    this.enqueueReconnect(f);

    //in case the call was made on an open connection, immediately process the queue again
    if (this.isConnected) {
        this.processQueue();
    }
}

TURTED.prototype.join = function (channel) {
    //enqueue the "join" action until connected
    this.enqueue(function () {
        this.nativeConnection.send(this.encode("join", {
            channel: channel
        }));
    }.bind(this));

    //in case the call was made on an open connection, immediately process the queue again
    if (this.isConnected) {
        this.processQueue();
    }
}

TURTED.prototype.send = function (message) {
    this.nativeConnection.send(this.encode("message", message));
    console.log(this.encode("message", message));
}

TURTED.prototype.echo = function (message) {
    this.nativeConnection.send(this.encode("echo", message));
}

TURTED.prototype.encode = function (type, data) {
    return JSON.stringify({
        type: type,
        data: data
    });
}

TURTED.prototype.enqueue = function (f) {
    //store the callback
    this.callbackQueue.push(f);
}

TURTED.prototype.enqueueReconnect = function (f) {
    //store the callback
    this.reconnectQueue.push(f);
}

TURTED.prototype.processQueue = function () {
    var emergency = 100;
    while ((this.callbackQueue.length > 0) && (emergency > 0)) {
        var f = this.callbackQueue.shift();
        if (typeof f === "function") {
            f();
        }
        emergency--;
    }
}

TURTED.prototype.processReconnectQueue = function () {
    var emergency = 100;
    while ((this.reconnectQueue.length > 0) && (emergency > 0)) {
        var f = this.reconnectQueue.shift();
        if (typeof f === "function") {
            f();
        }
        emergency--;
    }
}

<commit_msg>Make sure to repear ident after each reconnect<commit_after>var TURTED = function (sockjs_url) {
    var me = this;
    this.callbacks = {};
    this.nativeConnection = {};
    this.callbackQueue = [];
    this.reconnectQueue = [];
    this.isConnected = false;

    this.connect = function (isReconnect) {
        var sockjs = new SockJS(sockjs_url);
        sockjs.onopen = function () {
            //fire queued events here
            me.isConnected = true;
            me.processQueue();
            if (isReconnect) {
                me.processReconnectQueue()
            }
        }
        sockjs.onmessage = function (e) {
            //console.log("Sock gives me ", e);
            data = JSON.parse(e.data);
            //console.log("After parsing I have ", data);
            var type = data.type;
            var data = data.data;

            //console.log(type, data);
            if (typeof me.callbacks[type] === "object") {
                var l = me.callbacks[type].length;
                console.log("Triggering callbacks on ", type);
                for (var i = 0; i < l; i++) {
                    me.callbacks[type][i].call(me, data);
                }
            }
        };

        sockjs.onerror = function (e) {
            console.log("Error", e)
        }
        sockjs.onclose = function () {
            this.isConnected = false;
            setTimeout(this.reconnect.bind(this), 1000);
        }.bind(this);
        ;

        this.nativeConnection = sockjs;
    }

    this.reconnect = function() {
        this.connect(true);
    }.bind(this);

    this.connect();
}

TURTED.prototype.on = function (on, f) {
    if (typeof this.callbacks[on] === "undefined") {
        this.callbacks[on] = [];
    }
    this.callbacks[on].push(f);
};

TURTED.prototype.ident = function (id, username, token) {
    //enqueue the "ident" action until connected
    var f = function () {
        this.nativeConnection.send(this.encode("ident", {
            id: id,
            username: username,
            token: token
        }));
    }.bind(this);
    this.enqueue(f);
    this.enqueueReconnect(f);

    //in case the call was made on an open connection, immediately process the queue again
    if (this.isConnected) {
        this.processQueue();
    }
}

TURTED.prototype.join = function (channel) {
    //enqueue the "join" action until connected
    this.enqueue(function () {
        this.nativeConnection.send(this.encode("join", {
            channel: channel
        }));
    }.bind(this));

    //in case the call was made on an open connection, immediately process the queue again
    if (this.isConnected) {
        this.processQueue();
    }
}

TURTED.prototype.send = function (message) {
    this.nativeConnection.send(this.encode("message", message));
    console.log(this.encode("message", message));
}

TURTED.prototype.echo = function (message) {
    this.nativeConnection.send(this.encode("echo", message));
}

TURTED.prototype.encode = function (type, data) {
    return JSON.stringify({
        type: type,
        data: data
    });
}

TURTED.prototype.enqueue = function (f) {
    //store the callback
    this.callbackQueue.push(f);
}

TURTED.prototype.enqueueReconnect = function (f) {
    //store the callback
    this.reconnectQueue.push(f);
}

TURTED.prototype.processQueue = function () {
    var emergency = 100;
    while ((this.callbackQueue.length > 0) && (emergency > 0)) {
        var f = this.callbackQueue.shift();
        if (typeof f === "function") {
            f();
        }
        emergency--;
    }
}

TURTED.prototype.processReconnectQueue = function () {
    var emergency = 100;
    for (var i=0;i<this.reconnectQueue.length;i++) {
        var f = this.reconnectQueue[i];
        if (typeof f === "function") {
            f();
        }
        emergency--;
    }
}

<|endoftext|>


<commit_before>/* =====================================================================================
 * @author Vadim Tiukov
 * =====================================================================================
 * Copyright (c) 2015 Rakuten Marketing
 * Licensed under MIT (https://github.com/linkshare/plus.garden.api/blob/master/LICENSE)
 * ===================================================================================== */

var Tester = function (logger, options) {
  var qs = require('querystring');
  var url = require('url');

  this.jsondiffpatch = require('jsondiffpatch');
  this.request = require('request');
  this.merge = require('merge');
  this.jspath = require('jspath');
  this.chai = require('chai');
  this.logger = logger;

  this.options = options;
  this.req = {};
  this.res = {};
  this.globals = {};

  this.getUrl = function (path) {
    var url_parts = url.parse(path, true);
    var query = url_parts.query;
    var params = qs.stringify(this.merge({}, this.globals.parameters, this.req.parameters, query));

    var host_proto = (url_parts.host
      ? url_parts.protocol + '//' + url_parts.host
      : this.options.host
    );

    return host_proto + url_parts.pathname + (params ? '?' + params : '');
  };

  this.convertToJson = function (table) {
    var result = {}, hashes = table.hashes(), i, j, key;

    for (i in hashes) {
      key = undefined;
      for (j in hashes[i]) {
        if (!key) {
          key = hashes[i][j];
        } else {
          result[key] = hashes[i][j];
        }
      }
    }

    return result;
  };

  this.convertToQuerystring = function (obj) {
    return Object.keys(obj).reduce(function(a,k){a.push(k+'='+encodeURIComponent(obj[k]));return a},[]).join('&')
  }

};

Tester.prototype = {

  then: function (next) {
    next();
  },

  addParameters: function (parameters, next) {
    this.req.parameters = this.merge({}, this.req.parameters, parameters);
    next();
  },

  addHeaders: function (headers, next) {
    if (headers.hashes) {
      headers = this.convertToJson(headers);
    }
    this.req.headers = this.merge({}, this.req.headers, headers);
    next();
  },

  addGlobalParameters: function (parameters, next) {
    this.globals.parameters = this.merge({}, this.globals.parameters, parameters);
    this.logger.debug('GlobalParameters: ' + JSON.stringify(this.globals.parameters));
    next();
  },

  addGlobalHeaders: function (headers, next) {
    if (headers.hashes) {
      headers = this.convertToJson(headers);
    }
    this.globals.headers = headers;
    this.logger.debug('addGlobalHeaders: ' + JSON.stringify(this.globals.headers));
    next();
  },

  setGlobals: function(globals, next) {
    this.globals = globals;
    next();
  },

  setBody: function (body, next) {
    if (body.hashes) {
      body = this.convertToJson(body);
      this.req.body = this.convertToQuerystring(body);
    } else {
      this.req.body = body;
    }
    next();
  },

  get: function (path, next) {
    this.http(path, 'get', next);
  },

  head: function (path, next) {
    this.http(path, 'head', next);
  },

  put: function (path, next) {
    this.http(path, 'put', next);
  },

  /**
   * @see https://tools.ietf.org/html/rfc5789
   */
  patch: function (path, next) {
    this.http(path, 'patch', next);
  },

  post: function (path, next) {
    this.http(path, 'post', next);
  },

  del: function (path, next) {
    this.http(path, 'del', next);
  },

  http: function (path, method, next) {
    var url = this.getUrl(path);
    this.logger.debug(method.toUpperCase() + ': ' + url);

    var headers = this.merge({}, this.globals.headers, this.req.headers);

    this.logger.debug('with headers: ' + JSON.stringify(headers));

    if (this.req.body) {
      this.logger.debug('with body: ' + JSON.stringify(this.req.body));
    }

    var sendObj = {
      url: url,
      method: method,
      body: this.req.body,
      headers: headers
    };

    this.request(sendObj, function (error, response, body) {
      if (error) throw error;
      this.req = {};
      this.res.body = (typeof body === 'undefined') ? '' : body; //HEAD has no body
      this.res.headers = response.headers;
      this.res.statusCode = response.statusCode;

      this.logger.debug('response body: ' + this.res.body);

      next(this.res);
    }.bind(this));
  },

  assertStatus: function (statusCode, next) {
    this.chai.assert.equal(statusCode, this.res.statusCode);
    next();
  },

  assertStatusNot: function (statusCode, next) {
    this.chai.assert.notEqual(statusCode, this.res.statusCode);
    next();
  },

  assertContentType: function (contentType, next) {
    this.chai.assert.include(this.res.headers['content-type'], contentType);
    next();
  },

  modifyAndAssertJSON: function(custom_assertion) {
    var self = this;
    custom_assertion(
      JSON.parse(this.res.body),
      function(expected_json, modified_json) {
        expected_json = JSON.parse(expected_json);
        self.chai.assert.deepEqual(expected_json, modified_json);
      }
    );
  },

  assertJSON: function (json, next) {
    this.chai.assert.deepEqual(json, JSON.parse(this.res.body));
    next();
  },

  assertJSONContains: function (key, value, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    this.chai.assert.equal(value, givenValue);
    next();
  },

  assertJSONElementPresented: function (key, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    if (!givenValue.length) {
      this.chai.assert(false, 'element '+ key +' are not presented');
    }
    next();
  },

  assertJSONElementNotPresented: function (key, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    if (givenValue.length) {
      this.chai.assert(false, 'element '+ key +' are presented');
    }
    next();
  },

  assertKeysEqual: function (path, keys, next) {
    var givenValue = this.jspath.apply(path, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  path);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    this.chai.assert.equal(Object.keys(givenValue).length, Object.keys(keys).length);
    for (var k in keys) {
        this.chai.assert.equal(givenValue.hasOwnProperty(keys[k]), true);
    }
    next();
  },

  assertValuesEqual: function (path, values, next) {
    var givenValues = this.jspath.apply(path, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  path);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValues));
    this.chai.assert.deepEqual(values, givenValues);
    next();
  },

  assertJSONLength: function (key, value, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    this.chai.assert.equal(Object.keys(givenValue).length, value);
    next();
  },

  assertText: function (text, next) {
    this.chai.assert.equal(text, this.res.body);
    next();
  },

  assertContains: function (value, next) {
    this.chai.assert.include(this.res.body, value);
    next();
  },

  assertHeaderEquals: function(header, value, next) {
    this.chai.assert.include(this.res.headers[header], value);
    next();
  },

  assertHeaderExists: function(header, next) {
    header = header.toLowerCase();
    this.chai.assert.isDefined(this.res.headers[header]);
    next();
  },

  assertHeaderNotExists: function(header, next) {
    header = header.toLowerCase();
    this.chai.assert.isUndefined(this.res.headers[header]);
    next();
  }
};

module.exports = Tester;
<commit_msg>Added comment for method modifyAndAssertJSON<commit_after>/* =====================================================================================
 * @author Vadim Tiukov
 * =====================================================================================
 * Copyright (c) 2015 Rakuten Marketing
 * Licensed under MIT (https://github.com/linkshare/plus.garden.api/blob/master/LICENSE)
 * ===================================================================================== */

var Tester = function (logger, options) {
  var qs = require('querystring');
  var url = require('url');

  this.jsondiffpatch = require('jsondiffpatch');
  this.request = require('request');
  this.merge = require('merge');
  this.jspath = require('jspath');
  this.chai = require('chai');
  this.logger = logger;

  this.options = options;
  this.req = {};
  this.res = {};
  this.globals = {};

  this.getUrl = function (path) {
    var url_parts = url.parse(path, true);
    var query = url_parts.query;
    var params = qs.stringify(this.merge({}, this.globals.parameters, this.req.parameters, query));

    var host_proto = (url_parts.host
      ? url_parts.protocol + '//' + url_parts.host
      : this.options.host
    );

    return host_proto + url_parts.pathname + (params ? '?' + params : '');
  };

  this.convertToJson = function (table) {
    var result = {}, hashes = table.hashes(), i, j, key;

    for (i in hashes) {
      key = undefined;
      for (j in hashes[i]) {
        if (!key) {
          key = hashes[i][j];
        } else {
          result[key] = hashes[i][j];
        }
      }
    }

    return result;
  };

  this.convertToQuerystring = function (obj) {
    return Object.keys(obj).reduce(function(a,k){a.push(k+'='+encodeURIComponent(obj[k]));return a},[]).join('&')
  }

};

Tester.prototype = {

  then: function (next) {
    next();
  },

  addParameters: function (parameters, next) {
    this.req.parameters = this.merge({}, this.req.parameters, parameters);
    next();
  },

  addHeaders: function (headers, next) {
    if (headers.hashes) {
      headers = this.convertToJson(headers);
    }
    this.req.headers = this.merge({}, this.req.headers, headers);
    next();
  },

  addGlobalParameters: function (parameters, next) {
    this.globals.parameters = this.merge({}, this.globals.parameters, parameters);
    this.logger.debug('GlobalParameters: ' + JSON.stringify(this.globals.parameters));
    next();
  },

  addGlobalHeaders: function (headers, next) {
    if (headers.hashes) {
      headers = this.convertToJson(headers);
    }
    this.globals.headers = headers;
    this.logger.debug('addGlobalHeaders: ' + JSON.stringify(this.globals.headers));
    next();
  },

  setGlobals: function(globals, next) {
    this.globals = globals;
    next();
  },

  setBody: function (body, next) {
    if (body.hashes) {
      body = this.convertToJson(body);
      this.req.body = this.convertToQuerystring(body);
    } else {
      this.req.body = body;
    }
    next();
  },

  get: function (path, next) {
    this.http(path, 'get', next);
  },

  head: function (path, next) {
    this.http(path, 'head', next);
  },

  put: function (path, next) {
    this.http(path, 'put', next);
  },

  /**
   * @see https://tools.ietf.org/html/rfc5789
   */
  patch: function (path, next) {
    this.http(path, 'patch', next);
  },

  post: function (path, next) {
    this.http(path, 'post', next);
  },

  del: function (path, next) {
    this.http(path, 'del', next);
  },

  http: function (path, method, next) {
    var url = this.getUrl(path);
    this.logger.debug(method.toUpperCase() + ': ' + url);

    var headers = this.merge({}, this.globals.headers, this.req.headers);

    this.logger.debug('with headers: ' + JSON.stringify(headers));

    if (this.req.body) {
      this.logger.debug('with body: ' + JSON.stringify(this.req.body));
    }

    var sendObj = {
      url: url,
      method: method,
      body: this.req.body,
      headers: headers
    };

    this.request(sendObj, function (error, response, body) {
      if (error) throw error;
      this.req = {};
      this.res.body = (typeof body === 'undefined') ? '' : body; //HEAD has no body
      this.res.headers = response.headers;
      this.res.statusCode = response.statusCode;

      this.logger.debug('response body: ' + this.res.body);

      next(this.res);
    }.bind(this));
  },

  assertStatus: function (statusCode, next) {
    this.chai.assert.equal(statusCode, this.res.statusCode);
    next();
  },

  assertStatusNot: function (statusCode, next) {
    this.chai.assert.notEqual(statusCode, this.res.statusCode);
    next();
  },

  assertContentType: function (contentType, next) {
    this.chai.assert.include(this.res.headers['content-type'], contentType);
    next();
  },

  /**
   * Enables modification of the JSON returned by the server via a custom callback, before the assertion is called.
   * This enables dynamic properties of the returned JSON to be stripped before the comparison is made.
   * This is for applying an assertion in the JSON with a custom callback for modifying the JSON before the comparison
   * takes place, so to account for dynamic response values, like ID fields, for example.
   *
   * this.Then(/^the response should match the following, taking into account that "([^"]*)" are dynamic fields:$/,
   *   function(fields, exp_json, callback) {
   *     fields = fields.split(',');
   *     this.api.modifyAndAssertJSON(function(actual_json, call){
   *       var field_name, i;
   *       for (i = 0; i < fields.length; i++) {
   *         field_name = fields[i];
   *         actual_json[field_name] = "";
   *       }
   *       call(exp_json, actual_json);
   *     })
   *     .then(callback);
   *   }
   * );
   */
  modifyAndAssertJSON: function(custom_assertion) {
    var self = this;
    custom_assertion(
      JSON.parse(this.res.body),
      function(expected_json, modified_json) {
        expected_json = JSON.parse(expected_json);
        self.chai.assert.deepEqual(expected_json, modified_json);
      }
    );
  },

  assertJSON: function (json, next) {
    this.chai.assert.deepEqual(json, JSON.parse(this.res.body));
    next();
  },

  assertJSONContains: function (key, value, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    this.chai.assert.equal(value, givenValue);
    next();
  },

  assertJSONElementPresented: function (key, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    if (!givenValue.length) {
      this.chai.assert(false, 'element '+ key +' are not presented');
    }
    next();
  },

  assertJSONElementNotPresented: function (key, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    if (givenValue.length) {
      this.chai.assert(false, 'element '+ key +' are presented');
    }
    next();
  },

  assertKeysEqual: function (path, keys, next) {
    var givenValue = this.jspath.apply(path, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  path);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    this.chai.assert.equal(Object.keys(givenValue).length, Object.keys(keys).length);
    for (var k in keys) {
        this.chai.assert.equal(givenValue.hasOwnProperty(keys[k]), true);
    }
    next();
  },

  assertValuesEqual: function (path, values, next) {
    var givenValues = this.jspath.apply(path, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  path);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValues));
    this.chai.assert.deepEqual(values, givenValues);
    next();
  },

  assertJSONLength: function (key, value, next) {
    var givenValue = this.jspath.apply(key, JSON.parse(this.res.body));
    this.logger.debug('JSON path: ' +  key);
    this.logger.debug('selected JSON: ' +  JSON.stringify(givenValue));
    this.chai.assert.equal(Object.keys(givenValue).length, value);
    next();
  },

  assertText: function (text, next) {
    this.chai.assert.equal(text, this.res.body);
    next();
  },

  assertContains: function (value, next) {
    this.chai.assert.include(this.res.body, value);
    next();
  },

  assertHeaderEquals: function(header, value, next) {
    this.chai.assert.include(this.res.headers[header], value);
    next();
  },

  assertHeaderExists: function(header, next) {
    header = header.toLowerCase();
    this.chai.assert.isDefined(this.res.headers[header]);
    next();
  },

  assertHeaderNotExists: function(header, next) {
    header = header.toLowerCase();
    this.chai.assert.isUndefined(this.res.headers[header]);
    next();
  }
};

module.exports = Tester;
<|endoftext|>


<commit_before>// BRICO
var through = require('through')
var level = require('level')

module.exports = Bricoleur

function Bricoleur (opts) {
  if (!opts) opts = {}

  var self = this

  // adhoc system for module connections
  // just connect directly to a module
  // really fast connections -- streams -- object -- trees
  // HANDLE SOCKET CONNECTIONS  to >---> from browser to map

  this.addSocket = function (id) {
    self[id] = through(function write (chunk) {
      this.queue(chunk)
    }, function end () {
      this.emit('end')
    }, {autoDestroy:false})
  }

  this.metaStream = through(MetaWrite, MetaEnd, {autoDestroy:false})

  function MetaWrite (chunk) {
    var data = JSON.parse(chunk)
    if (data.process) handleMapData(data) // map data
    if (data.host) {
      console.log(data)
      if (process.browser) window.document.title = data.host
    }
    if (data.fresh && data.fresh === true) console.log(JSON.parse(chunk))
  }
  function MetaEnd () {}

  // API / COMMANDS
  var api = {
    loadEnv: function (user,cb) {
    },
    make: function (mod,cb) {
      var libName = mod.id.toUpperCase()
      var lib = require(libName)
      _[mod.id] = new lib()
      if (process.browser && mod.html) _[mod.id].render(html)
      // verify module creation somehow
    },
    unmake: function (mod,cb) {
      _[mod.id].destroy()
    },
    conn: function (conss,cb) { // fix ugliness
      conns.forEach(function handleConnection (conn) {
        if (conn.match(/\+/) !== null) {
          var modA = conn.split('+')[0]
          , modB = conn.split('+')[1]
          _[modA].pipe(_[modB])
        }
        if (conn.match(/\-/) !== null) {
          var modA = conn.split('-')[0]
          , modB = conn.split('-')[1]
          modA.unpipe(modB)
        }
      })
    }
  }
  this.cmd = function (params) {
    var cmd = params[0]
    if (!api[cmd]) console.error('command '+cmd+' unknown')
    if (api[cmd]) api[cmd](params.slice(1))
  }
}
<commit_msg>ignore map data for now<commit_after>// BRICO
var through = require('through')
var level = require('level')

module.exports = Bricoleur

function Bricoleur (opts) {
  if (!opts) opts = {}

  var self = this

  // adhoc system for module connections
  // just connect directly to a module
  // really fast connections -- streams -- object -- trees
  // HANDLE SOCKET CONNECTIONS  to >---> from browser to map

  this.addSocket = function (id) {
    self[id] = through(function write (chunk) {
      this.queue(chunk)
    }, function end () {
      this.emit('end')
    }, {autoDestroy:false})
  }

  this.metaStream = through(MetaWrite, MetaEnd, {autoDestroy:false})

  function MetaWrite (chunk) {
    var data = JSON.parse(chunk)
    //if (data.process) HandleMapData(data) // map data
    if (data.host) {
      console.log(data)
      if (process.browser) window.document.title = data.host
    }
    if (data.fresh && data.fresh === true) console.log(JSON.parse(chunk))
  }
  function MetaEnd () {}

  // API / COMMANDS
  var api = {
    loadEnv: function (user,cb) {
    },
    make: function (mod,cb) {
      var libName = mod.id.toUpperCase()
      var lib = require(libName)
      _[mod.id] = new lib()
      if (process.browser && mod.html) _[mod.id].render(html)
      // verify module creation somehow
    },
    unmake: function (mod,cb) {
      _[mod.id].destroy()
    },
    conn: function (conss,cb) { // fix ugliness
      conns.forEach(function handleConnection (conn) {
        if (conn.match(/\+/) !== null) {
          var modA = conn.split('+')[0]
          , modB = conn.split('+')[1]
          _[modA].pipe(_[modB])
        }
        if (conn.match(/\-/) !== null) {
          var modA = conn.split('-')[0]
          , modB = conn.split('-')[1]
          modA.unpipe(modB)
        }
      })
    }
  }
  this.cmd = function (params) {
    var cmd = params[0]
    if (!api[cmd]) console.error('command '+cmd+' unknown')
    if (api[cmd]) api[cmd](params.slice(1))
  }
}
<|endoftext|>