<commit_before>// constants
var stopCircleColor = 'blue'; // map stop circle color
var stopFillColor = 'blue'; // map stop circle fill color
var setViewLat = 44.40;
var setViewLon = 26.10;
var setViewZoom = 13;
var mapid = 'mapid';

function getRefs(roleName, relation){
    var refList = [];
    var selector = '[role=' + roleName + ']';
    var objList = relation.querySelectorAll(selector);
    for(var p = 0; p < objList.length; p++){
        refList.push(objList[p].getAttribute("ref"));
    }
    return refList;
}

function getCoordinates(ref, xmlDoc, rand = [0,0]){
    var selector = '[id="' + ref + '"]';
    var node = xmlDoc.querySelectorAll(selector)[0];
    // if ref (usually a platform) is a way or area instead of a node
    if (node.tagName == 'way'){
        var firstNodeRef = node.getElementsByTagName('nd')[0].getAttribute("ref");
        selector = '[id="' + firstNodeRef + '"]';
        node = xmlDoc.querySelectorAll(selector)[0];
    }
    var lat = parseFloat(node.getAttribute("lat"));
    var lon = parseFloat(node.getAttribute("lon"));
    return [lat + rand[0], lon + rand[1]];
}

function getArrayCoordinates(array, xmlDoc, rand){
    var res = [];
    for(var i=0; i<array.length; i++){
        res.push(getCoordinates(array[i], xmlDoc, rand));
    }
    return res;
}

function segmentReferences(waypointReferences, stopReferences){
    var left = 0;
    var right = 0;
    var started = false;
    var counter =  0;
    var segments = [];

    for(var p = 0; p < waypointReferences.length; p++){
        // try to separate segments between stops
        if(waypointReferences[p] === stopReferences[counter]){
            counter ++;
            if(started){
                right = p;
                segments.push(waypointReferences.slice(left, right +1));
                left = p; // starting new segment
            } else {
                left = p;
                started = true;
            }
        }
    }
    return segments;
}

function displayPlatforms(names, coordinates, stopsTimes, myMap){
    // display platforms
    var markers = [];
    var circle;
    for(var p = 0; p < names.length; p++){
        var stopTime = stopsTimes[p];
        if (stopTime === undefined){
            stopTime = 16;
        }
        circle = L.circle([coordinates[p][0], coordinates[p][1]], {
            color: stopCircleColor,
            weight: 2,
            fillColor: stopFillColor,
            fillOpacity: 0.1,
            radius: 15*Math.max(1.5, Math.sqrt(stopTime))
        }).addTo(myMap)
            .bindPopup(names[p]);
    markers.push(circle);
    }
    return markers;
}

Number.prototype.toRad = function() {
   return this * Math.PI / 180;
};

function twoPointLength(lat1, lon1, lat2, lon2){
	// http://stackoverflow.com/questions/14560999/using-the-haversine-formula-in-javascript
	var R = 6371; // km
	//has a problem with the .toRad() method below.
	var x1 = lat2-lat1;
	var dLat = x1.toRad();
	var x2 = lon2-lon1;
	var dLon = x2.toRad();
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	                Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
	                Math.sin(dLon/2) * Math.sin(dLon/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	return R * c;
}

function segmentLength(segmentArray, xmlDoc){
	var segmLength = 0;
	var startPoint = [];
	var endPoint = [];
	for(var k = 0; k < segmentArray.length - 1; k++){
		startPoint = getCoordinates(segmentArray[k], xmlDoc);
		endPoint = getCoordinates(segmentArray[k+1], xmlDoc);
		segmLength += twoPointLength(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
	}
	return segmLength;
}

function joinWays(relation, xmlDoc){
        // get ways and display route
    var wayReferences = [];
    var waypointReferences = [];
    var ways = relation.querySelectorAll('[role=""]'); // the ways in the route have no role
    // get way refs
    for(p = 0; p < ways.length; p++){
        wayReferences.push(ways[p].getAttribute("ref"));
    }
    // for each way, get point and add to route
    for(var p = 0; p < wayReferences.length; p++){
        var selector = '[id="' + wayReferences[p] + '"]';
        var way = xmlDoc.querySelectorAll(selector)[0];
        var wayPoints = way.getElementsByTagName("nd");
        var currentPoints = [];
        for (var w = 0; w < wayPoints.length; w ++){
            var ref = wayPoints[w].getAttribute("ref");
            currentPoints.push(ref);
        }
        // first segment
        if(p === 0){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // last route point === first point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[0]){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // last route point === last point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }
        // first route point === first point  of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[0]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // first route point === last point of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }
        console.log("ERROR: WAYS NOT CONNECTED!");
        alert("ERROR: WAYS NOT CONNECTED!");

    }
    return waypointReferences;
}

function processRelation(xmlDoc){
    var names = [];
    var stopsLengths = [];

    // extract relation element
    var relation = xmlDoc.getElementsByTagName("relation")[0];
    // get platform refs and stop position refs
    var middle_platformReferences = getRefs('platform', relation);
    var platform_entry_onlyReferences = getRefs('platform_entry_only', relation);
    var platform_exit_only_References = getRefs('platform_exit_only', relation);
    var platformReferences = platform_entry_onlyReferences.concat(
        middle_platformReferences, platform_exit_only_References);
    var middle_stopReferences = getRefs('stop', relation);
    var stop_entry_onlyReferences = getRefs('stop_entry_only', relation);
    var stop_exit_only_References = getRefs('stop_exit_only', relation);
    var stopReferences = stop_entry_onlyReferences.concat(
        middle_stopReferences, stop_exit_only_References);

    // join ways to create full route
    var waypointReferences = joinWays(relation, xmlDoc);
    // create separate route segments
    var segments = segmentReferences(waypointReferences, stopReferences);
    // calculate segment lengths and total distance
    var segment;
    for(var i = 0; i< segments.length; i++){
        segment = segmentLength(segments[i], xmlDoc);
        stopsLengths.push(segment);
    }
    // get platform names and coordinates
    var platformCoordinates = [];
    for(var p = 0; p < platformReferences.length; p++){
        platformCoordinates.push(getCoordinates(platformReferences[p], xmlDoc));
        var selector = '[id="' + platformReferences[p].toString() + '"]';
        var platform = xmlDoc.querySelectorAll(selector)[0];
        names.push(platform.querySelectorAll('[k="name"]')[0].getAttribute("v"));
    }

    return {
        names: names,
        segments: segments,
        platformCoordinates: platformCoordinates,
        stopsLengths: stopsLengths
    };
}

function addRouteToMap(rel, res, L, xmlDoc, myMap){
    // add route segments to map

    //slightly randomize position of routes to avoid complete overlap
    //var rand_lat = (2*Math.random()-1)/2000
    //var rand_lon = (2*Math.random()-1)/2000
    var rand_lat = 0;
    var rand_lon = 0;

    for(i=0; i<rel.segments.length; i++){
        L.polyline(getArrayCoordinates(rel.segments[i], xmlDoc, rand = [rand_lat, rand_lon]),
            {color: res.segmentColors[i], weight: 5}).addTo(myMap);
    }
    // add platforms to map and return markers to fit bounds
    return displayPlatforms(rel.names, rel.platformCoordinates, res.stopsTimes, myMap);
}

function baseMap(L){
    var myMap = L.map(mapid).setView([setViewLat, setViewLon], setViewZoom);
    L.tileLayer('http://{s}.tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: '© OpenStreetMap'
    }).addTo(myMap);
    // L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    //     attribution: '© OpenStreetMap contributors'
    // }).addTo(myMap);
    return myMap;
}

function addMultipleRoutes(relList, relColors){
    var myMap = baseMap(L);
    var relation, xhttp;
    for(var i=0; i<relList.length; i++){
        // magic: http://stackoverflow.com/questions/6077357/passing-index-from-for-loop-to-ajax-callback-function-javascript
        (function(i) {
            routeID = relList[i];
            var overpass1 = 'http://overpass-api.de/api/interpreter?data=relation(';
            var overpass2 =');out body;>;out body;rel(bn)["public_transport"="stop_area"];out body;';
            var overpass_full = overpass1 + routeID + overpass2;
            xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    var xmlDoc = this.responseXML;
                    var rel = processRelation(xmlDoc);
                    var lengths = rel.names.length;
                    var res = {};
                    res.segmentColors = Array(lengths).fill(relColors[i]);
                    res.stopsTimes = Array(lengths).fill(30);
                    var markers = addRouteToMap(rel, res, L, xmlDoc, myMap);
                }
            };
            xhttp.open("GET", overpass_full, true);
            xhttp.send();
        })(i);
    }
}<commit_msg>roundabouts<commit_after>// constants
var stopCircleColor = 'blue'; // map stop circle color
var stopFillColor = 'blue'; // map stop circle fill color
var setViewLat = 44.40;
var setViewLon = 26.10;
var setViewZoom = 13;
var mapid = 'mapid';

function getRefs(roleName, relation){
    var refList = [];
    var selector = '[role=' + roleName + ']';
    var objList = relation.querySelectorAll(selector);
    for(var p = 0; p < objList.length; p++){
        refList.push(objList[p].getAttribute("ref"));
    }
    return refList;
}

function getCoordinates(ref, xmlDoc, rand = [0,0]){
    var selector = '[id="' + ref + '"]';
    var node = xmlDoc.querySelectorAll(selector)[0];
    // if ref (usually a platform) is a way or area instead of a node
    if (node.tagName == 'way'){
        var firstNodeRef = node.getElementsByTagName('nd')[0].getAttribute("ref");
        selector = '[id="' + firstNodeRef + '"]';
        node = xmlDoc.querySelectorAll(selector)[0];
    }
    var lat = parseFloat(node.getAttribute("lat"));
    var lon = parseFloat(node.getAttribute("lon"));
    return [lat + rand[0], lon + rand[1]];
}

function getArrayCoordinates(array, xmlDoc, rand){
    var res = [];
    for(var i=0; i<array.length; i++){
        res.push(getCoordinates(array[i], xmlDoc, rand));
    }
    return res;
}

