<commit_before>// Copyright 2011 Lucas Clemente Vella
// Software distributed under Affero GPL, see http://gnu.org/licenses/agpl.txt

var ctx;
var mine;
var move_request = new XMLHttpRequest();
var event_request = new XMLHttpRequest();

var images = {};

// Bomb things:
var bomb = {
    'allowed': false,
    'active': false
};

/* Displays the bomb if it is to be shown. */
function display_bomb() {
    var visible = !params.bomb_used && bomb.allowed;

    document.getElementById('bomb')
	.style.setProperty('visibility',
			   visible ? 'visible' : 'hidden', null);
}

function toggle_bomb(ev) {
    var button = document.getElementById('bomb');

    if(params.bomb_used || !bomb.allowed) {
	bomb.active = false;
	display_bomb();
	return;
    }

    // TODO: marker over the bomb area
    //var canvas =  document.getElementById('game_canvas');

    if(!bomb.active) {
	bomb.active = true;
	button.style.setProperty('background-color', 'red', null);
	//toggle_bomb.move_event = canvas.addEventListener('move', on_click, false);
    }
    else {
	bomb.active = false;
	button.style.removeProperty('background-color');
    }
}

// Class Tile
function Tile(x0,y0) {
  this.s = '?';
  this.x = x0*26;
  this.y = y0*26;
}

Tile.prototype.draw = function() {
    var TEXT_COLOR = [
	'rgb(0,0,255)',
	'rgb(0,160,0)',
	'rgb(255,0,0)',
	'rgb(0,0,127)',
	'rgb(160,0,0)',
	'rgb(0,255,255)',
	'rgb(160,,160)',
	'rgb(0,0,0)'
    ];

    if(this.s >= 0 && this.s <= 8) {
	ctx.fillStyle = 'rgb(255,216,161)';
	ctx.fillRect(this.x, this.y, 25, 25);
	if(this.s > 0) {
	    ctx.fillStyle = TEXT_COLOR[this.s-1];
	    ctx.fillText(this.s, this.x + 12.5, this.y + 12.5);
	}
    }
    else if(this.s == 'r' || this.s == 'b') {
	ctx.fillStyle = 'rgb(251,170,56)';
	ctx.fillRect(this.x, this.y, 25, 25);
	var icon = images[(this.s == 'b') ? 'saphire' : 'ruby'];
	ctx.drawImage(icon, this.x + 2, this.y + 5);
    }
    else {
	ctx.fillStyle = 'rgb(227,133,0)';
	ctx.fillRect(this.x, this.y, 25, 25);
    }
};

function load_img(name) {
    var img = new Image();
    img.src = "/static/images/" + name + ".png";
    images[name] = img;
}

function update_points() {
    var p1 = 0;
    var p2 = 0;

    for(var i = 0; i < 16; ++i)
	for(var j = 0; j < 16; ++j){
	    if(mine[i][j].s == 'r')
		++p1;
	    else if(mine[i][j].s == 'b')
		++p2;
	}

    if(params.player == 1)
	bomb.allowed = p2 > p1;
    else
	bomb.allowed = p1 > p2;

    display_bomb();

    document.getElementById('p1_pts').innerHTML = String(p1);
    document.getElementById('p2_pts').innerHTML = String(p2);
}

// TODO: localization
function set_state(state) {
    var msg;
    if(params.player) {
	if(state == params.player) {
	    msg = 'Sua vez!';
	}
	else if(state == 1 || state == 2) {
	    msg = '';
	}
	else if(state == 3 || state == 4) {
	    msg = 'Fim de jogo. ';
	    if((state - 2) == params.player)
		msg += 'Você venceu!';
	    else
		msg += 'Você perdeu.';
	}
	else
	    return; // What else can I do?
    } else {
	// Spectator mode.
	var state_msgs =
	    ['',
	     'Vez do vermelho.',
	     'Vez do azul.',
	     'Vermelho venceu.',
	     'Azul venceu.'];
	msg = state_msgs[state];
    }

    document.getElementById('message').innerHTML = msg;
    params.state = state;
}

function handle_event(msg) {
    var parser = /(\d+),(\d+):(.)/;
    var changes = msg.split('\n');
    var seq_num = parseInt(changes[0]);

    if(seq_num <= params.seq_num)
	return;
    params.seq_num = seq_num;

    set_state(parseInt(changes[1]));

    for(var i = 2; i < changes.length; ++i) {
	var res = parser.exec(changes[i]);
	if(res) {
	    var m = parseInt(res[1]);
	    var n = parseInt(res[2]);
	    // TODO: validate data
	    mine[m][n].s = res[3];
	    mine[m][n].draw();
	    update_points();
	}
    }
}

function register_event() {
    if(params.state >= 3 || register_event.last_status == 410)
	return; // Game is over

    event_request.open('GET', '/event/'+ params.channel, true);
    if(register_event.etag)
	event_request.setRequestHeader('If-None-Match', register_event.etag);
    event_request.setRequestHeader('If-Modified-Since', params.last_change);
    event_request.onreadystatechange = function(ev){
	if (event_request.readyState == 4) {
	    register_event.last_status == event_request.status;
	    if(event_request.status == 200) {
		register_event.error_count = 0;
		register_event.etag = event_request.getResponseHeader('Etag')
		params.last_change = event_request.getResponseHeader('Last-Modified')

		handle_event(event_request.responseText);
		register_event();
	    }
	    else {
		// If this is the first error, try again right away
		// If not, delay some time before trying again
		if(register_event.error_count)
		    register_event.error_count = Math.min(register_event.error_count + 1, 10);
		else
		    register_event.error_count = 1;

		if(register_event.error_count > 1) {
		    var delay = (register_event.error_count - 1) * 500;
		    setTimeout(register_event, delay);
		}
		else
		    register_event();
	    }
	}
    }
    event_request.send(null);
}

function on_click(ev) {
    if(ev.button != 0 || params.player != params.state)
	return;

    var m;
    var n;

    m = ev.clientX + document.body.scrollLeft +
	document.documentElement.scrollLeft - this.offsetLeft;
    n = ev.clientY + document.body.scrollTop +
        document.documentElement.scrollTop - this.offsetTop;

    m = Math.floor(m / 26);
    n = Math.floor(n / 26);

    if(!bomb.active && mine[m][n].s != '?')
	return;

    // TODO: indicate activity

    var url = '/game/'+ params.game_id + '/move/?m=' + m + '&n=' + n;
    var bombed = false;
    if(bomb.active) {
	url += '&bomb=y';
	bombed = true;
    }
    move_request.open('GET', url, true);
    move_request.onreadystatechange = function(ev){
	if (move_request.readyState == 4) {
	    if(move_request.status == 200) {
		// TODO: find a more reliable way to know if the bomb was used
		if(bombed) {
		    params.bomb_used = true;
		    toggle_bomb();
		}
	    }
	    // TODO: else: treat error
	    // TODO: stop activity indication
	}
    };
    move_request.send(null);
}

function init() {
    var canvas = document.getElementById('game_canvas');
    if (!canvas || !canvas.getContext) {
	// Panic return
	// TODO: add friendly message explaining why IE sucks
	return;
    }

    mine = new Array(16);
    for (var i=0; i < 16; ++i) {
	mine[i] = new Array(16);
	for (var j=0; j < 16; ++j)
	    mine[i][j] = new Tile(i,j);
    }

    if(params.mine)
	for(var i = 0; i < 256; ++i)
	    mine[Math.floor(i/16)][i%16].s = params.mine.charAt(i);

    ctx = canvas.getContext('2d');
    ctx.font = "17pt Arial, Helvetica, sans-serif";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    ctx.shadowOffsetX = 1;
    ctx.shadowOffsetY = 1;
    ctx.shadowBlur = 1;
    ctx.shadowColor = "black";

    // Draw map
    for(var i = 0; i < 16; ++i)
	for(var j = 0; j < 16; ++j)
	    mine[i][j].draw();

    // Put display in current state
    set_state(params.state);
    update_points();

    // Receive updates from server
    register_event();

    if(params.player) { // Not a spectator
	// Wait for user
	canvas.addEventListener('click', on_click, false);
	document.getElementById('bomb').addEventListener('click', toggle_bomb, false);
    }
}

// Load Images
load_img("saphire");
load_img("ruby");

window.addEventListener('load', init, false);
<commit_msg>Rudimentary marking of the last clicks.<commit_after>// Copyright 2011 Lucas Clemente Vella
// Software distributed under Affero GPL, see http://gnu.org/licenses/agpl.txt

var ctx;
var mine;
var move_request = new XMLHttpRequest();
var event_request = new XMLHttpRequest();

var images = {};
var last_click = [null, null];

// Bomb things:
var bomb = {
    'allowed': false,
    'active': false
};

/* Displays the bomb if it is to be shown. */
function display_bomb() {
    var visible = !params.bomb_used && bomb.allowed;

    document.getElementById('bomb')
	.style.setProperty('visibility',
			   visible ? 'visible' : 'hidden', null);
}

function toggle_bomb(ev) {
    var button = document.getElementById('bomb');

    if(params.bomb_used || !bomb.allowed) {
	bomb.active = false;
	display_bomb();
	return;
    }

    // TODO: marker over the bomb area
    //var canvas =  document.getElementById('game_canvas');

    if(!bomb.active) {
	bomb.active = true;
	button.style.setProperty('background-color', 'red', null);
	//toggle_bomb.move_event = canvas.addEventListener('move', on_click, false);
    }
    else {
	bomb.active = false;
	button.style.removeProperty('background-color');
    }
}

// Class Tile
function Tile(x0,y0) {
  this.s = '?';
  this.x = x0*26;
  this.y = y0*26;
}

Tile.prototype.draw = function() {
    var TEXT_COLOR = [
	'rgb(0,0,255)',
	'rgb(0,160,0)',
	'rgb(255,0,0)',
	'rgb(0,0,127)',
	'rgb(160,0,0)',
	'rgb(0,255,255)',
	'rgb(160,,160)',
	'rgb(0,0,0)'
    ];

    if(this.s >= 0 && this.s <= 8) {
	ctx.fillStyle = 'rgb(255,216,161)';
	ctx.fillRect(this.x, this.y, 25, 25);
	if(this.s > 0) {
	    ctx.fillStyle = TEXT_COLOR[this.s-1];
	    ctx.fillText(this.s, this.x + 12.5, this.y + 12.5);
	}
    }
    else if(this.s == 'r' || this.s == 'b') {
	ctx.fillStyle = 'rgb(251,170,56)';
	ctx.fillRect(this.x, this.y, 25, 25);
	var icon = images[(this.s == 'b') ? 'saphire' : 'ruby'];
	ctx.drawImage(icon, this.x + 2, this.y + 5);
    }
    else {
	ctx.fillStyle = 'rgb(227,133,0)';
	ctx.fillRect(this.x, this.y, 25, 25);
    }

    var MARK_COLOR = [
	'rgb(255,0,0)',
	'rgb(0,0,255)'
    ];

    for(var i = 1; i <= 2; ++i) {
	if(last_click[i] == this) {
	    ctx.strokeStyle = MARK_COLOR[i-1];
	    ctx.strokeRect(this.x + 2, this.y + 2, 21, 21);
	    break;
	}
    }
};

function load_img(name) {
    var img = new Image();
    img.src = "/static/images/" + name + ".png";
    images[name] = img;
}

function update_points() {
    var p1 = 0;
    var p2 = 0;

    for(var i = 0; i < 16; ++i)
	for(var j = 0; j < 16; ++j){
	    if(mine[i][j].s == 'r')
		++p1;
	    else if(mine[i][j].s == 'b')
		++p2;
	}

    if(params.player == 1)
	bomb.allowed = p2 > p1;
    else
	bomb.allowed = p1 > p2;

    display_bomb();

    document.getElementById('p1_pts').innerHTML = String(p1);
    document.getElementById('p2_pts').innerHTML = String(p2);
}

// TODO: localization
function set_state(state) {
    var msg;
    if(params.player) {
	if(state == params.player) {
	    msg = 'Sua vez!';
	}
	else if(state == 1 || state == 2) {
	    msg = '';
	}
	else if(state == 3 || state == 4) {
	    msg = 'Fim de jogo. ';
	    if((state - 2) == params.player)
		msg += 'Você venceu!';
	    else
		msg += 'Você perdeu.';
	}
	else
	    return; // What else can I do?
    } else {
	// Spectator mode.
	var state_msgs =
	    ['',
	     'Vez do vermelho.',
	     'Vez do azul.',
	     'Vermelho venceu.',
	     'Azul venceu.'];
	msg = state_msgs[state];
    }

    document.getElementById('message').innerHTML = msg;
    params.state = state;
}

function handle_event(msg) {
    var parser = /(\d+),(\d+):(.)/;
    var changes = msg.split('\n');
    var seq_num = parseInt(changes[0]);

    if(seq_num <= params.seq_num)
	return;
    params.seq_num = seq_num;

    for(var i = 2; i < changes.length; ++i) {
	var res = parser.exec(changes[i]);
	if(res) {
	    var m = parseInt(res[1]);
	    var n = parseInt(res[2]);

	    if(m < 0 || m > 15 || n < 0 || n > 15)
		continue; // Invalid answer; what else can I do?

	    mine[m][n].s = res[3];
	    if(i == 2 && (params.state == 1 || params.state == 2)) {
		var previous = last_click[params.state];
		last_click[params.state] = mine[m][n];
		if(previous)
		    previous.draw();
	    }
	    mine[m][n].draw();
	}
    }
    set_state(parseInt(changes[1]));
    update_points();
}

function register_event() {
    if(params.state >= 3 || register_event.last_status == 410)
	return; // Game is over

    event_request.open('GET', '/event/'+ params.channel, true);
    if(register_event.etag)
	event_request.setRequestHeader('If-None-Match', register_event.etag);
    event_request.setRequestHeader('If-Modified-Since', params.last_change);
    event_request.onreadystatechange = function(ev){
	if (event_request.readyState == 4) {
	    register_event.last_status == event_request.status;
	    if(event_request.status == 200) {
		register_event.error_count = 0;
		register_event.etag = event_request.getResponseHeader('Etag')
		params.last_change = event_request.getResponseHeader('Last-Modified')

		handle_event(event_request.responseText);
		register_event();
	    }
	    else {
		// If this is the first error, try again right away
		// If not, delay some time before trying again
		if(register_event.error_count)
		    register_event.error_count = Math.min(register_event.error_count + 1, 10);
		else
		    register_event.error_count = 1;

		if(register_event.error_count > 1) {
		    var delay = (register_event.error_count - 1) * 500;
		    setTimeout(register_event, delay);
		}
		else
		    register_event();
	    }
	}
    }
    event_request.send(null);
}

function on_click(ev) {
    if(ev.button != 0 || params.player != params.state)
	return;

    var m;
    var n;

    m = ev.clientX + document.body.scrollLeft +
	document.documentElement.scrollLeft - this.offsetLeft;
    n = ev.clientY + document.body.scrollTop +
        document.documentElement.scrollTop - this.offsetTop;

    m = Math.floor(m / 26);
    n = Math.floor(n / 26);

    if(!bomb.active && mine[m][n].s != '?')
	return;

    // TODO: indicate activity

    var url = '/game/'+ params.game_id + '/move/?m=' + m + '&n=' + n;
    var bombed = false;
    if(bomb.active) {
	url += '&bomb=y';
	bombed = true;
    }
    move_request.open('GET', url, true);
    move_request.onreadystatechange = function(ev){
	if (move_request.readyState == 4) {
	    if(move_request.status == 200) {
		// TODO: find a more reliable way to know if the bomb was used
		if(bombed) {
		    params.bomb_used = true;
		    toggle_bomb();
		}
	    }
	    // TODO: else: treat error
	    // TODO: stop activity indication
	}
    };
    move_request.send(null);
}

function init() {
    var canvas = document.getElementById('game_canvas');
    if (!canvas || !canvas.getContext) {
	// Panic return
	// TODO: add friendly message explaining why IE sucks
	return;
    }

    mine = new Array(16);
    for (var i=0; i < 16; ++i) {
	mine[i] = new Array(16);
	for (var j=0; j < 16; ++j)
	    mine[i][j] = new Tile(i,j);
    }

    if(params.mine)
	for(var i = 0; i < 256; ++i)
	    mine[Math.floor(i/16)][i%16].s = params.mine.charAt(i);

    // Text presets
    ctx = canvas.getContext('2d');
    ctx.font = "17pt Arial, Helvetica, sans-serif";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    // Strokes presets
    ctx.lineJoin = 'round';
    ctx.lineWidth = 3;

    // Shadows presets
    // TODO: Fix buggy shadows drawing...
    ctx.shadowOffsetX = 1;
    ctx.shadowOffsetY = 1;
    ctx.shadowBlur = 1;
    ctx.shadowColor = "black";

    // Draw map
    for(var i = 0; i < 16; ++i)
	for(var j = 0; j < 16; ++j)
	    mine[i][j].draw();

    // Put display in current state
    set_state(params.state);
    update_points();

    // Receive updates from server
    register_event();

    if(params.player) { // Not a spectator
	// Wait for user
	canvas.addEventListener('click', on_click, false);
	document.getElementById('bomb').addEventListener('click', toggle_bomb, false);
    }
}

// Load Images
load_img("saphire");
load_img("ruby");

window.addEventListener('load', init, false);
<|endoftext|>


<commit_before>var put_locale = function(svgCanvas, given_param){
	var lang_param;
	if(given_param) {
		lang_param = given_param;
	} else {
		lang_param = $.pref('lang');
		if(!lang_param) {
			if (navigator.userLanguage) // Explorer
				lang_param = navigator.userLanguage;
			else if (navigator.language) // FF, Opera, ...
				lang_param = navigator.language;
			if (lang_param == "")
				return;
		}
		if(typeof(lang_param) != 'string') {
			alert(typeof(lang_param));
			alert(lang_param)
		}
		// don't bother on first run if language is English
		if(lang_param.indexOf("en") == 0) return;
	}

	var url = "locale/lang." + lang_param + ".js";
	$.get(url, function(data){
		var LangData = eval(data), js_strings;
		$.each(LangData, function(i, data) {
			if(data.id) {
				var elem = $('#'+data.id)[0];
				if(elem) {
					if(data.title)
						elem.title = data.title;
					if(data.textContent) {
						// Only replace text nodes, not elements
						$.each(elem.childNodes, function(j, node) {
							if(node.nodeType == 3) {
								node.textContent = data.textContent;
							}
						});
					}
				}
			} else if(data.js_strings) {
				js_strings = data.js_strings;
			}
		});
		svgCanvas.setLang(lang_param, js_strings);
	},"json");
};
<commit_msg>Possibly fixed lang detection script<commit_after>var put_locale = function(svgCanvas, given_param){
	var lang_param;
	if(given_param) {
		lang_param = given_param;
	} else {
		lang_param = $.pref('lang');
		if(!lang_param) {
			if (navigator.userLanguage) // Explorer
				lang_param = navigator.userLanguage;
			else if (navigator.language) // FF, Opera, ...
				lang_param = navigator.language;
			if (lang_param == "")
				return;
		}
		// don't bother on first run if language is English
		if(String(lang_param).indexOf("en") == 0) return;
	}

	var url = "locale/lang." + lang_param + ".js";
	$.get(url, function(data){
		var LangData = eval(data), js_strings;
		$.each(LangData, function(i, data) {
			if(data.id) {
				var elem = $('#'+data.id)[0];
				if(elem) {
					if(data.title)
						elem.title = data.title;
					if(data.textContent) {
						// Only replace text nodes, not elements
						$.each(elem.childNodes, function(j, node) {
							if(node.nodeType == 3) {
								node.textContent = data.textContent;
							}
						});
					}
				}
			} else if(data.js_strings) {
				js_strings = data.js_strings;
			}
		});
		svgCanvas.setLang(lang_param, js_strings);
	},"json");
};
<|endoftext|>


<commit_before>import piwikTracker from '../../shared/services/piwik-tracker/piwik-tracker';
import { SET_MAP_BASE_LAYER, TOGGLE_MAP_OVERLAY } from '../../map/ducks/map/map';

const piwik = {
  TRACK_EVENT: 'trackEvent',
  SET_MAP_BASE_LAYER: 'achtergrond',
  TOGGLE_MAP_OVERLAY: 'kaartlaag'
};

const piwikMiddleware = (store) => (next) => (action) => {
  const nextAction = action;

  const { map, mapLayers } = store.getState();

  if (action.type) {
    if (action.type === SET_MAP_BASE_LAYER && action.payload) {
      const { baseLayers } = mapLayers;
      const newBaseLayerId = action.payload;

      // Check if layer isn't already activated
      if (newBaseLayerId !== map.baseLayer) {
        // Get layer info from the store
        const newBaseLayer = baseLayers.items.find((b) => b.value === newBaseLayerId);

        // Information for piwik
        piwikTracker([piwik.TRACK_EVENT, piwik[action.type],
          newBaseLayer.category, newBaseLayer.label]);
      }
    }

    if (action.type === TOGGLE_MAP_OVERLAY && action.mapLayerId) {
      const { panelLayers } = mapLayers;
      const { overlays: activePanelLayers } = map;
      const newPanelLayerId = action.mapLayerId;

      // Check if layer isn't already activated
      if (!activePanelLayers.filter((b) => b.id === newPanelLayerId).length) {
        // Get layer info from the store
        const newPanelLayer = panelLayers.items.find((b) => b.id === newPanelLayerId);

        // Look through legendItems if not found
        // Return object with info about parentPanelLayer
        const parentPanelLayer = (!newPanelLayer) ? panelLayers.items.map(
            (panelLayer) => (panelLayer.legendItems.filter(
              (b) => b.id === newPanelLayerId)).length && panelLayer
          ).filter((obj) => obj) : false;

        const activePanelLayer = newPanelLayer || parentPanelLayer ? parentPanelLayer[0] : null;

        // Information for piwik
        piwikTracker([piwik.TRACK_EVENT, piwik[action.type],
          activePanelLayer.category, activePanelLayer.title]);
      }
    }
  }

  return next(nextAction);
};