function segmentReferences(waypointReferences, stopReferences){
    var left = 0;
    var right = 0;
    var started = false;
    var counter =  0;
    var segments = [];

    for(var p = 0; p < waypointReferences.length; p++){
        // try to separate segments between stops
        if(waypointReferences[p] === stopReferences[counter]){
            counter ++;
            if(started){
                right = p;
                segments.push(waypointReferences.slice(left, right +1));
                left = p; // starting new segment
            } else {
                left = p;
                started = true;
            }
        }
    }
    return segments;
}

function displayPlatforms(names, coordinates, stopsTimes, myMap){
    // display platforms
    var markers = [];
    var circle;
    for(var p = 0; p < names.length; p++){
        var stopTime = stopsTimes[p];
        if (stopTime === undefined){
            stopTime = 16;
        }
        circle = L.circle([coordinates[p][0], coordinates[p][1]], {
            color: stopCircleColor,
            weight: 2,
            fillColor: stopFillColor,
            fillOpacity: 0.1,
            radius: 15*Math.max(1.5, Math.sqrt(stopTime))
        }).addTo(myMap)
            .bindPopup(names[p]);
    markers.push(circle);
    }
    return markers;
}

Number.prototype.toRad = function() {
   return this * Math.PI / 180;
};

function twoPointLength(lat1, lon1, lat2, lon2){
	// http://stackoverflow.com/questions/14560999/using-the-haversine-formula-in-javascript
	var R = 6371; // km
	//has a problem with the .toRad() method below.
	var x1 = lat2-lat1;
	var dLat = x1.toRad();
	var x2 = lon2-lon1;
	var dLon = x2.toRad();
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
	                Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
	                Math.sin(dLon/2) * Math.sin(dLon/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	return R * c;
}

function segmentLength(segmentArray, xmlDoc){
	var segmLength = 0;
	var startPoint = [];
	var endPoint = [];
	for(var k = 0; k < segmentArray.length - 1; k++){
		startPoint = getCoordinates(segmentArray[k], xmlDoc);
		endPoint = getCoordinates(segmentArray[k+1], xmlDoc);
		segmLength += twoPointLength(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
	}
	return segmLength;
}

function joinWays(relation, xmlDoc){
        // get ways and display route
    var wayReferences = [];
    var waypointReferences = [];
    var ways = relation.querySelectorAll('[role=""]'); // the ways in the route have no role
    // get way refs
    for(p = 0; p < ways.length; p++){
        wayReferences.push(ways[p].getAttribute("ref"));
    }
    // for each way, get point and add to route
    for(var p = 0; p < wayReferences.length; p++){
        var selector = '[id="' + wayReferences[p] + '"]';
        var way = xmlDoc.querySelectorAll(selector)[0];
        var wayPoints = way.getElementsByTagName("nd");
        var currentPoints = [];
        for (var w = 0; w < wayPoints.length; w ++){
            var ref = wayPoints[w].getAttribute("ref");
            currentPoints.push(ref);
        }
        // first segment
        if(p === 0){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // last route point === first point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[0]){
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // last route point === last point of current segment
        if(waypointReferences[waypointReferences.length-1] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }
        // first route point === first point  of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[0]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints);
            continue;
        }
        // first route point === last point of current segment
        // it can happen only immediately after first segment
        if(waypointReferences[0] === currentPoints[currentPoints.length-1]){
            waypointReferences = waypointReferences.reverse();
            waypointReferences = waypointReferences.concat(currentPoints.reverse());
            continue;
        }

        // test for roundabouts
        // assumptions:
        //roundabout not last segment and no two roundabouts in a row;
        //stop not in roundabout
        var round_select = '[v="roundabout"]';
        if(way.querySelectorAll(round_select).length > 0){
            var next_selector = '[id="' + wayReferences[p+1] + '"]';
            var next_way = xmlDoc.querySelectorAll(next_selector)[0];
            // get the points of the next way
            var next_wayPoints = next_way.getElementsByTagName("nd");
            var nextPoints = [];
            for (var w = 0; w < next_wayPoints.length; w ++){
                var ref = next_wayPoints[w].getAttribute("ref");
                nextPoints.push(ref);
            }
            // find roundabout point in the last segment
            var prev = false;
            var found_it = false;
            for (var i = 0; i < currentPoints.length; i++) {
                if (currentPoints[i] === waypointReferences[waypointReferences.length-1]) {
                    prev = true;
                    break;
                }
                if (currentPoints[i] === waypointReferences[0]){
                    prev = true;
                    waypointReferences = waypointReferences.reverse();
                    break;
                }
            }
            // find roundabout point in next segment
            for (var i = 0; i < currentPoints.length; i++) {
                if (currentPoints[i] === nextPoints[0] ||
                    currentPoints[i] === nextPoints[nextPoints.length-1]){
                    found_it = true;
                    if (prev){
                        waypointReferences = waypointReferences.concat([currentPoints[i]]);
                    }
                    break;
                }
            }
        }
        if (found_it){
            continue;
        }
        console.log("ERROR: WAYS NOT CONNECTED!");
    }
    return waypointReferences;
}

function processRelation(xmlDoc){
    var names = [];
    var stopsLengths = [];

    // extract relation element
    var relation = xmlDoc.getElementsByTagName("relation")[0];
    // get platform refs and stop position refs
    var middle_platformReferences = getRefs('platform', relation);
    var platform_entry_onlyReferences = getRefs('platform_entry_only', relation);
    var platform_exit_only_References = getRefs('platform_exit_only', relation);
    var platformReferences = platform_entry_onlyReferences.concat(
        middle_platformReferences, platform_exit_only_References);
    var middle_stopReferences = getRefs('stop', relation);
    var stop_entry_onlyReferences = getRefs('stop_entry_only', relation);
    var stop_exit_only_References = getRefs('stop_exit_only', relation);
    var stopReferences = stop_entry_onlyReferences.concat(
        middle_stopReferences, stop_exit_only_References);

    // join ways to create full route
    var waypointReferences = joinWays(relation, xmlDoc);
    // create separate route segments
    var segments = segmentReferences(waypointReferences, stopReferences);
    // calculate segment lengths and total distance
    var segment;
    for(var i = 0; i< segments.length; i++){
        segment = segmentLength(segments[i], xmlDoc);
        stopsLengths.push(segment);
    }
    // get platform names and coordinates
    var platformCoordinates = [];
    for(var p = 0; p < platformReferences.length; p++){
        platformCoordinates.push(getCoordinates(platformReferences[p], xmlDoc));
        var selector = '[id="' + platformReferences[p].toString() + '"]';
        var platform = xmlDoc.querySelectorAll(selector)[0];
        names.push(platform.querySelectorAll('[k="name"]')[0].getAttribute("v"));
    }

    return {
        names: names,
        segments: segments,
        platformCoordinates: platformCoordinates,
        stopsLengths: stopsLengths
    };
}

function addRouteToMap(rel, res, L, xmlDoc, myMap){
    // add route segments to map

    //slightly randomize position of routes to avoid complete overlap
    //var rand_lat = (2*Math.random()-1)/2000
    //var rand_lon = (2*Math.random()-1)/2000
    var rand_lat = 0;
    var rand_lon = 0;

    for(i=0; i<rel.segments.length; i++){
        L.polyline(getArrayCoordinates(rel.segments[i], xmlDoc, rand = [rand_lat, rand_lon]),
            {color: res.segmentColors[i], weight: 5}).addTo(myMap);
    }
    // add platforms to map and return markers to fit bounds
    return displayPlatforms(rel.names, rel.platformCoordinates, res.stopsTimes, myMap);
}

function baseMap(L){
    var myMap = L.map(mapid).setView([setViewLat, setViewLon], setViewZoom);
    L.tileLayer('http://{s}.tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: '© OpenStreetMap'
    }).addTo(myMap);
    // L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
    //     attribution: '© OpenStreetMap contributors'
    // }).addTo(myMap);
    return myMap;
}

function addMultipleRoutes(relList, relColors){
    var myMap = baseMap(L);
    var relation, xhttp;
    for(var i=0; i<relList.length; i++){
        // magic: http://stackoverflow.com/questions/6077357/passing-index-from-for-loop-to-ajax-callback-function-javascript
        (function(i) {
            routeID = relList[i];
            var overpass1 = 'http://overpass-api.de/api/interpreter?data=relation(';
            var overpass2 =');out body;>;out body;rel(bn)["public_transport"="stop_area"];out body;';
            var overpass_full = overpass1 + routeID + overpass2;
            xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    var xmlDoc = this.responseXML;
                    var rel = processRelation(xmlDoc);
                    var lengths = rel.names.length;
                    var res = {};
                    res.segmentColors = Array(lengths).fill(relColors[i]);
                    res.stopsTimes = Array(lengths).fill(30);
                    var markers = addRouteToMap(rel, res, L, xmlDoc, myMap);
                }
            };
            xhttp.open("GET", overpass_full, true);
            xhttp.send();
        })(i);
    }
}<|endoftext|>


<commit_before>var miio = require('./lib');

// var vacuum = python.import('mirobot/protocol');