export default piwikMiddleware;
<commit_msg>Splitted function to get baseLayer and panelLayer from state after action for changing them<commit_after>import piwikTracker from '../../shared/services/piwik-tracker/piwik-tracker';
import { SET_MAP_BASE_LAYER, TOGGLE_MAP_OVERLAY } from '../../map/ducks/map/map';

const piwik = {
  TRACK_EVENT: 'trackEvent',
  SET_MAP_BASE_LAYER: 'achtergrond',
  TOGGLE_MAP_OVERLAY: 'kaartlaag'
};

/** Match the BaseLayer from the Action to the State */
const getMapBaseLayer = (payload, { map, mapLayers }) => {
  const { baseLayers } = mapLayers;
  const newBaseLayerId = payload;

  let newBaseLayer;

  // Check if layer isn't already activated
  if (newBaseLayerId !== map.baseLayer) {
    // Return layer info from the store
    newBaseLayer = baseLayers.items.find((b) => b.value === newBaseLayerId);
  }

  return newBaseLayer || null;
};

/** Match the PanelLayer from the Action to the State */
const getMapPanelLayer = (payload, { map, mapLayers }) => {
  const { panelLayers } = mapLayers;
  const { overlays: activePanelLayers } = map;
  const newPanelLayerId = payload;

  let newPanelLayer;
  let parentPanelLayer;

  // Check if layer isn't already activated
  if (!activePanelLayers.filter((b) => b.id === newPanelLayerId).length) {
    // Get layer info from the store
    newPanelLayer = panelLayers.items.find((b) => b.id === newPanelLayerId);

    // Look through legendItems if not found
    // Return object with info about parentPanelLayer
    parentPanelLayer = (!newPanelLayer) ? panelLayers.items.map(
        (panelLayer) => (panelLayer.legendItems.filter(
          (b) => b.id === newPanelLayerId)).length && panelLayer
      ).filter((obj) => obj) : false;
  }

  return newPanelLayer || parentPanelLayer ? parentPanelLayer[0] : null;
};

const piwikMiddleware = (store) => (next) => (action) => {
  const nextAction = action;

  const state = store.getState();

  let event;
  let value;

  if (action.type) {
    if (action.type === SET_MAP_BASE_LAYER && action.payload) {
      const newBaseLayer = getMapBaseLayer(action.payload, state);

      event = newBaseLayer.category;
      value = newBaseLayer.label;
    }

    if (action.type === TOGGLE_MAP_OVERLAY && action.mapLayerId) {
      const newPanelLayer = getMapPanelLayer(action.mapLayerId, state);

      event = newPanelLayer.category;
      value = newPanelLayer.title;
    }

    // Information for piwik
    if (event && value) {
      piwikTracker([piwik.TRACK_EVENT, piwik[action.type],
        event, value]);
    }
  }

  return next(nextAction);
};

export default piwikMiddleware;
<|endoftext|>


<commit_before>let mod = new ActionObj('Upgrade');
module.exports = mod;

mod.nextTarget = function() {
    return this.creep.room.controller;
};

mod.loop = function(creep) {
    return this.loop0(creep, (creep, target) => {
        const result = creep.upgradeController(target);
        if(result == ERR_NOT_IN_RANGE) {
            creep.moveTo(target, {visualizePathStyle: {stroke: '#ffffff'}});
        }
    });
};<commit_msg>let creep goes to controller even if controller has been blocked so creep will not occupy around container<commit_after>let mod = new ActionObj('Upgrade');
module.exports = mod;

mod.nextTarget = function() {
    return this.creep.room.controller;
};

mod.loop = function(creep) {
    return this.loop0(creep, (creep, target) => {
        const result = creep.upgradeController(target);
        if(result == ERR_NOT_IN_RANGE) {
            creep.moveTo(target, {visualizePathStyle: {stroke: '#ffffff'}});
        } else if(result == ERR_INVALID_TARGET && target.upgradeBlocked > 0) {
            //Still goes there in case not to occupy container
            creep.moveTo(target, {visualizePathStyle: {stroke: '#ffffff'}});
        }
    });
};<|endoftext|>


<commit_before>class AnimationLoop {
    constructor(options) {
        if (!(this instanceof AnimationLoop)) {
            return new AnimationLoop(options);
        }

        this.animations = AnimationLoop.createAnimations(options);
        this.startTime = null;
        this.remaining = this.animations.length;
        this.complete = false;
    }

    static create(options) {
        return new this(options).start();
    }

    static createAnimations(options) {
        if (not(options, 'Array')) {
            options = [options];
        }

        return options.map(option => new Animation(option));
    }

    start() {
        this.rafId = raf(this.cycle.bind(this));
        return this;
    }

    // @TODO: pauseAll()

    cancel() {
        caf(this.rafId);
        return this;
    }

    add(options) {
        var anims = AnimationLoop.createAnimations(options);
        this.animations = this.animations.concat(anims);
    }

    cycle(now) {
        var startTime, lastTime, runningTime, deltaT, timing;
        var pauseThreshold = this.pauseThreshold;

        if (!this.animations.length) {
            return;
        }

        startTime = this.startTime = this.startTime || now;
        lastTime = this.lastTime = this.lastTime || now;
        runningTime = this.runningTime || now - startTime;
        deltaT = now - lastTime;

        this.lastTime = now;
        this.rafId = raf(this.cycle.bind(this));

        if (pauseThreshold && deltaT >= pauseThreshold) {
            return;
        }

        // runningTime only updates when the time between
        // frames is short enough, as defined by the user.
        // the gap can be wide if the tab becomes inactive, etc.
        // ref: hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
        this.runningTime = (runningTime += deltaT);

        // @TODO: pass previous timing object to before() ?
        timing = { now, deltaT, startTime, lastTime, runningTime };

        this.animations.forEach(anim => {
            // @TODO: add this.pause() ?
            var pct;
            var passArgs = [timing];

            if (anim.duration) {
                pct = Math.max(0, Math.min(1, runningTime / anim.duration));
                anim.lastPct = anim.pct || pct;
                anim.pct = pct;
                passArgs.push({ now: pct, last: anim.lastPct });
            }

            if (Array.isArray(anim.args)) {
                passArgs = passArgs.concat(anim.args);
            }

            if (typeof anim.before === 'function') {
                anim.before(...passArgs);
            }

            if (!anim.running || anim.canceled) {
                return false;
            }

            return true;
        });

        this.animations = this.animations.filter(anim => {
            var passArgs = [timing];

            if (anim.duration) {
                passArgs.push({ now: anim.pct, last: anim.lastPct });
            }

            if (Array.isArray(anim.args)) {
                passArgs = passArgs.concat(anim.args);
            }

            anim.render(...passArgs);

            if (anim.canceled) {
                this.remaining--;
            }

            // if animation running time is 100% of its duration, don't queue it up again
            if (!anim.canceled && (!anim.running || anim.pct === 1)) {
                anim.stop();
                this.remaining--;

                if (typeof anim.done === 'function') {
                    anim.done(...passArgs);
                }
            }

            if (this.remaining === 0 && typeof this.oncomplete === 'function') {
                this.complete = true;
                this.oncomplete();
            }

            if (!anim.running || anim.canceled) {
                return false;
            }

            return true;
        });
    }
}
<commit_msg>Count of remaining animations increases with AnimationLoop#add<commit_after>class AnimationLoop {
    constructor(options) {
        if (!(this instanceof AnimationLoop)) {
            return new AnimationLoop(options);
        }

        this.animations = AnimationLoop.createAnimations(options);
        this.startTime = null;
        this.remaining = this.animations.length;
        this.complete = false;
    }

    static create(options) {
        return new this(options).start();
    }

    static createAnimations(options) {
        if (not(options, 'Array')) {
            options = [options];
        }

        return options.map(option => new Animation(option));
    }

    start() {
        this.rafId = raf(this.cycle.bind(this));
        return this;
    }

    // @TODO: pauseAll()

    cancel() {
        caf(this.rafId);
        return this;
    }

    add(options) {
        var anims = AnimationLoop.createAnimations(options);
        this.animations = this.animations.concat(anims);
        this.remaining += anims.length;
    }

    cycle(now) {
        var startTime, lastTime, runningTime, deltaT, timing;
        var pauseThreshold = this.pauseThreshold;

        if (!this.animations.length) {
            return;
        }

        startTime = this.startTime = this.startTime || now;
        lastTime = this.lastTime = this.lastTime || now;
        runningTime = this.runningTime || now - startTime;
        deltaT = now - lastTime;

        this.lastTime = now;
        this.rafId = raf(this.cycle.bind(this));

        if (pauseThreshold && deltaT >= pauseThreshold) {
            return;
        }

        // runningTime only updates when the time between
        // frames is short enough, as defined by the user.
        // the gap can be wide if the tab becomes inactive, etc.
        // ref: hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
        this.runningTime = (runningTime += deltaT);

        // @TODO: pass previous timing object to before() ?
        timing = { now, deltaT, startTime, lastTime, runningTime };

        this.animations.forEach(anim => {
            // @TODO: add this.pause() ?
            var pct;
            var passArgs = [timing];

            if (anim.duration) {
                pct = Math.max(0, Math.min(1, runningTime / anim.duration));
                anim.lastPct = anim.pct || pct;
                anim.pct = pct;
                passArgs.push({ now: pct, last: anim.lastPct });
            }

            if (Array.isArray(anim.args)) {
                passArgs = passArgs.concat(anim.args);
            }

            if (typeof anim.before === 'function') {
                anim.before(...passArgs);
            }

            if (!anim.running || anim.canceled) {
                return false;
            }

            return true;
        });

        this.animations = this.animations.filter(anim => {
            var passArgs = [timing];

            if (anim.duration) {
                passArgs.push({ now: anim.pct, last: anim.lastPct });
            }

            if (Array.isArray(anim.args)) {
                passArgs = passArgs.concat(anim.args);
            }

            anim.render(...passArgs);

            if (anim.canceled) {
                this.remaining--;
            }

            // if animation running time is 100% of its duration, don't queue it up again
            if (!anim.canceled && (!anim.running || anim.pct === 1)) {
                anim.stop();
                this.remaining--;

                if (typeof anim.done === 'function') {
                    anim.done(...passArgs);
                }
            }

            if (this.remaining === 0 && typeof this.oncomplete === 'function') {
                this.complete = true;
                this.oncomplete();
            }

            if (!anim.running || anim.canceled) {
                return false;
            }

            return true;
        });
    }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2014 - 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
View is currently directly implemented as Nova VM Spawns Viz
and extended into Nova CPU/Memory/Disk Resource Charts

instantiated similar to:

this.vmSpawnChart = new StackedBarChartCollection({
    urlPrefix: '/nova/hypervisor/spawns'
});

this.vmSpawnChartView = new StackedBarChartView({
    chartTitle: "VM Spawns",
    collection: this.vmSpawnChart,
    height: 300,
    infoCustom: 'novaSpawns',
    el: '#nova-report-r1-c2',
    width: $('#nova-report-r1-c2').width(),
    yAxisLabel: 'Spawn Events'
});
*/

// view is linked to collection when instantiated in api_perf_report.html

var StackedBarChartView = GoldstoneBaseView.extend({

    defaults: {
        margin: {
            top: 45,
            right: 40,
            bottom: 60,
            left: 70
        }
    },

    processOptions: function() {

        // this will invoke the processOptions method of the parent view,
        // and also add an additional param of featureSet which is used
        // to create a polymorphic interface for a variety of charts
        StackedBarChartView.__super__.processOptions.apply(this, arguments);

        this.defaults.featureSet = this.options.featureSet || null;
    },

    specialInit: function() {
        var ns = this.defaults;

        ns.yAxis = d3.svg.axis()
            .scale(ns.y)
            .orient("left")
            .tickFormat(d3.format("01d"));

        // differentiate color sets for mem and cpu charts
        if (ns.featureSet === 'mem' || ns.featureSet === 'cpu') {
            ns.color = d3.scale.ordinal().range(ns.colorArray.distinct[3]);
        } else {
            // this includes "VM Spawns" and "Disk Resources" chars
            ns.color = d3.scale.ordinal()
                .range(ns.colorArray.distinct[2]);
        }

    },

    dataPrep: function(data) {

        /*
        this is where the fetched JSON payload is transformed into a
        dataset than can be consumed by the D3 charts
        each chart may have its own perculiarities

        IMPORTANT:
        the order of items that are 'push'ed into the
        result array matters. After 'eventTime', the items
        will be stacked on the graph from the bottom of
        the graph upward. Or another way of saying it is
        the first item listed will be first one to be rendered
        from the x-axis of the graph going upward.
        */

        var ns = this.defaults;

        var result = [];

        if (ns.featureSet === 'cpu') {

            // CPU Resources chart data prep
            // {timestamp: [used, phys, virt]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Used": item[0],
                    "Physical": item[1],
                    "Virtual": item[2]
                });
            });

        } else if (ns.featureSet === 'disk') {

            // Disk Resources chart data prep
            // {timestamp: [used, total]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Used": item[0],
                    "Total": item[1]
                });
            });

        } else if (ns.featureSet === 'mem') {

            // Memory Resources chart data prep
            // {timestamp: [used, phys, virt]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Used": item[0],
                    "Physical": item[1],
                    "Virtual": item[2]
                });
            });

        } else {

            // Spawns Resources chart data prep
            // {timestamp: [success, fail]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Success": item[0],
                    "Failure": item[1]
                });
            });

        }

        return result;
    },

    computeBarHeightPopover: function(d) {

        if (d.name === undefined) {
            d.name = 'Missing name param';
        }

        // if no y0 or y1 val, don't try to do math on undefined
        if (d.y0 === undefined || d.y1 === undefined) {
            return "
" + d.name + "
" + "No value reported";
        }

        // otherwise return a string in the format of
        // "
Success
10"

        // 'Failure/Success' is only used by Spawn viz
        if (d.name === 'Failure' || d.name === 'Success') {

            // VM Spawn chart should only return the exact
            // value represented by the individual rect
            return "
" + d.name + "
" + (d.y1 - d.y0);
        } else {

            // the other charts should return the value
            // represented by the top edge of the rect
            return "
" + d.name + "
" + d.y1;
        }
    },

    update: function() {

        var ns = this.defaults;
        var self = this;

        // data originally returned from collection as:
        // [{"1424586240000": [6, 16, 256]}...]
        var data = this.collection.toJSON();

        // data morphed through dataPrep into:
        // {
        //     "eventTime": "1424586240000",
        //     "Used": 6,
        //     "Physical": 16,
        //     "Virtual": 256
        // });
        data = this.dataPrep(data);

        this.hideSpinner();

        // if empty set, append info popup and stop
        if (this.checkReturnedDataSet(data) === false) {
            return;
        }

        // clear elements from previous render
        $(this.el).find('svg').find('rect').remove();
        $(this.el).find('svg').find('line').remove();
        $(this.el).find('svg').find('.axis').remove();
        $(this.el).find('svg').find('.legend').remove();
        $(this.el).find('svg').find('path').remove();
        $(this.el).find('svg').find('circle').remove();
        $(this.el + '.d3-tip').detach();

        // maps keys such as "Used / Physical / Virtual" to a color
        // but skips mapping "eventTime" to a color
        ns.color.domain(d3.keys(data[0]).filter(function(key) {
            return key !== "eventTime";
        }));

        /*
        forEach morphs data into:
        {
            "eventTime": "1424586240000",
            "Used": 6,
            "Physical": 16,
            "Virtual": 256,
            stackedBarPrep: [
                {
                    name: "Used",
                    y0: 0,
                    y1: 6
                },
                {
                    name: "Physical",
                    y0: 6,
                    y1: 22,
                },
                {
                    name: "Virtual",
                    y0: 22,
                    y1: 278,
                },
            ],
            total: 278
        });
        */

        data.forEach(function(d) {
            var y0 = 0;

            // calculates heights of each stacked bar by adding
            // to the heights of the previous bars
            d.stackedBarPrep = ns.color.domain().map(function(name) {
                return {
                    name: name,
                    y0: y0,
                    y1: y0 += +d[name]
                };
            });

            // this is the height of the last element, and used to
            // calculate the domain of the y-axis
            d.total = d.stackedBarPrep[d.stackedBarPrep.length - 1].y1;

            // or for the charts with paths, use the top line as the
            // total, which will inform that domain of the y-axis
            // d.Virtual and d.Total are the top lines on their
            // respective charts
            if (d.Virtual) {
                d.total = d.Virtual;
            }
            if (d.Total) {
                d.total = d.Total;
            }
        });

        // the forEach operation creates chaos in the order of the set
        // must _.sortBy to return it to an array sorted by eventTime
        data = _.sortBy(data, function(item) {
            return item.eventTime;
        });

        ns.x.domain(d3.extent(data, function(d) {
            return d.eventTime;
        }));

        ns.y.domain([0, d3.max(data, function(d) {
            return d.total;
        })]);

        ns.chart.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + ns.mh + ")")
            .call(ns.xAxis);

        ns.chart.append("g")
            .attr("class", "y axis")
            .call(ns.yAxis)
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .style("text-anchor", "end");

        ns.event = ns.chart.selectAll(".event")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g")
            .attr("transform", function(d) {
                return "translate(" + ns.x(d.eventTime) + ",0)";
            });

        ns.solidLineCanvas = ns.chart.selectAll(".event")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g")
            .attr("class", "solid-line-canvas");

        ns.dashedLineCanvas = ns.chart.selectAll(".event")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g")
            .attr("class", "dashed-line-canvas");

        var tip = d3.tip()
            .attr('class', 'd3-tip')
            .attr('id', this.el.slice(1))
            .html(function(d) {
                return self.computeBarHeightPopover(d);
            });

        // Invoke the tip in the context of your visualization
        ns.chart.call(tip);

        // used below to determing whether to render as
        // a "rect" or a "line"
        var showOrHide = {
            "Failure": true,
            "Success": true,
            "Virtual": false,
            "Physical": false,
            "Total": false,
            "Used": true
        };

        // append rectangles
        ns.event.selectAll("rect")
            .data(function(d) {
                return d.stackedBarPrep;
            })
            .enter().append("rect")
            .attr("width", function(d) {
                var segmentWidth = (ns.mw / data.length);

                // spacing corrected
                // for proportional gaps between rects
                return segmentWidth - segmentWidth * 0.07;
            })
            .attr("y", function(d) {
                return ns.y(d.y1);
            })
            .attr("height", function(d) {
                return ns.y(d.y0) - ns.y(d.y1);
            })
            .attr("rx", 0.8)
            .attr("stroke", function(d) {
                return ns.color(d.name);
            })
            .attr("stroke-opacity", function(d) {
                if (!showOrHide[d.name]) {
                    return 0;
                } else {
                    return 0.9;
                }
            })
            .attr("fill-opacity", function(d) {
                if (!showOrHide[d.name]) {
                    return 0;
                } else {
                    return 0.7;
                }
            })
            .attr("stroke-width", 2)
            .style("fill", function(d) {
                return ns.color(d.name);
            }).on('mouseenter', function(d, i) {

                // show popups for: spawn chart and cpu/disk/mem bars
                // but don't show popups for the invisible 'rect's
                var displayPopupKey = ['Used', 'Failure', 'Success'];
                if (displayPopupKey.indexOf(d.name) !== -1) {
                    var targ = d3.select(self.el).select('rect');
                    tip.offset([0, 0]).show(d, targ);
                }
            }).on('mouseleave', function() {
                tip.hide();
            });

        // abstracts the line generator to accept a data param
        // variable. will be used in the path generator
        var lineFunctionGenerator = function(param) {
            return d3.svg.line()
                .interpolate("linear")
                .x(function(d) {
                    return ns.x(d.eventTime);
                })
                .y(function(d) {
                    return ns.y(d[param]);
                });
        };

        // abstracts the path generator to accept a data param
        // and creates a solid line with the appropriate color
        var solidPathGenerator = function(param) {
            return ns.solidLineCanvas.append("path")
                .attr("d", lineFunction(data))
                .attr("stroke", function() {
                    return ns.color(param);
                })
                .attr("stroke-width", 2)
                .attr("fill", "none");
        };

        // abstracts the path generator to accept a data param
        // and creates a dashed line with the appropriate color
        var dashedPathGenerator = function(param) {
            return ns.dashedLineCanvas.append("path")
                .attr("d", lineFunction(data))
                .attr("stroke", function() {
                    return ns.color(param);
                })
                .attr("stroke-width", 2)
                .attr("fill", "none")
                .attr("stroke-dasharray", "5, 2");
        };

        // lineFunction must be a named localvariable as it will be called by
        // the pathGenerator function that immediately follows
        var lineFunction;
        if (ns.featureSet === 'cpu') {

            // generate solid line for Virtual data points
            lineFunction = lineFunctionGenerator('Virtual');
            solidPathGenerator('Virtual');

            // generate dashed line for Physical data points
            lineFunction = lineFunctionGenerator('Physical');
            dashedPathGenerator('Physical');

        } else if (ns.featureSet === 'disk') {

            // generate solid line for Total data points
            lineFunction = lineFunctionGenerator('Total');
            solidPathGenerator('Total');
        } else if (ns.featureSet === 'mem') {

            // generate solid line for Virtual data points
            lineFunction = lineFunctionGenerator('Virtual');
            solidPathGenerator('Virtual');

            // generate dashed line for Physical data points
            lineFunction = lineFunctionGenerator('Physical');
            dashedPathGenerator('Physical');
        }

        // appends chart legends
        var legendSpecs = {
            mem: [
                ['Virtual', 2],
                ['Physical', 1],
                ['Used', 0]
            ],
            cpu: [
                ['Virtual', 2],
                ['Physical', 1],
                ['Used', 0]
            ],
            disk: [
                ['Total', 1],
                ['Used', 0]
            ],
            spawn: [
                ['Fail', 1],
                ['Success', 0]
            ]
        };

        if (ns.featureSet !== null) {
            this.appendLegend(legendSpecs[ns.featureSet]);
        } else {
            this.appendLegend(legendSpecs.spawn);
        }
    },

    appendLegend: function(legendSpecs) {

        // abstracts the appending of chart legends based on the
        // passed in array params [['Title', colorSetIndex],['Title', colorSetIndex'],...]

        var ns = this.defaults;

        _.each(legendSpecs, function(item) {
            ns.chart.append('path')
                .attr('class', 'line')
                .attr('id', item[0])
                .attr('data-legend', item[0])
                .attr('data-legend-color', ns.color.range()[item[1]]);
        });

        var legend = ns.chart.append('g')
            .attr('class', 'legend')
            .attr('transform', 'translate(20,-35)')
            .attr('opacity', 0.7)
            .call(d3.legend);
    },

    template: _.template(
        '

'),

    render: function() {

        new ChartHeaderView({
            el: this.el,
            columns: 12,
            chartTitle: this.defaults.chartTitle,
            infoText: this.defaults.infoCustom
        });

        $(this.el).find('.mainContainer').append(this.template());
        return this;
    }

});
<commit_msg>Renders popovers for stackedBarChartViews in a stylistically similar fashion to api perf charts.<commit_after>/**
 * Copyright 2014 - 2015 Solinea, Inc.
 *
 * Licensed under the Solinea Software License Agreement (goldstone),
 * Version 1.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *     http://www.solinea.com/goldstone/LICENSE.pdf
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
View is currently directly implemented as Nova VM Spawns Viz
and extended into Nova CPU/Memory/Disk Resource Charts

instantiated similar to:

this.vmSpawnChart = new StackedBarChartCollection({
    urlPrefix: '/nova/hypervisor/spawns'
});

this.vmSpawnChartView = new StackedBarChartView({
    chartTitle: "VM Spawns",
    collection: this.vmSpawnChart,
    height: 300,
    infoCustom: 'novaSpawns',
    el: '#nova-report-r1-c2',
    width: $('#nova-report-r1-c2').width(),
    yAxisLabel: 'Spawn Events'
});
*/