var MiHome = function(name, ip, port, token, model) {

  var that = this;

  this.ip = ip;
  this.port = port
  this.token = token;
  this.name = name
  this.model = model
  this.device = null;

// initialize the vacuum independently, and general device will create ether with token or without token
  if (model == 'vacuum') {
    this.device = miio.createDevice({
      address: this.ip,
      token: this.token,
      model: 'rockrobo.vacuum.v1'
    });

    this.device.init()
    .then(() => {
        console.log(this.model + " init success");
        return this;
    })
    .catch(err => console.error(this.model + " init error: " + err));
  }
  else {

    if (token == null) {
      var para = { address: ip };
    }
    else {
      var para = { address: ip, token: token };
    };

    return new Promise(function(resolve, reject){
       miio.device(para)
        .then(device => {
            this.device = device;
            debugger;
            console.log(this.name + " " + this.device.type + " init success");
            resolve(this);
          })
        .catch(err => {
          console.error(this.name + " init error: " + err);
          reject(err);
        });
      }

    // miio.device(para)
    //   .then(device => {
    //       this.device = device;
    //       debugger;
    //       console.log(this.name + " " + this.device.type + " init success");
    //       return this;
    //     })
    //   .catch(err => console.error(this.name + " init error: " + err));
  };
};

MiHome.prototype.exec = function(cmd) {

  var that = this;

  if (cmd == 'Stop') {
    cmd = 'Charge';
  }

  switch (cmd) {
    case 'Start':

      if (this.device.type == 'vacuum') {
        this.device.start()
          .then(res => console.log(this.name + " start result : " + res))
          .catch(err => console.error(this.name + " start error: " + err));
      }

      break;

    case 'Charge':

      if (this.device.type == 'vacuum') {
        this.device.charge()
          .then(res => console.log(this.name + " charge result : " + res))
          .catch(err => console.error(this.name + " charge error: " + err));
      }

      break;

    case 'Pause':

      if (this.device.type == 'vacuum') {
        this.device.pause()
          .then(res => console.log(this.name + " pause result : " + res))
          .catch(err => console.error(this.name + " pause error: " + err));
      }

      break;

    case 'List':
      if (this.device.type == 'gateway') {
        console.log(this.device.devices);
      }

      break;

    default:
      console.log('Invalid type:' + cmd);
  }

  return;

};

MiHome.prototype.powerOn = function() {

  var that = this;

  if(this.device.hasCapability('power')) {
    this.device.setPower(true)
      .then(console.log(this.name + ":Power On"))
      .catch(console.error(this.name + "Failed to Power On"));
  }

};

module.exports = function(name, ip, port, token, model) {
  return new MiHome(name, ip, port, token, model);
};

<commit_msg>update mihome index<commit_after>var miio = require('./lib');

// var vacuum = python.import('mirobot/protocol');

var MiHome = function(name, ip, port, token, model) {

  var that = this;

  this.ip = ip;
  this.port = port
  this.token = token;
  this.name = name
  this.model = model
  this.device = null;

// initialize the vacuum independently, and general device will create ether with token or without token
  if (model == 'vacuum') {
    this.device = miio.createDevice({
      address: this.ip,
      token: this.token,
      model: 'rockrobo.vacuum.v1'
    });

    this.device.init()
    .then(() => {
        console.log(this.model + " init success");
        return this;
    })
    .catch(err => console.error(this.model + " init error: " + err));
  }
  else {

    if (token == null) {
      var para = { address: ip };
    }
    else {
      var para = { address: ip, token: token };
    };

    return new Promise(function(resolve, reject){
       miio.device(para)
        .then(device => {
            this.device = device;
            debugger;
            console.log(this.name + " " + this.device.type + " init success");
            resolve(this);
          })
        .catch(err => {
          console.error(this.name + " init error: " + err);
          reject(err);
        });
      });

    // miio.device(para)
    //   .then(device => {
    //       this.device = device;
    //       debugger;
    //       console.log(this.name + " " + this.device.type + " init success");
    //       return this;
    //     })
    //   .catch(err => console.error(this.name + " init error: " + err));
  };
};

MiHome.prototype.exec = function(cmd) {

  var that = this;

  if (cmd == 'Stop') {
    cmd = 'Charge';
  }

  switch (cmd) {
    case 'Start':

      if (this.device.type == 'vacuum') {
        this.device.start()
          .then(res => console.log(this.name + " start result : " + res))
          .catch(err => console.error(this.name + " start error: " + err));
      }

      break;

    case 'Charge':

      if (this.device.type == 'vacuum') {
        this.device.charge()
          .then(res => console.log(this.name + " charge result : " + res))
          .catch(err => console.error(this.name + " charge error: " + err));
      }

      break;

    case 'Pause':

      if (this.device.type == 'vacuum') {
        this.device.pause()
          .then(res => console.log(this.name + " pause result : " + res))
          .catch(err => console.error(this.name + " pause error: " + err));
      }

      break;

    case 'List':
      if (this.device.type == 'gateway') {
        console.log(this.device.devices);
      }

      break;

    default:
      console.log('Invalid type:' + cmd);
  }

  return;

};

MiHome.prototype.powerOn = function() {

  var that = this;

  if(this.device.hasCapability('power')) {
    this.device.setPower(true)
      .then(console.log(this.name + ":Power On"))
      .catch(console.error(this.name + "Failed to Power On"));
  }

};

module.exports = function(name, ip, port, token, model) {
  return new MiHome(name, ip, port, token, model);
};

<|endoftext|>


<commit_before>// ==UserScript==
// @author      PotcFdk
// @name        Steam Discovery Queue Auto-Skipper
// @namespace   https://github.com/PotcFdk/SteamDiscoveryQueueAutoSkipper
// @description Auto-clicks the "Next in Queue" button in Steam Discovery Queues.
// @include     http://store.steampowered.com/app/*
// @include     https://store.steampowered.com/app/*
// @include     http://store.steampowered.com/agecheck/app/*
// @include     https://store.steampowered.com/agecheck/app/*
// @include     http://store.steampowered.com/explore*
// @include     https://store.steampowered.com/explore*
// @version     0.3.1
// @grant       none
// @downloadURL https://raw.githubusercontent.com/PotcFdk/SteamDiscoveryQueueAutoSkipper/master/SteamDiscoveryQueueAutoSkipper.user.js
// @updateURL   https://raw.githubusercontent.com/PotcFdk/SteamDiscoveryQueueAutoSkipper/master/SteamDiscoveryQueueAutoSkipper.meta.js
// ==/UserScript==

/*
	Steam Discovery Queue Auto-Skipper - Copyright (c) PotcFdk, 2015

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

var page = document.getElementsByTagName("BODY")[0].innerHTML;

if (page.length < 100
	|| page.includes ("An error occurred while processing your request")
	|| page.includes ("The Steam Store is experiencing some heavy load right now"))
{
	location.reload();
}

function click (obj)
{
	var evObj = new MouseEvent ('click');
	obj.dispatchEvent (evObj);

	window.addEventListener ('load', function() {
		click (obj);
	});
}

var btn = document.getElementsByClassName ("btn_next_in_queue")[0];

if (btn)
{
	var btn_text = btn.getElementsByTagName ("span")[0];
	var btn_subtext = document.getElementsByClassName ("queue_sub_text")[0];
	if (btn_text)
	{
		if (btn_subtext)
		{
			btn_text.textContent = "Loading next item...";
			btn_text.appendChild (document.createElement ("br"));
			btn_text.appendChild (btn_subtext);
		}
		else
		{
			btn_text.textContent = "Finishing Queue...";
		}
	}
	click (btn);
	setInterval (function() { click (btn); }, 5000);
}

var app_agegate = document.getElementById ("app_agegate");
if (app_agegate)
{
	var btn_medium = app_agegate.getElementsByClassName ("btn_medium");
	if (btn_medium)
	{
		for (i = 0; i < btn_medium.length; i++)
		{
			if (btn_medium[i].textContent.includes ("Continue"))
			{
				click (btn_medium[i]);
			}
		}
	}
}

var ageYear = document.getElementById ("ageYear");
if (ageYear)
{
	ageYear.value = 1985;
	if (DoAgeGateSubmit)
	{
		DoAgeGateSubmit();
	}
}

var refresh_queue_btn = document.getElementById ("refresh_queue_btn");
if (refresh_queue_btn && (Number (document.getElementsByClassName ('subtext')[0].innerHTML.substring(12,13)) >= 1))
{
	click (refresh_queue_btn);
}
<commit_msg>Remove unnecessary setInterval call<commit_after>// ==UserScript==
// @author      PotcFdk
// @name        Steam Discovery Queue Auto-Skipper
// @namespace   https://github.com/PotcFdk/SteamDiscoveryQueueAutoSkipper
// @description Auto-clicks the "Next in Queue" button in Steam Discovery Queues.
// @include     http://store.steampowered.com/app/*
// @include     https://store.steampowered.com/app/*
// @include     http://store.steampowered.com/agecheck/app/*
// @include     https://store.steampowered.com/agecheck/app/*
// @include     http://store.steampowered.com/explore*
// @include     https://store.steampowered.com/explore*
// @version     0.3.1
// @grant       none
// @downloadURL https://raw.githubusercontent.com/PotcFdk/SteamDiscoveryQueueAutoSkipper/master/SteamDiscoveryQueueAutoSkipper.user.js
// @updateURL   https://raw.githubusercontent.com/PotcFdk/SteamDiscoveryQueueAutoSkipper/master/SteamDiscoveryQueueAutoSkipper.meta.js
// ==/UserScript==

/*
	Steam Discovery Queue Auto-Skipper - Copyright (c) PotcFdk, 2015

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

var page = document.getElementsByTagName("BODY")[0].innerHTML;

if (page.length < 100
	|| page.includes ("An error occurred while processing your request")
	|| page.includes ("The Steam Store is experiencing some heavy load right now"))
{
	location.reload();
}

function click (obj)
{
	var evObj = new MouseEvent ('click');
	obj.dispatchEvent (evObj);

	window.addEventListener ('load', function() {
		click (obj);
	});
}

var btn = document.getElementsByClassName ("btn_next_in_queue")[0];

if (btn)
{
	var btn_text = btn.getElementsByTagName ("span")[0];
	var btn_subtext = document.getElementsByClassName ("queue_sub_text")[0];
	if (btn_text)
	{
		if (btn_subtext)
		{
			btn_text.textContent = "Loading next item...";
			btn_text.appendChild (document.createElement ("br"));
			btn_text.appendChild (btn_subtext);
		}
		else
		{
			btn_text.textContent = "Finishing Queue...";
		}
	}
	click (btn);
}

var app_agegate = document.getElementById ("app_agegate");
if (app_agegate)
{
	var btn_medium = app_agegate.getElementsByClassName ("btn_medium");
	if (btn_medium)
	{
		for (i = 0; i < btn_medium.length; i++)
		{
			if (btn_medium[i].textContent.includes ("Continue"))
			{
				click (btn_medium[i]);
			}
		}
	}
}

var ageYear = document.getElementById ("ageYear");
if (ageYear)
{
	ageYear.value = 1985;
	if (DoAgeGateSubmit)
	{
		DoAgeGateSubmit();
	}
}

var refresh_queue_btn = document.getElementById ("refresh_queue_btn");
if (refresh_queue_btn && (Number (document.getElementsByClassName ('subtext')[0].innerHTML.substring(12,13)) >= 1))
{
	click (refresh_queue_btn);
}
<|endoftext|>


<commit_before><commit_msg>Bug fix: crash in TR4 cutscene #10<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * this module contains all the code related to the build process
 */

const { FilesManager, StringUtils, ObjectUtils } = require('turbocommons-ts');
const { execSync } = require('child_process');
const console = require('./console');
const setupModule = require('./setup');
const validateModule = require('./validate');
const sass = require('node-sass');

let fm = new FilesManager(require('fs'), require('os'), require('path'), process);

/**
 * We will delete the unpacked src files when application exits, may it be due to a
 * success or an error
 */
process.on('exit', () => {

    if(global.setup !== null &&
            !global.setup.build.keepUnpackedSrcFiles){

        this.removeUnpackedSrcFiles(global.runtimePaths.target + fm.dirSep() + this.getBuildRelativePath());
    }
});

/**
 * Execute the build process
 */
exports.execute = function () {

    console.log("\nbuild start: " + setupModule.detectProjectTypeFromSetup(global.setup));

    let buildFullPath = global.runtimePaths.target + fm.dirSep() + this.getBuildRelativePath();

    // Delete all files inside the target/projectName folder
    fm.deleteDirectory(buildFullPath);

    // Copy all the src main files to the target dev build folder
    this.copyMainFiles(buildFullPath);

    if(global.setup.validate.runBeforeBuild){

        validateModule.execute(false);
    }

    // Perform custom build depending on project type
    if(global.setup.build.site_php){

        this.buildSitePhp(buildFullPath);
    }

    if(global.setup.build.lib_php){

        this.buildLibPhp(buildFullPath);
    }

    if(global.setup.build.lib_ts){

        this.buildLibTs(buildFullPath);
    }

    console.success('build ok');
};

/**
 * Gets the path relative to project target where current build version is generated
 */
exports.getBuildRelativePath = function () {

    return global.runtimePaths.projectName;
}

/**
 * Copy all the project src/main files to the target folder. Any unwanted files/folders are excluded
 */
exports.copyMainFiles = function (destPath) {

    // If source file is empty, alert the user
    if(!fm.isDirectory(global.runtimePaths.main) ||
       fm.findDirectoryItems(global.runtimePaths.main, /.*/i, 'relative', 'files').length === 0){

        console.error('no files to build');
    }

    let destMain = destPath + fm.dirSep() + 'main';

    // Copy the main folder to the target
    fm.createDirectory(destMain, true);

    // Delete all the following files: thumbs.db .svn .git
    // TODO let filesToDelete = fm.findDirectoryItems(global.runtimePaths.main, /^(?!.*(thumbs\.db|\.svn|\.git)$)/i, 'absolute', 'files');

    fm.copyDirectory(global.runtimePaths.main, destMain);

    // TODO - Replace the string @@package-build-version@@ on all the files with the real build version number

    console.success('copy main files ok');
}

/**
 * Execute the site_php build process to the specified dest folder
 */
exports.buildSitePhp = function (destPath) {

    let sep = fm.dirSep();
    let destMain = destPath + sep + 'main';
    let destDist = destPath + sep + 'dist';
    let destSite = destDist + sep + 'site';

    // Create the dist and site folders if not exists
    if(!fm.isDirectory(destDist) && !fm.createDirectory(destDist)){

        console.error('Could not create ' + destDist);
    }

    if(!fm.createDirectory(destSite)){

        console.error('Could not create ' + destSite);
    }

    fm.copyDirectory(destMain, destSite);

    // Move htaccess file from site to dist
    fm.copyFile(destSite + sep + 'htaccess.txt', destDist + sep + '.htaccess');
    fm.deleteFile(destSite + sep + 'htaccess.txt');

    // Generate a random hash to avoid browser caches
    let turboSiteSetup = JSON.parse(fm.readFile(destSite + sep + 'turbosite.json'));

    turboSiteSetup.cacheHash = StringUtils.generateRandom(15, 15);

    fm.saveFile(destSite + sep + 'turbosite.json', JSON.stringify(turboSiteSetup, null, 4));

    // Process all sass scss files to css
    let scssFiles = fm.findDirectoryItems(destSite, /^.*\.scss$/i, 'absolute', 'files');

    for (let scssFile of scssFiles) {

        let scssCode = sass.renderSync({
            file: scssFile
          });

        fm.saveFile(StringUtils.replace(scssFile, '.scss', '.css'), scssCode.css);
    }

    for (let scssFile of scssFiles) {

        fm.deleteFile(scssFile);
    }

    // Create global css file
    fm.saveFile(destSite + sep + 'glob-' + turboSiteSetup.cacheHash +'.css',
            mergeFilesFromArray(turboSiteSetup.globalCss, destSite, true));

    // Create global Js file
    fm.saveFile(destSite + sep + 'glob-' + turboSiteSetup.cacheHash +'.js',
            mergeFilesFromArray(turboSiteSetup.globalJs, destSite, true));

    // Generate all the components css and js merged files
    mergeCssAndJsByFolder(destSite + sep + 'view' + sep + 'components', destSite, turboSiteSetup.cacheHash, 'comp-view-components-');

    // Generate all the views css and js merged files
    mergeCssAndJsByFolder(destSite + sep + 'view' + sep + 'views', destSite, turboSiteSetup.cacheHash, 'view-view-views-');
}

/**
 * Execute the lib_php build process to the specified dest folder
 */
exports.buildLibPhp = function (destPath) {

    let sep = fm.dirSep();
    let destMain = destPath + sep + 'main';
    let destDist = destPath + sep + 'dist';

    this.checkPhpAvailable();

    // autoloader.php must exist on src/main/php/ for the phar to be correctly generated
    let autoLoaderPath = global.runtimePaths.main + sep + 'php' + sep + 'autoLoader.php';

    if(!fm.isFile(autoLoaderPath)){

        console.error(autoLoaderPath + " not found.\nThis is required to create a phar that loads classes automatically");
    }

    // Define the contents for the stub file that will be autoexecuted when the phar file is included
    let pharName = global.runtimePaths.projectName + "-" + setupModule.getProjectRepoSemVer() + '.phar';

    let phpStubFile = "<?php Phar::mapPhar(); include \\'phar://" + pharName + "/php/autoloader.php\\'; __HALT_COMPILER(); ?>";

    // Create the dist folder if not exists
    if(!fm.isDirectory(destDist) && !fm.createDirectory(destDist)){

        console.error('Could not create ' + destDist);
    }

    // Create the phar using the current project name
    let phpExecCommand = 'php -d display_errors -r';

    phpExecCommand += ' "';
    phpExecCommand += " $p = new Phar('" + destDist + sep + pharName + "', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME, '" + pharName + "');";
    phpExecCommand += " $p->startBuffering();";
    phpExecCommand += " $p->setStub('" + phpStubFile + "');";
    phpExecCommand += " $p->buildFromDirectory('" + destMain + "');";
    phpExecCommand += " $p->compressFiles(Phar::GZ); $p->stopBuffering();";
    phpExecCommand += '"';

    console.exec(phpExecCommand);
}

/**
 * Execute the lib_ts build process to the specified dest folder
 */
exports.buildLibTs = function (destPath) {

    let sep = fm.dirSep();
    let destMain = destPath + sep + 'main';
    let destDist = destPath + sep + 'dist';
    let tsConfig = destMain + sep + 'ts' + sep + 'tsconfig.json';

    // Create a default tsconfig file if there's no specific one
    if (!fm.isFile(tsConfig) &&
        !fm.saveFile(tsConfig, '{"compilerOptions":{"target": "es5"}}')) {

        console.error('Could not create ' + tsConfig);
    }

    for (let target of global.setup.build.lib_ts.targets) {

        let isMergedFile = target.hasOwnProperty('mergedFile') && !StringUtils.isEmpty(target.mergedFile);

        let compiledFolder = destDist + sep + target.folder + (isMergedFile ? sep + 'tmp' : '');

        let tsExecution = global.installationPaths.typeScriptBin;

        tsExecution += global.setup.build.lib_ts.strict ? ' --strict' : '';
        tsExecution += global.setup.build.lib_ts.declaration ? ' --declaration' : '';
        tsExecution += global.setup.build.lib_ts.sourceMap ? ' --sourceMap' : '';

        tsExecution += ' --alwaysStrict';
        tsExecution += ' --target ' + target.jsTarget;
        tsExecution += ' --outDir "' + compiledFolder + '"';
        tsExecution += ' --module commonjs';
        tsExecution += ' --rootDir "' + destMain + sep + 'ts"';
        tsExecution += ' --project "' + destMain + sep + 'ts"';
        console.exec(tsExecution);

        // Check if the target requires a merged JS file or not
        if(isMergedFile){

            let mergedFileName = target.mergedFile + '.js';

            // Generate via webpack the merged JS file for the current target
            let webPackExecution = global.installationPaths.webPackBin;

            webPackExecution += ' "' + compiledFolder + sep + 'index.js"';
            webPackExecution += ' "' + destDist + sep + target.folder + sep + mergedFileName + '"';
            webPackExecution += ' --output-library ' + target.globalVar;

            if(global.setup.build.lib_ts.sourceMap){

                fm.saveFile(compiledFolder + sep + 'webpack.config.js', "module.exports = {devtool: 'source-map'};");

                webPackExecution += ' --config "' + compiledFolder + sep + 'webpack.config.js"';
            }

            console.exec(webPackExecution, 'Webpack ' + target.jsTarget + ' ok');

            fm.deleteDirectory(compiledFolder);
        }
    }
}

/**
 * Join all the files from the specified array and get a string with the result
 */
let mergeFilesFromArray = function (array, basePath, deleteFiles = false) {

    let result = '';

    if(basePath !== ''){

        basePath += fm.dirSep();
    }

    for (let file of array) {

        result += fm.readFile(basePath + StringUtils.replace(file, '.scss', '.css')) + "\n\n";
    }

    if(deleteFiles){

        for (let file of array) {

            fm.deleteFile(basePath + file);
        }
    }

    return result;
}

/**
 * Join all the css and js files for each folder on the specified path and generate the global js and css files
 */
let mergeCssAndJsByFolder = function (basePath, destPath, cacheHash, prefix = "comp-view-components-") {

    let sep = fm.dirSep();
    let items = fm.getDirectoryList(basePath);

    for (let item of items) {

        if(fm.isDirectory(basePath + fm.dirSep() + item)){

            // Merge all css files on the folder and generate the css
            let cssFiles = fm.findDirectoryItems(basePath + fm.dirSep() + item, /^.*\.css$/i, 'absolute', 'files');
            let cssContent = mergeFilesFromArray(cssFiles, '', true);

            if(!StringUtils.isEmpty(cssContent)){

                fm.saveFile(destPath + sep + prefix + item + '-' + cacheHash +'.css', cssContent);
            }

            // Merge all the js files on the folder and generate the component js
            let jsFiles = fm.findDirectoryItems(basePath + fm.dirSep() + item, /.*\.js$/i, 'absolute', 'files');
            let jsContent = mergeFilesFromArray(jsFiles, '', true);

            if(!StringUtils.isEmpty(jsContent)){

                fm.saveFile(destPath + sep + prefix + item + '-' + cacheHash +'.js', jsContent);
            }
        }
    }
}

/**
 * Delete all the src main files that exist on target folder
 */
exports.removeUnpackedSrcFiles = function (destPath) {

    let destMain = destPath + fm.dirSep() + 'main';

    // Delete the files
    if(fm.isDirectory(destMain) && !fm.deleteDirectory(destMain)){

        console.error('Could not delete unpacked src files from ' + destMain);
    }
}

let isWinSCPAvailable = false;

/**
 * Check if the WinSCP cmd executable is available or not on the system
 */
exports.checkWinSCPAvailable = function () {

    if(!isWinSCPAvailable){

        try{

            execSync('winscp /help', {stdio : 'pipe'});

            isWinSCPAvailable = true;

        }catch(e){

            console.error('Could not find winscp cmd executable. Please install winscp and make sure is available globally via cmd (add to PATH enviroment variable) to perform sync operations');
        }
    }
}

let isGitAvailable = false;

/**
 * Check if the git cmd executable is available or not on the system
 */
exports.checkGitAvailable = function () {

    if(!isGitAvailable){

        try{

            execSync('git --version', {stdio : 'pipe'});

            isGitAvailable = true;

        }catch(e){

            console.error('Could not find Git cmd executable. Please install git on your system to create git changelogs');
        }
    }
}

let isPhpAvailable = false;

/**
 * Check if the php cmd executable is available or not on the system
 */
exports.checkPhpAvailable = function () {

    if(!isPhpAvailable){

        try{

            execSync('php -v', {stdio : 'pipe'});

            isPhpAvailable = true;

        }catch(e){

            console.error('Could not find Php cmd executable. Please install php and make sure is available globally via cmd (add to enviroment variables).');
        }
    }
}<commit_msg>Fixed delete bug when merging files<commit_after>'use strict';

/**
 * this module contains all the code related to the build process
 */

const { FilesManager, StringUtils, ObjectUtils } = require('turbocommons-ts');
const { execSync } = require('child_process');
const console = require('./console');
const setupModule = require('./setup');
const validateModule = require('./validate');
const sass = require('node-sass');

let fm = new FilesManager(require('fs'), require('os'), require('path'), process);

/**
 * We will delete the unpacked src files when application exits, may it be due to a
 * success or an error
 */
process.on('exit', () => {

    if(global.setup !== null &&
            !global.setup.build.keepUnpackedSrcFiles){

        this.removeUnpackedSrcFiles(global.runtimePaths.target + fm.dirSep() + this.getBuildRelativePath());
    }
});

/**
 * Execute the build process
 */
exports.execute = function () {

    console.log("\nbuild start: " + setupModule.detectProjectTypeFromSetup(global.setup));

    let buildFullPath = global.runtimePaths.target + fm.dirSep() + this.getBuildRelativePath();

    // Delete all files inside the target/projectName folder
    fm.deleteDirectory(buildFullPath);

    // Copy all the src main files to the target dev build folder
    this.copyMainFiles(buildFullPath);

    if(global.setup.validate.runBeforeBuild){

        validateModule.execute(false);
    }

    // Perform custom build depending on project type
    if(global.setup.build.site_php){

        this.buildSitePhp(buildFullPath);
    }

    if(global.setup.build.lib_php){

        this.buildLibPhp(buildFullPath);
    }

    if(global.setup.build.lib_ts){

        this.buildLibTs(buildFullPath);
    }

    console.success('build ok');
};

/**
 * Gets the path relative to project target where current build version is generated
 */
exports.getBuildRelativePath = function () {

    return global.runtimePaths.projectName;
}

/**
 * Copy all the project src/main files to the target folder. Any unwanted files/folders are excluded
 */
exports.copyMainFiles = function (destPath) {

    // If source file is empty, alert the user
    if(!fm.isDirectory(global.runtimePaths.main) ||
       fm.findDirectoryItems(global.runtimePaths.main, /.*/i, 'relative', 'files').length === 0){

        console.error('no files to build');
    }

    let destMain = destPath + fm.dirSep() + 'main';

    // Copy the main folder to the target
    fm.createDirectory(destMain, true);

    // Delete all the following files: thumbs.db .svn .git
    // TODO let filesToDelete = fm.findDirectoryItems(global.runtimePaths.main, /^(?!.*(thumbs\.db|\.svn|\.git)$)/i, 'absolute', 'files');

    fm.copyDirectory(global.runtimePaths.main, destMain);

    // TODO - Replace the string @@package-build-version@@ on all the files with the real build version number

    console.success('copy main files ok');
}

/**
 * Execute the site_php build process to the specified dest folder
 */
exports.buildSitePhp = function (destPath) {

    let sep = fm.dirSep();
    let destMain = destPath + sep + 'main';
    let destDist = destPath + sep + 'dist';
    let destSite = destDist + sep + 'site';

    // Create the dist and site folders if not exists
    if(!fm.isDirectory(destDist) && !fm.createDirectory(destDist)){

        console.error('Could not create ' + destDist);
    }

    if(!fm.createDirectory(destSite)){

        console.error('Could not create ' + destSite);
    }

    fm.copyDirectory(destMain, destSite);

    // Move htaccess file from site to dist
    fm.copyFile(destSite + sep + 'htaccess.txt', destDist + sep + '.htaccess');
    fm.deleteFile(destSite + sep + 'htaccess.txt');

    // Generate a random hash to avoid browser caches
    let turboSiteSetup = JSON.parse(fm.readFile(destSite + sep + 'turbosite.json'));

    turboSiteSetup.cacheHash = StringUtils.generateRandom(15, 15);

    fm.saveFile(destSite + sep + 'turbosite.json', JSON.stringify(turboSiteSetup, null, 4));

    // Process all sass scss files to css
    let scssFiles = fm.findDirectoryItems(destSite, /^.*\.scss$/i, 'absolute', 'files');

    for (let scssFile of scssFiles) {

        let scssCode = sass.renderSync({
            file: scssFile
          });

        fm.saveFile(StringUtils.replace(scssFile, '.scss', '.css'), scssCode.css);
    }

    for (let scssFile of scssFiles) {

        fm.deleteFile(scssFile);
    }

    // Create global css file
    fm.saveFile(destSite + sep + 'glob-' + turboSiteSetup.cacheHash +'.css',
            mergeFilesFromArray(turboSiteSetup.globalCss, destSite, true));

    // Create global Js file
    fm.saveFile(destSite + sep + 'glob-' + turboSiteSetup.cacheHash +'.js',
            mergeFilesFromArray(turboSiteSetup.globalJs, destSite, true));

    // Generate all the components css and js merged files
    mergeCssAndJsByFolder(destSite + sep + 'view' + sep + 'components', destSite, turboSiteSetup.cacheHash, 'comp-view-components-');

    // Generate all the views css and js merged files
    mergeCssAndJsByFolder(destSite + sep + 'view' + sep + 'views', destSite, turboSiteSetup.cacheHash, 'view-view-views-');
}

/**
 * Execute the lib_php build process to the specified dest folder
 */
exports.buildLibPhp = function (destPath) {

    let sep = fm.dirSep();
    let destMain = destPath + sep + 'main';
    let destDist = destPath + sep + 'dist';

    this.checkPhpAvailable();

    // autoloader.php must exist on src/main/php/ for the phar to be correctly generated
    let autoLoaderPath = global.runtimePaths.main + sep + 'php' + sep + 'autoLoader.php';

    if(!fm.isFile(autoLoaderPath)){

        console.error(autoLoaderPath + " not found.\nThis is required to create a phar that loads classes automatically");
    }

    // Define the contents for the stub file that will be autoexecuted when the phar file is included
    let pharName = global.runtimePaths.projectName + "-" + setupModule.getProjectRepoSemVer() + '.phar';

    let phpStubFile = "<?php Phar::mapPhar(); include \\'phar://" + pharName + "/php/autoloader.php\\'; __HALT_COMPILER(); ?>";

    // Create the dist folder if not exists
    if(!fm.isDirectory(destDist) && !fm.createDirectory(destDist)){

        console.error('Could not create ' + destDist);
    }

    // Create the phar using the current project name
    let phpExecCommand = 'php -d display_errors -r';

    phpExecCommand += ' "';
    phpExecCommand += " $p = new Phar('" + destDist + sep + pharName + "', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME, '" + pharName + "');";
    phpExecCommand += " $p->startBuffering();";
    phpExecCommand += " $p->setStub('" + phpStubFile + "');";
    phpExecCommand += " $p->buildFromDirectory('" + destMain + "');";
    phpExecCommand += " $p->compressFiles(Phar::GZ); $p->stopBuffering();";
    phpExecCommand += '"';

    console.exec(phpExecCommand);
}

/**
 * Execute the lib_ts build process to the specified dest folder
 */
exports.buildLibTs = function (destPath) {

    let sep = fm.dirSep();
    let destMain = destPath + sep + 'main';
    let destDist = destPath + sep + 'dist';
    let tsConfig = destMain + sep + 'ts' + sep + 'tsconfig.json';

    // Create a default tsconfig file if there's no specific one
    if (!fm.isFile(tsConfig) &&
        !fm.saveFile(tsConfig, '{"compilerOptions":{"target": "es5"}}')) {

        console.error('Could not create ' + tsConfig);
    }

    for (let target of global.setup.build.lib_ts.targets) {

        let isMergedFile = target.hasOwnProperty('mergedFile') && !StringUtils.isEmpty(target.mergedFile);

        let compiledFolder = destDist + sep + target.folder + (isMergedFile ? sep + 'tmp' : '');

        let tsExecution = global.installationPaths.typeScriptBin;

        tsExecution += global.setup.build.lib_ts.strict ? ' --strict' : '';
        tsExecution += global.setup.build.lib_ts.declaration ? ' --declaration' : '';
        tsExecution += global.setup.build.lib_ts.sourceMap ? ' --sourceMap' : '';

        tsExecution += ' --alwaysStrict';
        tsExecution += ' --target ' + target.jsTarget;
        tsExecution += ' --outDir "' + compiledFolder + '"';
        tsExecution += ' --module commonjs';
        tsExecution += ' --rootDir "' + destMain + sep + 'ts"';
        tsExecution += ' --project "' + destMain + sep + 'ts"';
        console.exec(tsExecution);

        // Check if the target requires a merged JS file or not
        if(isMergedFile){

            let mergedFileName = target.mergedFile + '.js';

            // Generate via webpack the merged JS file for the current target
            let webPackExecution = global.installationPaths.webPackBin;

            webPackExecution += ' "' + compiledFolder + sep + 'index.js"';
            webPackExecution += ' "' + destDist + sep + target.folder + sep + mergedFileName + '"';
            webPackExecution += ' --output-library ' + target.globalVar;

            if(global.setup.build.lib_ts.sourceMap){

                fm.saveFile(compiledFolder + sep + 'webpack.config.js', "module.exports = {devtool: 'source-map'};");

                webPackExecution += ' --config "' + compiledFolder + sep + 'webpack.config.js"';
            }

            console.exec(webPackExecution, 'Webpack ' + target.jsTarget + ' ok');

            fm.deleteDirectory(compiledFolder);
        }
    }
}

/**
 * Join all the files from the specified array and get a string with the result
 */
let mergeFilesFromArray = function (array, basePath, deleteFiles = false) {

    let result = '';

    if(basePath !== ''){

        basePath += fm.dirSep();
    }

    for (let file of array) {

        result += fm.readFile(basePath + StringUtils.replace(file, '.scss', '.css')) + "\n\n";
    }

    if(deleteFiles){

        for (let file of array) {

            fm.deleteFile(basePath + StringUtils.replace(file, '.scss', '.css'));
        }
    }

    return result;
}

/**
 * Join all the css and js files for each folder on the specified path and generate the global js and css files
 */
let mergeCssAndJsByFolder = function (basePath, destPath, cacheHash, prefix = "comp-view-components-") {

    let sep = fm.dirSep();
    let items = fm.getDirectoryList(basePath);

    for (let item of items) {

        if(fm.isDirectory(basePath + fm.dirSep() + item)){

            // Merge all css files on the folder and generate the css
            let cssFiles = fm.findDirectoryItems(basePath + fm.dirSep() + item, /^.*\.css$/i, 'absolute', 'files');
            let cssContent = mergeFilesFromArray(cssFiles, '', true);

            if(!StringUtils.isEmpty(cssContent)){

                fm.saveFile(destPath + sep + prefix + item + '-' + cacheHash +'.css', cssContent);
            }

            // Merge all the js files on the folder and generate the component js
            let jsFiles = fm.findDirectoryItems(basePath + fm.dirSep() + item, /.*\.js$/i, 'absolute', 'files');
            let jsContent = mergeFilesFromArray(jsFiles, '', true);

            if(!StringUtils.isEmpty(jsContent)){

                fm.saveFile(destPath + sep + prefix + item + '-' + cacheHash +'.js', jsContent);
            }
        }
    }
}

/**
 * Delete all the src main files that exist on target folder
 */
exports.removeUnpackedSrcFiles = function (destPath) {

    let destMain = destPath + fm.dirSep() + 'main';

    // Delete the files
    if(fm.isDirectory(destMain) && !fm.deleteDirectory(destMain)){

        console.error('Could not delete unpacked src files from ' + destMain);
    }
}

let isWinSCPAvailable = false;

/**
 * Check if the WinSCP cmd executable is available or not on the system
 */
exports.checkWinSCPAvailable = function () {

    if(!isWinSCPAvailable){

        try{

            execSync('winscp /help', {stdio : 'pipe'});

            isWinSCPAvailable = true;

        }catch(e){

            console.error('Could not find winscp cmd executable. Please install winscp and make sure is available globally via cmd (add to PATH enviroment variable) to perform sync operations');
        }
    }
}

let isGitAvailable = false;

/**
 * Check if the git cmd executable is available or not on the system
 */
exports.checkGitAvailable = function () {

    if(!isGitAvailable){

        try{

            execSync('git --version', {stdio : 'pipe'});

            isGitAvailable = true;

        }catch(e){

            console.error('Could not find Git cmd executable. Please install git on your system to create git changelogs');
        }
    }
}

let isPhpAvailable = false;

/**
 * Check if the php cmd executable is available or not on the system
 */
exports.checkPhpAvailable = function () {

    if(!isPhpAvailable){

        try{

            execSync('php -v', {stdio : 'pipe'});

            isPhpAvailable = true;

        }catch(e){

            console.error('Could not find Php cmd executable. Please install php and make sure is available globally via cmd (add to enviroment variables).');
        }
    }
}<|endoftext|>


<commit_before>function LineBottomLeft(graphObject) {
    this.graphObject = graphObject;
    this.visible = false;
    graphObject.preview = {};
    this.graphID = graphObject.id;
    this.previewDiv = null;
    this.Initialize = function (container) {
        var width = this.graphObject.width - GraphManager.defaultValues.axisMargin.y;
        var height = this.graphObject.height - GraphManager.defaultValues.axisMargin.x;
        var marginLeft = GraphManager.defaultValues.graphPadding.left + GraphManager.defaultValues.axisMargin.y;
        var marginTop = GraphManager.defaultValues.graphPadding.top;

        var svg = d3.select(container).append("svg")
        .attr("width", width)
        .attr("height", height);

        svg.className = "graph-svg";

        var lineG = svg.append("g");

        var text = null;

        if (typeof this.graphObject.name !== "undefined") {
            text = svg.append("text")
            .attr("x", (width / 2))
            .attr("y", marginTop)
            .attr("dy", 20 - GraphManager.defaultValues.graphPadding.top)
            .attr("text-anchor", "middle")
            .attr("pointer-events", "none")
            .attr("class", "graph-title-text")
            .style("font-size", "16px")
            .text(this.graphObject.name);
        }

        //generate x axis;
        var axisX = svg.append("g")
            .attr("class", "axis xAxis");
        this.graphObject.axisX = axisX;
        if (this.graphObject.x.label != "") {
            var axisXLabel = svg.append("text")
            .attr("class", "axisLabel xAxisLabel")
            .text(this.graphObject.x.label);
            this.graphObject.axisXLabel = axisXLabel;
        }

        //generate y axis
        var axisY = svg.append("g")
            .attr("class", "axis yAxis");
        this.graphObject.axisY = axisY;
        if (this.graphObject.y[0].label != "") { //todo check to see if there are different labels -> label lines instead of axis!
            var axisYLabel = svg.append("text")
            .attr("class", "axisLabel yAxisLabel")
            .text(this.graphObject.y[0].label);
            this.graphObject.axisYLabel = axisYLabel;
        }

        this.graphObject.container = container;
        this.graphObject.svg = svg;
        this.graphObject.data = [];
        this.graphObject.displayData = [];
        for (var i = 0; i < graphObject.y.length; i++) {
            this.graphObject.data[i] = [];
            this.graphObject.displayData[i] = [];
        }
        this.graphObject.lineG = lineG;
        this.graphObject.text = text;

        container.style.visibility = "hidden";
        container.graph = this;
    }

    this._UpdatePreview = function () {

        if (this.previewDiv == null || this.graphObject.data == null || this.graphObject.data.length == 0)
            return;
        else {
            let found = false;
            for (var i = 0; i < this.graphObject.data.length; i++) {
                if (this.graphObject.data[i].length > 0)
                    found = true;
            }
            if (!found)
                return;
        }

        var graph = this.graphObject;

        var width = DataManager.detailsInfo.chartWidth;
        var height = DataManager.detailsInfo.chartHeight;

        var minX = graph.bounds.minX;
        var maxX = graph.bounds.maxX;
        var minY = graph.bounds.minY;
        var maxY = graph.bounds.maxY;

        var xScale;
        var yScale;

        //var width = graph.container.clientWidth;
        //var height = graph.container.clientHeight;

        switch (graph.xScale) {
            case "linear": xScale = d3.scale.linear().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
            case "ordinal": xScale = d3.scale.ordinal().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
            case "power": xScale = d3.scale.power().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
            case "log": xScale = d3.scale.log().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
        }

        switch (graph.yScale) {
            case "linear": yScale = d3.scale.linear().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
            case "ordinal": yScale = d3.scale.ordinal().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
            case "power": yScale = d3.scale.power().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
            case "log": yScale = d3.scale.log().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
        }

        graph.preview.lineG.selectAll("path").remove();

        var lineFunction = d3.svg.line()
            .x(function (d) { return xScale(d.x); })
            .y(function (d) { return yScale(d.y); })
            .interpolate(graph.interpolation);

        for (var i = 0; i < graph.y.length; i++) {

            graph.preview.lineG.append("path")
            .attr("d", lineFunction(graph.displayData[i]))
            .attr("class", "graphLine")
            .attr("stroke", graph.y[i].color)
            .attr('stroke-width', 2)
            .attr("fill", "none");
        }
    }

    this.GetPreview = function(container)
    {

        if (this.previewDiv != null)
            return container.appendChild(this.previewDiv);

        var previewContainer = container.appendChild(document.createElement("div"));
        previewContainer.className = "detailContainer graphDetails";
        previewContainer.style.width = DataManager.detailsInfo.chartWidth + "px";
        previewContainer.style.height = DataManager.detailsInfo.elementHeight + "px";
        //if (typeof this.graphObject.description !== "undefined")
        //    previewContainer.title = this.graphObject.description;

        this.previewDiv = previewContainer;
        previewContainer.graph = this;

        previewContainer.addEventListener("click", this._clickEvent);

        var title = previewContainer.appendChild(document.createElement("h4"));
        title.className = "detailTitle graphDetailTitle";
        title.textContent = this.graphObject.name;
        title.style.width = DataManager.detailsInfo.elementWidth + "px";

        var svgContainer = previewContainer.appendChild(document.createElement("div"));
        svgContainer.className = "preview-svg-container";
        svgContainer.style.width = DataManager.detailsInfo.chartWidth + "px";
        svgContainer.style.height = DataManager.detailsInfo.chartHeight + "px";

        var svg = d3.select(svgContainer).append("svg")
            .attr("width", DataManager.detailsInfo.chartWidth)
            .attr("height", DataManager.detailsInfo.chartHeight);

        svg.className = "graph-svg-preview";

        var lineG = svg.append("g");
        lineG.className = "graph-g-preview";

        this.graphObject.preview.container = previewContainer;
        this.graphObject.preview.svg = svg;
        this.graphObject.preview.lineG = lineG;

        this._UpdatePreview();

    }
    this.Update = function (data)
    {
        var graph = this.graphObject;

        //sets data and displaydata
        GraphManager.AddGraphData(graph, data);

        var width = graph.container.clientWidth;
        var height = graph.container.clientHeight;
        var marginLeft = GraphManager.defaultValues.graphPadding.left + GraphManager.defaultValues.axisMargin.y;
        var marginTop = GraphManager.defaultValues.graphPadding.top;
        var marginRight = GraphManager.defaultValues.graphPadding.right;
        var marginBottom = GraphManager.defaultValues.graphPadding.bottom + GraphManager.defaultValues.axisMargin.x;;

        graph.axisX.attr("transform", "translate(" + 0 + ", " + (height - marginBottom) + ")");

        graph.axisY.attr("transform", "translate(" + marginLeft + ", " + 0 + ")");

        //sets the labels //todo possible to remove this from Update and add it to initialization and resizing;
        if (typeof graph.axisXLabel !== "undefined") {
            var yTransform = height - (marginBottom + GraphManager.defaultValues.axisTextPadding);

            graph.axisXLabel.attr("text-anchor", "end");
            graph.axisXLabel.attr("x", (width - marginRight));
            graph.axisXLabel.attr("y", yTransform);
        }
        if (typeof graph.axisYLabel !== "undefined") {
            var xTransform = marginLeft + GraphManager.defaultValues.axisTextPadding;
            var rotation = 90;

            graph.axisYLabel.attr("text-anchor", "start")
            graph.axisYLabel.attr("x", xTransform);
            graph.axisYLabel.attr("y", marginTop);
            graph.axisYLabel.attr("transform", "rotate(" + rotation + "," + xTransform + "," + marginTop + ")");
        }

        graph.svg.attr("width", width);
        graph.svg.attr("height", height);

        if (graph.text != null)
            graph.text.attr("x", (width / 2));

        //gets the data to display
        var displayData = graph.displayData;
        //for (var i = Math.max(0, graph.data.length - graph.maxPoints) ; i < graph.data.length; i++) {
        //    yData = [graph.data[i].y[0]];
        //    for (var j = 1; j < graph.data[i].y.length; j++) {
        //        if (graph.additive)
        //            yData.push(graph.data[i].y[j] + yData[j - 1]);
        //        else
        //            yData.push(graph.data[i].y[j]);
        //    }
        //    displayData.push({ x: graph.data[i].x, y: yData });
        //}

        //graph.displayData = displayData;

        //sets min/max values
        var minX = (typeof graph.minX === "undefined") ? d3.min(displayData, function (d) { return d3.min(d, function (p) { return p.x; }) }) : graph.minX;
        var maxX = (typeof graph.maxX === "undefined") ? d3.max(displayData, function (d) { return d3.max(d, function (p) { return p.x; }) }) : graph.maxX;
        var minY = (typeof graph.minY === "undefined") ? d3.min(displayData, function (d) { return d3.min(d, function (p) { return p.y; }) }) : graph.minY;
        var maxY = (typeof graph.maxY === "undefined") ? d3.max(displayData, function (d) { return d3.max(d, function (p) { return p.y; }) }) : graph.maxY;
        if (graph.holdminmax) {
            if (typeof graph.holdvalues === "undefined") //todo fix possible missed values when new data.length > maxPoints
                graph.holdvalues = {
                    minY: minY,
                    maxY: maxY
                }
            else {
                minY = (minY > graph.holdvalues.minY) ? graph.holdvalues.minY : minY;
                maxY = (maxY < graph.holdvalues.maxY) ? graph.holdvalues.maxY : maxY;
            }
        }
        if (minX == maxX) {
            minX--;
            maxX++;
        }
        if (minY == maxY) {
            minY--;
            maxY++;
        }

        graph.bounds = {
            minX: minX,
            maxX: maxX,
            minY: minY,
            maxY: maxY
        }

        var xScale;
        var yScale;

        //var width = graph.container.clientWidth;
        //var height = graph.container.clientHeight;

        switch (graph.xScale) {
            case "linear": xScale = d3.scale.linear().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
            case "ordinal": xScale = d3.scale.ordinal().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
            case "power": xScale = d3.scale.power().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
            case "log": xScale = d3.scale.log().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
        }

        switch (graph.yScale) {
            case "linear": yScale = d3.scale.linear().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
            case "ordinal": yScale = d3.scale.ordinal().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
            case "power": yScale = d3.scale.power().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
            case "log": yScale = d3.scale.log().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
        }

        var xAxis = d3.svg.axis().scale(xScale).orient(graph.xAxisOrient).ticks(5);
        graph.axisX.call(xAxis);

        var yAxis = d3.svg.axis().scale(yScale).orient(graph.yAxisOrient).ticks(5);
        graph.axisY.call(yAxis);

        graph.lineG.selectAll("path").remove();

        var lineFunction = d3.svg.line()
            .x(function (d) {
                return xScale(d.x);
            })
            .y(function (d) {
                return yScale(d.y);
            })
            .interpolate(graph.interpolation);

        for (var i = 0; i < graph.y.length; i++) {

            graph.lineG.append("path")
            .attr("d", lineFunction(displayData[i]))
            .attr("class", "graphLine")
            .attr("stroke", graph.y[i].color)
            .attr("stroke-width", 3)
            .attr("fill", "none");
        }

        this._UpdatePreview();
    }

    this._clickEvent = function (e) {
        var graph = e.currentTarget.graph;

        if (graph.visible)
        {
            graph._closeGraph();
        }
        else
        {
            graph._openGraph();
        }
    }

    this._closeGraph = function () {
        this.visible = false;
        GraphManager.RemoveGraph(this.graphID)
        L.DomUtil.removeClass(this.previewDiv, "chartPreviewActive");
    }

    this._openGraph = function () {
        this.visible = true;
        this._resetSize();
        GraphManager.AddGraph(this.graphObject.container);
        L.DomUtil.addClass(this.previewDiv, "chartPreviewActive");
    }

    this._resetSize = function () {
        var changed = false
        if (parseInt(this.graphObject.container.style.width) != this.graphObject.width) {
            this.graphObject.container.style.width = this.graphObject.width + "px";
            changed = true;
        }
        if (parseInt(this.graphObject.container.style.height) != this.graphObject.height) {
            this.graphObject.container.style.height = this.graphObject.height + "px";
            changed = true;
        }

        if (changed)
            this.Update();
    }
}<commit_msg>Fixed issue with graph labels not correctly being displayed<commit_after>function LineBottomLeft(graphObject) {
    this.graphObject = graphObject;
    this.visible = false;
    graphObject.preview = {};
    this.graphID = graphObject.id;
    this.previewDiv = null;
    this.Initialize = function (container) {
        var width = this.graphObject.width - GraphManager.defaultValues.axisMargin.y;
        var height = this.graphObject.height - GraphManager.defaultValues.axisMargin.x;
        var marginLeft = GraphManager.defaultValues.graphPadding.left + GraphManager.defaultValues.axisMargin.y;
        var marginTop = GraphManager.defaultValues.graphPadding.top;

        var svg = d3.select(container).append("svg")
        .attr("width", width)
        .attr("height", height);

        svg.className = "graph-svg";

        var lineG = svg.append("g");

        var text = null;

        if (typeof this.graphObject.name !== "undefined") {
            text = svg.append("text")
            .attr("x", (width / 2))
            .attr("y", marginTop)
            .attr("dy", 20 - GraphManager.defaultValues.graphPadding.top)
            .attr("text-anchor", "middle")
            .attr("pointer-events", "none")
            .attr("class", "graph-title-text")
            .style("font-size", "16px")
            .text(this.graphObject.name);
        }

        //generate x axis;
        var axisX = svg.append("g")
            .attr("class", "axis xAxis");
        this.graphObject.axisX = axisX;
        if (this.graphObject.x.label != "") {
            var axisXLabel = svg.append("text")
            .attr("class", "axisLabel xAxisLabel")
            .text(this.graphObject.x.label);
            this.graphObject.axisXLabel = axisXLabel;
        }

        //generate y axis
        var axisY = svg.append("g")
            .attr("class", "axis yAxis");
        this.graphObject.axisY = axisY;
        if (this.graphObject.y[0].label != "") { //todo check to see if there are different labels -> label lines instead of axis!
            var axisYLabel = svg.append("text")
            .attr("class", "axisLabel yAxisLabel")
            .text(this.graphObject.y[0].label);
            this.graphObject.axisYLabel = axisYLabel;
        }

        this.graphObject.container = container;
        this.graphObject.svg = svg;
        this.graphObject.data = [];
        this.graphObject.displayData = [];
        for (var i = 0; i < graphObject.y.length; i++) {
            this.graphObject.data[i] = [];
            this.graphObject.displayData[i] = [];
        }
        this.graphObject.lineG = lineG;
        this.graphObject.text = text;

        container.style.visibility = "hidden";
        container.graph = this;
        this.Update();
    }

    this._UpdatePreview = function () {

        if (this.previewDiv == null || this.graphObject.data == null || this.graphObject.data.length == 0)
            return;
        else {
            let found = false;
            for (var i = 0; i < this.graphObject.data.length; i++) {
                if (this.graphObject.data[i].length > 0)
                    found = true;
            }
            if (!found)
                return;
        }

        var graph = this.graphObject;

        var width = DataManager.detailsInfo.chartWidth;
        var height = DataManager.detailsInfo.chartHeight;

        var minX = graph.bounds.minX;
        var maxX = graph.bounds.maxX;
        var minY = graph.bounds.minY;
        var maxY = graph.bounds.maxY;

        var xScale;
        var yScale;

        //var width = graph.container.clientWidth;
        //var height = graph.container.clientHeight;

        switch (graph.xScale) {
            case "linear": xScale = d3.scale.linear().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
            case "ordinal": xScale = d3.scale.ordinal().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
            case "power": xScale = d3.scale.power().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
            case "log": xScale = d3.scale.log().domain([minX, maxX]).range([DataManager.detailsInfo.graphMargin, width - DataManager.detailsInfo.graphMargin]);
                break;
        }

        switch (graph.yScale) {
            case "linear": yScale = d3.scale.linear().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
            case "ordinal": yScale = d3.scale.ordinal().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
            case "power": yScale = d3.scale.power().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
            case "log": yScale = d3.scale.log().domain([minY, maxY]).range([height - DataManager.detailsInfo.graphMargin, DataManager.detailsInfo.graphMargin]);
                break;
        }

        graph.preview.lineG.selectAll("path").remove();

        var lineFunction = d3.svg.line()
            .x(function (d) { return xScale(d.x); })
            .y(function (d) { return yScale(d.y); })
            .interpolate(graph.interpolation);

        for (var i = 0; i < graph.y.length; i++) {

            graph.preview.lineG.append("path")
            .attr("d", lineFunction(graph.displayData[i]))
            .attr("class", "graphLine")
            .attr("stroke", graph.y[i].color)
            .attr('stroke-width', 2)
            .attr("fill", "none");
        }
    }

    this.GetPreview = function(container)
    {

        if (this.previewDiv != null)
            return container.appendChild(this.previewDiv);

        var previewContainer = container.appendChild(document.createElement("div"));
        previewContainer.className = "detailContainer graphDetails";
        previewContainer.style.width = DataManager.detailsInfo.chartWidth + "px";
        previewContainer.style.height = DataManager.detailsInfo.elementHeight + "px";
        //if (typeof this.graphObject.description !== "undefined")
        //    previewContainer.title = this.graphObject.description;

        this.previewDiv = previewContainer;
        previewContainer.graph = this;

        previewContainer.addEventListener("click", this._clickEvent);

        var title = previewContainer.appendChild(document.createElement("h4"));
        title.className = "detailTitle graphDetailTitle";
        title.textContent = this.graphObject.name;
        title.style.width = DataManager.detailsInfo.elementWidth + "px";

        var svgContainer = previewContainer.appendChild(document.createElement("div"));
        svgContainer.className = "preview-svg-container";
        svgContainer.style.width = DataManager.detailsInfo.chartWidth + "px";
        svgContainer.style.height = DataManager.detailsInfo.chartHeight + "px";

        var svg = d3.select(svgContainer).append("svg")
            .attr("width", DataManager.detailsInfo.chartWidth)
            .attr("height", DataManager.detailsInfo.chartHeight);

        svg.className = "graph-svg-preview";

        var lineG = svg.append("g");
        lineG.className = "graph-g-preview";

        this.graphObject.preview.container = previewContainer;
        this.graphObject.preview.svg = svg;
        this.graphObject.preview.lineG = lineG;

        this._UpdatePreview();

    }
    this.Update = function (data)
    {
        var graph = this.graphObject;

        //sets data and displaydata
        GraphManager.AddGraphData(graph, data);

        var width = graph.container.clientWidth;
        var height = graph.container.clientHeight;
        var marginLeft = GraphManager.defaultValues.graphPadding.left + GraphManager.defaultValues.axisMargin.y;
        var marginTop = GraphManager.defaultValues.graphPadding.top;
        var marginRight = GraphManager.defaultValues.graphPadding.right;
        var marginBottom = GraphManager.defaultValues.graphPadding.bottom + GraphManager.defaultValues.axisMargin.x;;

        graph.axisX.attr("transform", "translate(" + 0 + ", " + (height - marginBottom) + ")");

        graph.axisY.attr("transform", "translate(" + marginLeft + ", " + 0 + ")");

        //sets the labels //todo possible to remove this from Update and add it to initialization and resizing;
        if (typeof graph.axisXLabel !== "undefined") {
            var yTransform = height - (marginBottom + GraphManager.defaultValues.axisTextPadding);

            graph.axisXLabel.attr("text-anchor", "end");
            graph.axisXLabel.attr("x", (width - marginRight));
            graph.axisXLabel.attr("y", yTransform);
        }
        if (typeof graph.axisYLabel !== "undefined") {
            var xTransform = marginLeft + GraphManager.defaultValues.axisTextPadding;
            var rotation = 90;

            graph.axisYLabel.attr("text-anchor", "start")
            graph.axisYLabel.attr("x", xTransform);
            graph.axisYLabel.attr("y", marginTop);
            graph.axisYLabel.attr("transform", "rotate(" + rotation + "," + xTransform + "," + marginTop + ")");
        }

        graph.svg.attr("width", width);
        graph.svg.attr("height", height);

        if (graph.text != null)
            graph.text.attr("x", (width / 2));

        //gets the data to display
        var displayData = graph.displayData;
        //for (var i = Math.max(0, graph.data.length - graph.maxPoints) ; i < graph.data.length; i++) {
        //    yData = [graph.data[i].y[0]];
        //    for (var j = 1; j < graph.data[i].y.length; j++) {
        //        if (graph.additive)
        //            yData.push(graph.data[i].y[j] + yData[j - 1]);
        //        else
        //            yData.push(graph.data[i].y[j]);
        //    }
        //    displayData.push({ x: graph.data[i].x, y: yData });
        //}

        //graph.displayData = displayData;

        //sets min/max values
        var minX = (typeof graph.minX === "undefined") ? d3.min(displayData, function (d) { return d3.min(d, function (p) { return p.x; }) }) : graph.minX;
        var maxX = (typeof graph.maxX === "undefined") ? d3.max(displayData, function (d) { return d3.max(d, function (p) { return p.x; }) }) : graph.maxX;
        var minY = (typeof graph.minY === "undefined") ? d3.min(displayData, function (d) { return d3.min(d, function (p) { return p.y; }) }) : graph.minY;
        var maxY = (typeof graph.maxY === "undefined") ? d3.max(displayData, function (d) { return d3.max(d, function (p) { return p.y; }) }) : graph.maxY;
        if (graph.holdminmax) {
            if (typeof graph.holdvalues === "undefined") //todo fix possible missed values when new data.length > maxPoints
                graph.holdvalues = {
                    minY: minY,
                    maxY: maxY
                }
            else {
                minY = (minY > graph.holdvalues.minY) ? graph.holdvalues.minY : minY;
                maxY = (maxY < graph.holdvalues.maxY) ? graph.holdvalues.maxY : maxY;
            }
        }
        if (minX == maxX) {
            minX--;
            maxX++;
        }
        if (minY == maxY) {
            minY--;
            maxY++;
        }

        graph.bounds = {
            minX: minX,
            maxX: maxX,
            minY: minY,
            maxY: maxY
        }

        var xScale;
        var yScale;

        //var width = graph.container.clientWidth;
        //var height = graph.container.clientHeight;

        switch (graph.xScale) {
            case "linear": xScale = d3.scale.linear().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
            case "ordinal": xScale = d3.scale.ordinal().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
            case "power": xScale = d3.scale.power().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
            case "log": xScale = d3.scale.log().domain([minX, maxX]).range([marginLeft, width - marginRight]);
                break;
        }

        switch (graph.yScale) {
            case "linear": yScale = d3.scale.linear().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
            case "ordinal": yScale = d3.scale.ordinal().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
            case "power": yScale = d3.scale.power().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
            case "log": yScale = d3.scale.log().domain([minY, maxY]).range([height - marginBottom, marginTop]);
                break;
        }

        var xAxis = d3.svg.axis().scale(xScale).orient(graph.xAxisOrient).ticks(5);
        graph.axisX.call(xAxis);

        var yAxis = d3.svg.axis().scale(yScale).orient(graph.yAxisOrient).ticks(5);
        graph.axisY.call(yAxis);

        graph.lineG.selectAll("path").remove();

        var lineFunction = d3.svg.line()
            .x(function (d) {
                return xScale(d.x);
            })
            .y(function (d) {
                return yScale(d.y);
            })
            .interpolate(graph.interpolation);

        for (var i = 0; i < graph.y.length; i++) {

            graph.lineG.append("path")
            .attr("d", lineFunction(displayData[i]))
            .attr("class", "graphLine")
            .attr("stroke", graph.y[i].color)
            .attr("stroke-width", 3)
            .attr("fill", "none");
        }

        this._UpdatePreview();
    }

    this._clickEvent = function (e) {
        var graph = e.currentTarget.graph;

        if (graph.visible)
        {
            graph._closeGraph();
        }
        else
        {
            graph._openGraph();
        }
    }

    this._closeGraph = function () {
        this.visible = false;
        GraphManager.RemoveGraph(this.graphID)
        L.DomUtil.removeClass(this.previewDiv, "chartPreviewActive");
    }

    this._openGraph = function () {
        this.visible = true;
        this._resetSize();
        GraphManager.AddGraph(this.graphObject.container);
        L.DomUtil.addClass(this.previewDiv, "chartPreviewActive");
    }

    this._resetSize = function () {
        var changed = false
        if (parseInt(this.graphObject.container.style.width) != this.graphObject.width) {
            this.graphObject.container.style.width = this.graphObject.width + "px";
            changed = true;
        }
        if (parseInt(this.graphObject.container.style.height) != this.graphObject.height) {
            this.graphObject.container.style.height = this.graphObject.height + "px";
            changed = true;
        }

        if (changed)
            this.Update();
    }
}<|endoftext|>