// view is linked to collection when instantiated in api_perf_report.html

var StackedBarChartView = GoldstoneBaseView.extend({

    defaults: {
        margin: {
            top: 45,
            right: 40,
            bottom: 60,
            left: 70
        }
    },

    processOptions: function() {

        // this will invoke the processOptions method of the parent view,
        // and also add an additional param of featureSet which is used
        // to create a polymorphic interface for a variety of charts
        StackedBarChartView.__super__.processOptions.apply(this, arguments);

        this.defaults.featureSet = this.options.featureSet || null;
    },

    specialInit: function() {
        var ns = this.defaults;

        ns.yAxis = d3.svg.axis()
            .scale(ns.y)
            .orient("left")
            .tickFormat(d3.format("01d"));

        // differentiate color sets for mem and cpu charts
        if (ns.featureSet === 'mem' || ns.featureSet === 'cpu') {
            ns.color = d3.scale.ordinal().range(ns.colorArray.distinct[3]);
        } else {
            // this includes "VM Spawns" and "Disk Resources" chars
            ns.color = d3.scale.ordinal()
                .range(ns.colorArray.distinct[2]);
        }

    },

    dataPrep: function(data) {

        /*
        this is where the fetched JSON payload is transformed into a
        dataset than can be consumed by the D3 charts
        each chart may have its own perculiarities

        IMPORTANT:
        the order of items that are 'push'ed into the
        result array matters. After 'eventTime', the items
        will be stacked on the graph from the bottom of
        the graph upward. Or another way of saying it is
        the first item listed will be first one to be rendered
        from the x-axis of the graph going upward.
        */

        var ns = this.defaults;

        var result = [];

        if (ns.featureSet === 'cpu') {

            // CPU Resources chart data prep
            // {timestamp: [used, phys, virt]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Used": item[0],
                    "Physical": item[1],
                    "Virtual": item[2]
                });
            });

        } else if (ns.featureSet === 'disk') {

            // Disk Resources chart data prep
            // {timestamp: [used, total]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Used": item[0],
                    "Total": item[1]
                });
            });

        } else if (ns.featureSet === 'mem') {

            // Memory Resources chart data prep
            // {timestamp: [used, phys, virt]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Used": item[0],
                    "Physical": item[1],
                    "Virtual": item[2]
                });
            });

        } else {

            // Spawns Resources chart data prep
            // {timestamp: [success, fail]}
            _.each(data[0], function(item, i) {
                result.push({
                    "eventTime": "" + i,
                    "Success": item[0],
                    "Failure": item[1]
                });
            });

        }

        return result;
    },

    computeHiddenBarText: function(d) {
        var valuesToReport = _.filter((_.keys(d)), function(item) {
            return item !== "eventTime" && item !== "stackedBarPrep" && item !== "total";
        }).reverse();

        var result = "";

        result += moment().toDate(d.eventTime) + '
';

        valuesToReport.forEach(function(item) {
            result += item + ': ' + d[item] + '
';
        });

        return result;
    },

    update: function() {

        var ns = this.defaults;
        var self = this;

        // data originally returned from collection as:
        // [{"1424586240000": [6, 16, 256]}...]
        var data = this.collection.toJSON();

        // data morphed through dataPrep into:
        // {
        //     "eventTime": "1424586240000",
        //     "Used": 6,
        //     "Physical": 16,
        //     "Virtual": 256
        // });
        data = this.dataPrep(data);

        this.hideSpinner();

        // if empty set, append info popup and stop
        if (this.checkReturnedDataSet(data) === false) {
            return;
        }

        // clear elements from previous render
        $(this.el).find('svg').find('rect').remove();
        $(this.el).find('svg').find('line').remove();
        $(this.el).find('svg').find('.axis').remove();
        $(this.el).find('svg').find('.legend').remove();
        $(this.el).find('svg').find('path').remove();
        $(this.el).find('svg').find('circle').remove();
        $(this.el + '.d3-tip').detach();

        // maps keys such as "Used / Physical / Virtual" to a color
        // but skips mapping "eventTime" to a color
        ns.color.domain(d3.keys(data[0]).filter(function(key) {
            return key !== "eventTime";
        }));

        /*
        forEach morphs data into:
        {
            "eventTime": "1424586240000",
            "Used": 6,
            "Physical": 16,
            "Virtual": 256,
            stackedBarPrep: [
                {
                    name: "Used",
                    y0: 0,
                    y1: 6
                },
                {
                    name: "Physical",
                    y0: 6,
                    y1: 22,
                },
                {
                    name: "Virtual",
                    y0: 22,
                    y1: 278,
                },
            ],
            total: 278
        });
        */

        data.forEach(function(d) {
            var y0 = 0;

            // calculates heights of each stacked bar by adding
            // to the heights of the previous bars
            d.stackedBarPrep = ns.color.domain().map(function(name) {
                return {
                    name: name,
                    y0: y0,
                    y1: y0 += +d[name]
                };
            });

            // this is the height of the last element, and used to
            // calculate the domain of the y-axis
            d.total = d.stackedBarPrep[d.stackedBarPrep.length - 1].y1;

            // or for the charts with paths, use the top line as the
            // total, which will inform that domain of the y-axis
            // d.Virtual and d.Total are the top lines on their
            // respective charts
            if (d.Virtual) {
                d.total = d.Virtual;
            }
            if (d.Total) {
                d.total = d.Total;
            }
        });

        // the forEach operation creates chaos in the order of the set
        // must _.sortBy to return it to an array sorted by eventTime
        data = _.sortBy(data, function(item) {
            return item.eventTime;
        });

        ns.x.domain(d3.extent(data, function(d) {
            return d.eventTime;
        }));

        ns.y.domain([0, d3.max(data, function(d) {
            return d.total;
        })]);

        ns.chart.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + ns.mh + ")")
            .call(ns.xAxis);

        ns.chart.append("g")
            .attr("class", "y axis")
            .call(ns.yAxis)
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 6)
            .attr("dy", ".71em")
            .style("text-anchor", "end");

        ns.event = ns.chart.selectAll(".event")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g")
            .attr("transform", function(d) {
                return "translate(" + ns.x(d.eventTime) + ",0)";
            });

        ns.solidLineCanvas = ns.chart.selectAll(".event")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g")
            .attr("class", "solid-line-canvas");

        ns.dashedLineCanvas = ns.chart.selectAll(".event")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g")
            .attr("class", "dashed-line-canvas");

        ns.hiddenBarsCanvas = ns.chart.selectAll(".hidden")
            .data(data)
            .enter()
            .append("g")
            .attr("class", "g");

        var tip = d3.tip()
            .attr('class', 'd3-tip')
            .attr('id', this.el.slice(1))
            .html(function(d) {
                return self.computeHiddenBarText(d);
            });

        // Invoke the tip in the context of your visualization
        ns.chart.call(tip);

        // used below to determing whether to render as
        // a "rect" or a "line"
        var showOrHide = {
            "Failure": true,
            "Success": true,
            "Virtual": false,
            "Physical": false,
            "Total": false,
            "Used": true
        };

        // append rectangles
        ns.event.selectAll("rect")
            .data(function(d) {
                return d.stackedBarPrep;
            })
            .enter().append("rect")
            .attr("width", function(d) {
                var segmentWidth = (ns.mw / data.length);

                // spacing corrected
                // for proportional gaps between rects
                return segmentWidth - segmentWidth * 0.07;
            })
            .attr("y", function(d) {
                return ns.y(d.y1);
            })
            .attr("height", function(d) {
                return ns.y(d.y0) - ns.y(d.y1);
            })
            .attr("rx", 0.8)
            .attr("stroke", function(d) {
                return ns.color(d.name);
            })
            .attr("stroke-opacity", function(d) {
                if (!showOrHide[d.name]) {
                    return 0;
                } else {
                    return 0.9;
                }
            })
            .attr("fill-opacity", function(d) {
                if (!showOrHide[d.name]) {
                    return 0;
                } else {
                    return 0.7;
                }
            })
            .attr("stroke-width", 2)
            .style("fill", function(d) {
                return ns.color(d.name);
            });

        // append hidden bars
        ns.hiddenBarsCanvas.selectAll("rect")
            .data(data)
            .enter().append("rect")
            .attr("width", function(d) {
                var hiddenBarWidth = (ns.mw / data.length);
                return hiddenBarWidth - hiddenBarWidth * 0.07;
            })
            .attr("opacity", "0")
            .attr("x", function(d) {
                return ns.x(d.eventTime);
            })
            .attr("y", 0)
            .attr("height", function(d) {
                return ns.mh;
            }).on('mouseenter', function(d) {
                var targ = d3.select(self.el).select('rect');
                tip.offset([20, 0]).show(d, targ);
            }).on('mouseleave', function() {
                tip.hide();
            });

        // abstracts the line generator to accept a data param
        // variable. will be used in the path generator
        var lineFunctionGenerator = function(param) {
            return d3.svg.line()
                .interpolate("linear")
                .x(function(d) {
                    return ns.x(d.eventTime);
                })
                .y(function(d) {
                    return ns.y(d[param]);
                });
        };

        // abstracts the path generator to accept a data param
        // and creates a solid line with the appropriate color
        var solidPathGenerator = function(param) {
            return ns.solidLineCanvas.append("path")
                .attr("d", lineFunction(data))
                .attr("stroke", function() {
                    return ns.color(param);
                })
                .attr("stroke-width", 2)
                .attr("fill", "none");
        };

        // abstracts the path generator to accept a data param
        // and creates a dashed line with the appropriate color
        var dashedPathGenerator = function(param) {
            return ns.dashedLineCanvas.append("path")
                .attr("d", lineFunction(data))
                .attr("stroke", function() {
                    return ns.color(param);
                })
                .attr("stroke-width", 2)
                .attr("fill", "none")
                .attr("stroke-dasharray", "5, 2");
        };

        // lineFunction must be a named localvariable as it will be called by
        // the pathGenerator function that immediately follows
        var lineFunction;
        if (ns.featureSet === 'cpu') {

            // generate solid line for Virtual data points
            lineFunction = lineFunctionGenerator('Virtual');
            solidPathGenerator('Virtual');

            // generate dashed line for Physical data points
            lineFunction = lineFunctionGenerator('Physical');
            dashedPathGenerator('Physical');

        } else if (ns.featureSet === 'disk') {

            // generate solid line for Total data points
            lineFunction = lineFunctionGenerator('Total');
            solidPathGenerator('Total');
        } else if (ns.featureSet === 'mem') {

            // generate solid line for Virtual data points
            lineFunction = lineFunctionGenerator('Virtual');
            solidPathGenerator('Virtual');

            // generate dashed line for Physical data points
            lineFunction = lineFunctionGenerator('Physical');
            dashedPathGenerator('Physical');
        }

        // appends chart legends
        var legendSpecs = {
            mem: [
                ['Virtual', 2],
                ['Physical', 1],
                ['Used', 0]
            ],
            cpu: [
                ['Virtual', 2],
                ['Physical', 1],
                ['Used', 0]
            ],
            disk: [
                ['Total', 1],
                ['Used', 0]
            ],
            spawn: [
                ['Fail', 1],
                ['Success', 0]
            ]
        };

        if (ns.featureSet !== null) {
            this.appendLegend(legendSpecs[ns.featureSet]);
        } else {
            this.appendLegend(legendSpecs.spawn);
        }
    },

    appendLegend: function(legendSpecs) {

        // abstracts the appending of chart legends based on the
        // passed in array params [['Title', colorSetIndex],['Title', colorSetIndex'],...]

        var ns = this.defaults;

        _.each(legendSpecs, function(item) {
            ns.chart.append('path')
                .attr('class', 'line')
                .attr('id', item[0])
                .attr('data-legend', item[0])
                .attr('data-legend-color', ns.color.range()[item[1]]);
        });

        var legend = ns.chart.append('g')
            .attr('class', 'legend')
            .attr('transform', 'translate(20,-35)')
            .attr('opacity', 0.7)
            .call(d3.legend);
    },

    template: _.template(
        '

'),

    render: function() {

        new ChartHeaderView({
            el: this.el,
            columns: 12,
            chartTitle: this.defaults.chartTitle,
            infoText: this.defaults.infoCustom
        });

        $(this.el).find('.mainContainer').append(this.template());
        return this;
    }

});
<|endoftext|>


<commit_before>var _ = require('lodash'),
  Promise = require('bluebird');

module.exports = require('./component').extend({
  initActions: function() {
    _.forEach(this.actions || {}, this.initAction, this);
  },
  initAction: function(action, path) {
    if (!path.match(/^\//)) return; // actions start with a slash
    var app = this.express,
      actions = this.actions || {};
    path = ['', this.base].join('/') + path;
    this.logger.debug(path);
    var handlers = action.handler;
    if (!_.isArray(handlers)) handlers = [handlers];

    action.method = action.method || 'get';
    var methods = action.method;
    if (!_.isArray(methods)) methods = [methods];
    methods.forEach(function(method) {
      ['begin', 'end'].forEach(function(sAction) {
        if (!actions[sAction]) return;
        sHandlers = actions[sAction].handler;
        if (!_.isArray(sHandlers)) sHandlers = [sHandlers];
        if (actions[sAction].method.indexOf(method) !== -1)
          handlers[sAction === 'begin' ? 'unshift' : 'push'].apply(handlers, sHandlers);
      }, this);
      handlers = handlers.map(function(handler) {
        return function(req, res, next) {
          this.logger.debug('calling controller', path, ' with params', req.params);
          try {
            var ret = handler.apply(this, arguments);
            if (ret instanceof Promise) ret.then(next, function(e) {
              if (actions.catch) actions.catch.handler.call(this, req, res, e);
              if (actions.end) actions.end.handler.call(this, req, res);
            }.bind(this));
          } catch (e) {
            if (actions.catch) actions.catch.handler.call(this, req, res, e);
            if (actions.end) actions.end.handler.call(this, req, res);
          };
        }.bind(this);
      }, this);
      handlers.unshift(path);
      app[method].apply(app, handlers);
      handlers.shift();
    }, this);
  },
  initViews: function() {
    if (!this.views) return;
    this.views = this.initComponentWith(this.views, '../../view/');
  },
  initModels: function() {
    if (!this.models) return;
    this.models = this.initComponentWith(this.models, '../../model/');
  },
  getView: function(view) {
    if (!this.views[view]) this.throw('ViewNotFound', 'view "%s" doesn\'t exist', view);
    return this.views[view];
  },
  getModel: function(model) {
    if (!this.models[model]) this.throw('ModelNotFound', 'model "%s" doesn\'t exist', model);
    return this.models[model];
  },
  handleError: function(req, res, e) {
    res.status(500).send('an error occurred\n');
  },
}, {
  attributes: {
    actions: {
      required: true,
      initializer: 'initActions',
    },
    base: {
      required: true,
    },
    express: {
      required: true,
    },
    views: {
      initializer: 'initViews',
    },
    models: {
      initializer: 'initModels',
    },
  },
});
<commit_msg>allow strings as handlers<commit_after>var _ = require('lodash'),
  Promise = require('bluebird');

module.exports = require('./component').extend({
  initActions: function() {
    _.forEach(this.actions || {}, this.initAction, this);
  },
  initAction: function(action, path) {
    if (!path.match(/^\//)) return; // actions start with a slash
    var app = this.express,
      actions = this.actions || {};
    path = ['', this.base].join('/') + path;
    this.logger.debug(path);
    var handlers = action.handler;
    if (!_.isArray(handlers)) handlers = [handlers];

    action.method = action.method || 'get';
    var methods = action.method;
    if (!_.isArray(methods)) methods = [methods];
    methods.forEach(function(method) {
      ['begin', 'end'].forEach(function(sAction) {
        if (!actions[sAction]) return;
        sHandlers = actions[sAction].handler;
        if (!_.isArray(sHandlers)) sHandlers = [sHandlers];
        if (actions[sAction].method.indexOf(method) !== -1)
          handlers[sAction === 'begin' ? 'unshift' : 'push'].apply(handlers, sHandlers);
      }, this);
      handlers = handlers.map(function(handler) {
        return function(req, res, next) {
          this.logger.debug('calling controller', path, ' with params', req.params);
          try {
            var ret = _.isString(handler) ? this[handler].apply(this, arguments) : handler.apply(this, arguments);
            if (ret instanceof Promise) ret.then(next, function(e) {
              if (actions.catch) actions.catch.handler.call(this, req, res, e);
              if (actions.end) actions.end.handler.call(this, req, res);
            }.bind(this));
          } catch (e) {
            if (actions.catch) actions.catch.handler.call(this, req, res, e);
            if (actions.end) actions.end.handler.call(this, req, res);
          };
        }.bind(this);
      }, this);
      handlers.unshift(path);
      app[method].apply(app, handlers);
      handlers.shift();
    }, this);
  },
  initViews: function() {
    if (!this.views) return;
    this.views = this.initComponentWith(this.views, '../../view/');
  },
  initModels: function() {
    if (!this.models) return;
    this.models = this.initComponentWith(this.models, '../../model/');
  },
  getView: function(view) {
    if (!this.views[view]) this.throw('ViewNotFound', 'view "%s" doesn\'t exist', view);
    return this.views[view];
  },
  getModel: function(model) {
    if (!this.models[model]) this.throw('ModelNotFound', 'model "%s" doesn\'t exist', model);
    return this.models[model];
  },
  handleError: function(req, res, e) {
    res.status(500).send('an error occurred\n');
  },
}, {
  attributes: {
    actions: {
      required: true,
      initializer: 'initActions',
    },
    base: {
      required: true,
    },
    express: {
      required: true,
    },
    views: {
      initializer: 'initViews',
    },
    models: {
      initializer: 'initModels',
    },
  },
});
<|endoftext|>


<commit_before>import inquirer from 'inquirer'

import { Command } from '../../core'
import { ConfigurationFileNotExist, TargetFileAlreadyExist } from '../../shared/errors'
import { LogUtils } from '../../shared/utils'

import { DeployStopedByUser } from './deploy-stop-by-user.error'

export class DeployModuleCommand extends Command {
  get commandName() {
    return 'deploy [modules...]'
  }

  get alias() {
    return 'd'
  }

  get description() {
    return 'deploy configuration files.'
  }

  get options() {
    return [
      ['-l, --local', 'deploy only local files of the module(s) in the current folder']
    ]
  }

  constructor(fileService, deployService) {
    super()

    this.fileService = fileService
    this.deployService = deployService
  }

  run(modules, options) {
    const localDeployment = options.local || false
    let modulesToDeploy = modules

    Promise.resolve()
      .then(_ => {
        if (modules.length <= 0) {
          return inquirer.prompt([{
            type: 'confirm',
            message: 'No modules was passed in parameter. Do you want to deploy all available modules ?',
            name: 'all_module',
            default: false
          }])
            .then(({ all_module: deployAllModule }) => {
              if (!deployAllModule) {
                throw new DeployStopedByUser()
              }

              modulesToDeploy = this.fileService.modules.map(el => el.module)
            })
        }
      })
      .then(_ => LogUtils.log({ type: 'info', message: `Deployment (${modulesToDeploy.join(', ')}) start.` }))
      .then(_ => {
        const files = this.fileService.modules
          .filter(element => modulesToDeploy.includes(element.module))
          .reduce((files, element) => {
            for (const file of element.files) {
              files.push(file)
            }

            return files
          }, [])
          .filter(element => element.global === !localDeployment)

        const failedFiles = []
        const deployPromises = files.map(file => {
          if (file.global) {
            return this.deployService.deployGlobalFile(file)
          }

          return this.deployService.deployLocalFile(file)
            .catch(error => {
              if (error instanceof TargetFileAlreadyExist) {
                failedFiles.push(file)
                return
              }

              throw error
            })
        })

        return Promise.all(deployPromises)
          .then(_ => { // User choices to relaunch deployment
            if (failedFiles.length <= 0) {
              return
            }

            return failedFiles
              .reduce((queue, file) => queue
                .then(_ => inquirer
                  .prompt([{ name: 'overwrite_file', message: `File ${file.target} already exist. Do you want to overwrite it ?`, type: 'confirm', default: false }])
                  .then(({ overwrite_file: overwriteFile }) => {
                    if (overwriteFile) {
                      return this.fileService.deployLocalFile(file, true)
                    }
                  })), Promise.resolve())
          })
      })
      .then(_ => LogUtils.log({ type: 'success', message: `Deployment task is finished.` }))
      .catch(error => {
        if (error instanceof ConfigurationFileNotExist) {
          LogUtils.log({ type: 'error', message: 'No configuration file. You need to run the init command before.' })
          return
        }

        if (error instanceof DeployStopedByUser) {
          LogUtils.log({ message: `${error.message} No modules to deploy.` })
          return
        }

        LogUtils.log({ type: 'error', message: 'An error occured.', prefix: ' Fail ' })
      })
  }
}
<commit_msg>Refactor deploy command<commit_after>import inquirer from 'inquirer'

import { Command } from '../../core'
import { ConfigurationFileNotExist, TargetFileAlreadyExist } from '../../shared/errors'
import { LogUtils } from '../../shared/utils'

import { DeployStopedByUser } from './deploy-stop-by-user.error'

export class DeployModuleCommand extends Command {
  get commandName() {
    return 'deploy [modules...]'
  }

  get alias() {
    return 'd'
  }

  get description() {
    return 'deploy configuration files.'
  }

  get options() {
    return [
      ['-l, --local', 'deploy only local files of the module(s) in the current folder']
    ]
  }

  constructor(fileService, deployService) {
    super()

    this.fileService = fileService
    this.deployService = deployService
  }

  async run(modules, options) {
    const localDeployment = options.local || false
    let modulesToDeploy = modules

    try {
      modulesToDeploy = await this._getModuleListToDeploy(modules)
    } catch (error) {
      if (error instanceof ConfigurationFileNotExist) {
        LogUtils.log({ type: 'error', message: 'No configuration file. You need to run the init command before.' })
        return
      }

      if (error instanceof DeployStopedByUser) {
        LogUtils.log({ message: `${error.message} No modules to deploy.` })
        return
      }
    }

    LogUtils.log({ type: 'info', message: `Deployment (${modulesToDeploy.join(', ')}) start.` })
    await this._deployModules(modulesToDeploy, localDeployment)
    LogUtils.log({ type: 'success', message: `Deployment task is finished.` })
  }

  async _deployModules(modulesToDeploy, localDeployment) {
    const files = this.fileService.modules
      .filter(element => modulesToDeploy.includes(element.module))
      .reduce((files, element) => {
        for (const file of element.files) {
          files.push(file)
        }
        return files
      }, [])
      .filter(element => element.global === !localDeployment)

    const failedFiles = []
    for (const file of files) {
      try {
        if (file.global) {
          await this.deployService.deployGlobalFile(file)
          continue
        }

        await this.deployService.deployLocalFile(file)
      } catch (error) {
        if (error instanceof TargetFileAlreadyExist) {
          failedFiles.push(file)
          return
        }

        throw error
      }
    }

    for (const file of failedFiles) {
      const { overwrite_file: overwriteFile } = await inquirer.prompt([{
        name: 'overwrite_file',
        message: `File ${file.target} already exist. Do you want to overwrite it ?`,
        type: 'confirm',
        default: false
      }])

      if (overwriteFile) {
        await this.fileService.deployLocalFile(file, true)
      }
    }
  }

  async _getModuleListToDeploy(modules = []) {
    if (modules.length <= 0) {
      const { all_module: deployAllModule } = await inquirer.prompt([{
        type: 'confirm',
        message: 'No modules was passed in parameter. Do you want to deploy all available modules ?',
        name: 'all_module',
        default: false
      }])

      if (!deployAllModule) {
        throw new DeployStopedByUser()
      }

      return this.fileService.modules.map(el => el.module)
    }

    return modules
  }
}
<|endoftext|>


<commit_before>const fs = require('fs-extra');
const csv = require('fast-csv');
const dateFormat = require('dateformat');
const path = require('path');
const eol = require('os').EOL;
const sanitize = require('sanitize-filename');

module.exports = {
  defaultConfig: {
    enabled: false,
    logWipes: false
  },
  defaultConfigDetails: {
    logWipes: { label: 'Log Wipes' }
  },
  pluginName: 'RunLogger',
  pluginDescription: 'Creates a local csv file and saves data of every dungeon and scenario run in there.',
  temp: {},
  init(proxy, config) {
    proxy.on('apiCommand', (req, resp) => {
      if (config.Config.Plugins[this.pluginName].enabled) {
        const { command, wizard_id } = req;

        if (!this.temp[wizard_id])
          this.temp[wizard_id] = {};

        if (command === 'BattleScenarioStart')
          this.temp[wizard_id].stage = gMapping.scenario[req.region_id] ? `${gMapping.scenario[req.region_id]} ${gMapping.difficulty[req.difficulty]} - ${req.stage_no}` : 'Unknown';

        if (command === 'BattleScenarioResult' || command === 'BattleDungeonResult')
          this.log(proxy, req, resp);
      }
    });
  },
  getItem(crate) {
    if (crate.random_scroll && crate.random_scroll.item_master_id === 1)
      return `Unknown Scroll x${crate.random_scroll.item_quantity}`;
    if (crate.random_scroll && crate.random_scroll.item_master_id === 8)
      return `Summoning Stones x${crate.random_scroll.item_quantity}`;
    if (crate.random_scroll && crate.random_scroll.item_master_id === 2)
      return 'Mystical Scroll';
    if (crate.costume_point)
      return `Shapeshifting Stone x${crate.costume_point}`;
    if (crate.rune_upgrade_stone)
      return `Power Stone x${crate.rune_upgrade_stone.item_quantity}`;
    if (crate.unit_info)
      return `${gMapping.getMonsterName(crate.unit_info.unit_master_id)} ${crate.unit_info.class}`;
    if (crate.material) {
      let id = crate.material.item_master_id.toString();
      let attribute = Number(id.slice(-1));
      let grade = Number(id.slice(0, -4));
      return `Essence of ${gMapping.essence.attribute[attribute]}(${gMapping.essence.grade[grade]}) x${crate.material.item_quantity}`;
    }
    if (crate.craft_stuff && gMapping.craftMaterial[crate.craft_stuff.item_master_id])
      return `${gMapping.craftMaterial[crate.craft_stuff.item_master_id]} x${crate.craft_stuff.item_quantity}`;
    if (crate.summon_pieces)
      return `Summoning Piece ${gMapping.getMonsterName(crate.summon_pieces.item_master_id)} x${crate.summon_pieces.item_quantity}`;

    return 'Unknown Drop';
  },
  log(proxy ,req, resp) {
    const { command } = req;
    const { wizard_id, wizard_name } = resp.wizard_info;

    let entry = {};

    if (command === 'BattleDungeonResult') {
      if (gMapping.dungeon[req.dungeon_id]) {
        entry.dungeon = `${gMapping.dungeon[req.dungeon_id]} B${req.stage_id}`;
      } else {
        entry.dungeon = req.dungeon_id > 10000 ? 'Hall of Heroes' : 'Unknown';
      }
    }

    if (command === 'BattleScenarioResult')
      entry.dungeon = this.temp[wizard_id].stage ? this.temp[wizard_id].stage : 'Unknown';

    let winLost = resp.win_lose == 1 ? 'Win' : 'Lost';
    if (winLost === 'Lost' && !config.Config.Plugins[this.pluginName].logWipes)
      return;

    entry.date = dateFormat(new Date(), 'yyyy-mm-dd HH:MM');
    entry.result = winLost;

    let reward = resp.reward ? resp.reward : {};
    entry.mana = reward.mana ? reward.mana : 0;
    entry.energy = reward.energy ? reward.energy : 0;
    entry.crystal = reward.crystal ? reward.crystal : 0;

    let seconds = Math.floor(resp.clear_time.current_time / 1000 % 60) < 10 ? '0' + Math.floor(resp.clear_time.current_time / 1000 % 60) : Math.floor(resp.clear_time.current_time / 1000 % 60);
    let time = [Math.floor(resp.clear_time.current_time / 1000 / 60), seconds];
    entry.time = `${time[0]}:${time[1]}`;

    if (reward.crate) {
      entry.mana = reward.crate.mana ? entry.mana + reward.crate.mana : entry.mana;
      entry.energy = reward.crate.energy ? entry.energy + reward.crate.energy : entry.energy;
      entry.crystal = reward.crate.crystal ? entry.crystal + reward.crate.crystal : entry.crystal;

      if (reward.crate.rune) {
        let rune = reward.crate.rune;
        entry.drop = 'Rune';
        entry.grade = `${rune.class}*`;
        entry.sell_value = rune.sell_value;
        entry.set = gMapping.rune.sets[rune.set_id];
        entry.slot = rune.slot_no;
        entry.efficiency = gMapping.getRuneEfficiency(rune).current;
        entry.rarity = gMapping.rune.class[rune.sec_eff.length];
        entry.main_stat = gMapping.getRuneEffect(rune.pri_eff);
        entry.prefix_stat = gMapping.getRuneEffect(rune.prefix_eff);

        rune.sec_eff.forEach((substat, i) => {
          entry['sub' + (i + 1)] = gMapping.getRuneEffect(substat);
        });
      } else {
        entry.drop = this.getItem(reward.crate);
      }
    }

    if (resp.unit_list && resp.unit_list.length > 0) {
      resp.unit_list.forEach((unit, i) => {
        entry['team' + (i + 1)] = gMapping.getMonsterName(unit.unit_master_id);
      });
    }

    if (resp.instance_info)
      entry.drop = 'Secret Dungeon';

    let csvData = [];
    const headers = ['date', 'dungeon', 'result', 'time', 'mana', 'crystal', 'energy', 'drop', 'grade', 'sell_value',
      'set', 'efficiency', 'slot', 'rarity', 'main_stat', 'prefix_stat', 'sub1', 'sub2', 'sub3', 'sub4', 'team1', 'team2', 'team3', 'team4', 'team5'];

    const filename = sanitize(`${wizard_name}-${wizard_id}-runs.csv`);
    const self = this;

    fs.ensureFile(path.join(config.Config.App.filesPath, filename), err => {
      csv.fromPath(path.join(config.Config.App.filesPath, filename), { ignoreEmpty: true, headers: headers, renameHeaders: true }).on('data', function (data) {
        csvData.push(data);
      }).on('end', function () {
        csvData.push(entry);
        csv.writeToPath(path.join(config.Config.App.filesPath, filename), csvData, { headers: headers }).on("finish", function () {
          proxy.log({ type: 'success', source: 'plugin', name: self.pluginName, message: `Saved run data to ${filename}` });
        });
      });
    })

  }
}
<commit_msg>RunLogger: fixed error with essence grades<commit_after>const fs = require('fs-extra');
const csv = require('fast-csv');
const dateFormat = require('dateformat');
const path = require('path');
const eol = require('os').EOL;
const sanitize = require('sanitize-filename');

module.exports = {
  defaultConfig: {
    enabled: false,
    logWipes: false
  },
  defaultConfigDetails: {
    logWipes: { label: 'Log Wipes' }
  },
  pluginName: 'RunLogger',
  pluginDescription: 'Creates a local csv file and saves data of every dungeon and scenario run in there.',
  temp: {},
  init(proxy, config) {
    proxy.on('apiCommand', (req, resp) => {
      if (config.Config.Plugins[this.pluginName].enabled) {
        const { command, wizard_id } = req;

        if (!this.temp[wizard_id])
          this.temp[wizard_id] = {};

        if (command === 'BattleScenarioStart')
          this.temp[wizard_id].stage = gMapping.scenario[req.region_id] ? `${gMapping.scenario[req.region_id]} ${gMapping.difficulty[req.difficulty]} - ${req.stage_no}` : 'Unknown';

        if (command === 'BattleScenarioResult' || command === 'BattleDungeonResult')
          this.log(proxy, req, resp);
      }
    });
  },
  getItem(crate) {
    if (crate.random_scroll && crate.random_scroll.item_master_id === 1)
      return `Unknown Scroll x${crate.random_scroll.item_quantity}`;
    if (crate.random_scroll && crate.random_scroll.item_master_id === 8)
      return `Summoning Stones x${crate.random_scroll.item_quantity}`;
    if (crate.random_scroll && crate.random_scroll.item_master_id === 2)
      return 'Mystical Scroll';
    if (crate.costume_point)
      return `Shapeshifting Stone x${crate.costume_point}`;
    if (crate.rune_upgrade_stone)
      return `Power Stone x${crate.rune_upgrade_stone.item_quantity}`;
    if (crate.unit_info)
      return `${gMapping.getMonsterName(crate.unit_info.unit_master_id)} ${crate.unit_info.class}`;
    if (crate.material) {
      let id = crate.material.item_master_id.toString();
      let attribute = Number(id.slice(-1));
      let grade = Number(id.slice(1, -3));
      return `Essence of ${gMapping.essence.attribute[attribute]}(${gMapping.essence.grade[grade]}) x${crate.material.item_quantity}`;
    }
    if (crate.craft_stuff && gMapping.craftMaterial[crate.craft_stuff.item_master_id])
      return `${gMapping.craftMaterial[crate.craft_stuff.item_master_id]} x${crate.craft_stuff.item_quantity}`;
    if (crate.summon_pieces)
      return `Summoning Piece ${gMapping.getMonsterName(crate.summon_pieces.item_master_id)} x${crate.summon_pieces.item_quantity}`;

    return 'Unknown Drop';
  },
  log(proxy ,req, resp) {
    const { command } = req;
    const { wizard_id, wizard_name } = resp.wizard_info;

    let entry = {};

    if (command === 'BattleDungeonResult') {
      if (gMapping.dungeon[req.dungeon_id]) {
        entry.dungeon = `${gMapping.dungeon[req.dungeon_id]} B${req.stage_id}`;
      } else {
        entry.dungeon = req.dungeon_id > 10000 ? 'Hall of Heroes' : 'Unknown';
      }
    }

    if (command === 'BattleScenarioResult')
      entry.dungeon = this.temp[wizard_id].stage ? this.temp[wizard_id].stage : 'Unknown';

    let winLost = resp.win_lose == 1 ? 'Win' : 'Lost';
    if (winLost === 'Lost' && !config.Config.Plugins[this.pluginName].logWipes)
      return;

    entry.date = dateFormat(new Date(), 'yyyy-mm-dd HH:MM');
    entry.result = winLost;

    let reward = resp.reward ? resp.reward : {};
    entry.mana = reward.mana ? reward.mana : 0;
    entry.energy = reward.energy ? reward.energy : 0;
    entry.crystal = reward.crystal ? reward.crystal : 0;

    let seconds = Math.floor(resp.clear_time.current_time / 1000 % 60) < 10 ? '0' + Math.floor(resp.clear_time.current_time / 1000 % 60) : Math.floor(resp.clear_time.current_time / 1000 % 60);
    let time = [Math.floor(resp.clear_time.current_time / 1000 / 60), seconds];
    entry.time = `${time[0]}:${time[1]}`;

    if (reward.crate) {
      entry.mana = reward.crate.mana ? entry.mana + reward.crate.mana : entry.mana;
      entry.energy = reward.crate.energy ? entry.energy + reward.crate.energy : entry.energy;
      entry.crystal = reward.crate.crystal ? entry.crystal + reward.crate.crystal : entry.crystal;

      if (reward.crate.rune) {
        let rune = reward.crate.rune;
        entry.drop = 'Rune';
        entry.grade = `${rune.class}*`;
        entry.sell_value = rune.sell_value;
        entry.set = gMapping.rune.sets[rune.set_id];
        entry.slot = rune.slot_no;
        entry.efficiency = gMapping.getRuneEfficiency(rune).current;
        entry.rarity = gMapping.rune.class[rune.sec_eff.length];
        entry.main_stat = gMapping.getRuneEffect(rune.pri_eff);
        entry.prefix_stat = gMapping.getRuneEffect(rune.prefix_eff);

        rune.sec_eff.forEach((substat, i) => {
          entry['sub' + (i + 1)] = gMapping.getRuneEffect(substat);
        });
      } else {
        entry.drop = this.getItem(reward.crate);
      }
    }

    if (resp.unit_list && resp.unit_list.length > 0) {
      resp.unit_list.forEach((unit, i) => {
        entry['team' + (i + 1)] = gMapping.getMonsterName(unit.unit_master_id);
      });
    }

    if (resp.instance_info)
      entry.drop = 'Secret Dungeon';

    let csvData = [];
    const headers = ['date', 'dungeon', 'result', 'time', 'mana', 'crystal', 'energy', 'drop', 'grade', 'sell_value',
      'set', 'efficiency', 'slot', 'rarity', 'main_stat', 'prefix_stat', 'sub1', 'sub2', 'sub3', 'sub4', 'team1', 'team2', 'team3', 'team4', 'team5'];

    const filename = sanitize(`${wizard_name}-${wizard_id}-runs.csv`);
    const self = this;

    fs.ensureFile(path.join(config.Config.App.filesPath, filename), err => {
      csv.fromPath(path.join(config.Config.App.filesPath, filename), { ignoreEmpty: true, headers: headers, renameHeaders: true }).on('data', function (data) {
        csvData.push(data);
      }).on('end', function () {
        csvData.push(entry);
        csv.writeToPath(path.join(config.Config.App.filesPath, filename), csvData, { headers: headers }).on("finish", function () {
          proxy.log({ type: 'success', source: 'plugin', name: self.pluginName, message: `Saved run data to ${filename}` });
        });
      });
    })

  }
}
<|endoftext|>


<commit_before>// scripts app
var app;
var ancho = $(window).width();

var isMobile = { //valida si es un dispositivo movil
    Android: function() {
        return navigator.userAgent.match(/Android/i);
    },
    BlackBerry: function() {
        return navigator.userAgent.match(/BlackBerry/i);
    },
    iOS: function() {
        return navigator.userAgent.match(/iPhone|iPad|iPod/i);
    },
    Opera: function() {
        return navigator.userAgent.match(/Opera Mini/i);
    },
    Windows: function() {
        return navigator.userAgent.match(/IEMobile/i);
    },
    any: function() {
        return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
    }
};

$(function(){
	"use strict";
	// load mapa
	$.get("img/caba_ilus.txt", function(mapa){
		$("#mapa_cont").html(mapa + '
Clickeá en las comunas para ver los resultados en detalle.
');

		tooltip();

		//app = new ElecionesApp();
		//console.log(app);

	});

	if(ancho < 750 && ancho > 640){
		$(".compartir").show();
	}else{
		$(".compartir").hide();
	}

	 /* // scroll // */

	  $("#list, #list_interna").niceScroll({
	        cursorcolor:"#d7d7d7",
	        cursorborder:"0px solid #fff",
	        cursorwidth: "7px",
	        autohidemode:false,
	        hidecursordelay:0
	  });

		/*compartir*/
		function myPopup(url) {
	   	 	window.open( url, "Compartir", "status = yes, height = 360, width = 500, resizable = yes, left = "+(ancho/2+250)+", top =" +(alto/2-150) );
	    	return false;
		}

});

	/* // tooltip /*/
function tooltip(){

	  	var comu = $("polygon, path");
	  	var tool = $(".tooltip")

        comu.on( 'mouseenter', function() {
          ide = $(this).attr("id");
          console.log(ide);
        });

        if(isMobile.any()) {

        }else{
          comu.hover(function() {
            $('.tooltip').show();
          },function(){
            $('.tooltip').hide();
          });
        }

        comu.on('mousemove', function(e){

         e = e || window.event;
         e = jQuery.event.fix(e);

         if(e.pageX > 185 && ancho < 750){
           var itemX = e.pageX - 200;
         }else if(e.pageX < 330){
           var itemX = e.pageX;
         }else{
         	var itemX = e.pageX - 200;
         }

         if(e.pageY > 450){
           var itemY = e.pageY - 260;
         }else{
           var itemY = e.pageY + 30;
         }

         $(".tooltip").animate({"top":itemY, "left":itemX},100, 'swing').stop( true, true );

        });
}
<commit_msg>varios<commit_after>// scripts app
var app;

var ancho = $(window).width();
var isMobile = { //valida si es un dispositivo movil
    Android: function() {
        return navigator.userAgent.match(/Android/i);
    },
    BlackBerry: function() {
        return navigator.userAgent.match(/BlackBerry/i);
    },
    iOS: function() {
        return navigator.userAgent.match(/iPhone|iPad|iPod/i);
    },
    Opera: function() {
        return navigator.userAgent.match(/Opera Mini/i);
    },
    Windows: function() {
        return navigator.userAgent.match(/IEMobile/i);
    },
    any: function() {
        return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
    }
};

$(function(){
	"use strict";
	// load mapa
	$.get("img/caba_ilus.txt", function(mapa){
		$("#mapa_cont").html(mapa + '
Clickeá en las comunas para ver los resultados en detalle.
');

		// tooltip();

		app = new ElecionesApp();
		//console.log(app);

	});

	if(ancho < 750 && ancho > 640){
		$(".compartir").show();
	}else{
		$(".compartir").hide();
	}

	 /* // scroll // */

	  $("#list, #list_interna").niceScroll({
	        cursorcolor:"#d7d7d7",
	        cursorborder:"0px solid #fff",
	        cursorwidth: "7px",
	        autohidemode:false,
	        hidecursordelay:0
	  });

		/*compartir*/
		function myPopup(url) {
	   	 	window.open( url, "Compartir", "status = yes, height = 360, width = 500, resizable = yes, left = "+(ancho/2+250)+", top =" +(alto/2-150) );
	    	return false;
		}

});

	/* // tooltip /*/
function tooltip(){

	  	var comu = $("polygon, path");
	  	var tool = $(".tooltip")

        comu.on( 'mouseenter', function() {
          ide = $(this).attr("id");
          console.log(ide);
        });

        if(isMobile.any()) {

        }else{
          comu.hover(function() {
            $('.tooltip').show();
          },function(){
            $('.tooltip').hide();
          });
        }

        comu.on('mousemove', function(e){

         e = e || window.event;
         e = jQuery.event.fix(e);

         if(e.pageX > 185 && ancho < 750){
           var itemX = e.pageX - 200;
         }else if(e.pageX < 330){
           var itemX = e.pageX;
         }else{
         	var itemX = e.pageX - 200;
         }

         if(e.pageY > 450){
           var itemY = e.pageY - 260;
         }else{
           var itemY = e.pageY + 30;
         }

         $(".tooltip").animate({"top":itemY, "left":itemX},100, 'swing').stop( true, true );

        });
}
<|endoftext|>