<commit_before>'use strict';
var Promise = require('bluebird');
var auth = Promise.promisify(require('google-auth2-service-account').auth);
var scope = 'https://www.googleapis.com/auth/mapsengine';
var rawRequest = Promise.promisify(require('request'));
var Deque = require('double-ended-queue');
var fs = require('fs');
var Bulk = require('./bulk');
var limits = require('./limits.json');
var Stream = require('./stream');
var FeatureStream = require('./featureStream');
var readFile = Promise.promisify(fs.readFile, fs);
var TRUE = Promise.resolve(true);
module.exports = GME;
function GME(key, email, table, opts) {
  if (Buffer.isBuffer(key)) {
    this.key = Promise.resolve(key);
  } else {
    this.key = readFile(key);
  }
  if (typeof opts === 'string') {
    opts = {
      primary: opts
    };
  }
  this.iss = email;
  this.tableid = table;
  opts = opts || {};
  this.primary = opts.primary || 'gx_id';
  this.verbose = !!opts.verbose;
  this.nogeo = !!opts.nogeo;
  if (opts.trace) {
    this.trace = opts.trace;
  } else {
    this.trace = false;
  }
  this.baseurl = 'https://www.googleapis.com/mapsengine/v1/';
  this.queue = new Deque();
  this.inProgress = false;
  if (opts.limits && typeof opts.limits === 'object') {
    this.limits = opts.limits;
  } else if (typeof opts.limits === 'string') {
    if (limits[opts.limits]) {
      this.limits = limits[opts.limits];
    }
  }
}

GME.prototype._request = function (opts) {
  return rawRequest(opts).then(function (resp) {
    resp = resp[1];
    if (resp && resp.error) {
      throw resp.error;
    }
    return resp;
  });
};
GME.prototype.request = function (opts) {
  if (this.inProgress) {
    var resolver = Promise.defer();
    this.queue.push({
      resolver: resolver,
      opts: opts
    });
    return resolver.promise;
  }
  return this.createRequest(opts);
};
GME.prototype.createRequest = function (opts) {
  var self = this;
  this.inProgress = true;
  var tries = 1;
  function attemptDownload(){
    return self._request(opts).catch(function (err) {
      if (tries++ > 10 || err.code !== 403) {
        var error = new Error(err.message);
        error.code = err.code;
        error.errors = err.errors;
        if (self.verbose) {
          if (self.nogeo) {
            try {
              opts.body.features = opts.body.features.map(function (feature) {
                delete feature.geometry;
                return feature;
              });
            } catch(e) {}
          }
          console.log(JSON.stringify(opts, false, 4));
        }
        throw error;
      }
      return Promise.delay(tries * 1000).then(function () {
        return attemptDownload();
      });
    });
  }
  return attemptDownload().finally(function () {
    if (self.queue.length) {
      var next = self.queue.shift();
      next.resolver.resolve(self.createRequest(next.opts));
      return;
    }
    self.inProgress = false;
  });
};

GME.prototype.init = function () {
  var self = this;
  return this.key.then(function (key) {
    return auth(key, {
      iss: self.iss,
      scope: scope
    }).then(function (token) {
      if (self.timeout) {
        clearTimeout(self.timeout);
        self.timeout = null;
      }
      self.token = token;
      self.timeout = setTimeout(function () {
        self.token = null;
        self.timeout = null;
      }, 3600 * 1000);
      return token;
    });
  });
};
GME.prototype.auth = function () {
  if (this.token) {
    return Promise.resolve(this.token);
  } else {
    return this.init();
  }
};
GME.prototype.get = function (url, qs) {
  qs = qs || {};
  url = this.baseurl + url;
  var self = this;
  if (this.trace) {
    qs.trace = this.trace;
  }
  return this.auth().then(function (auth) {
    return self.request({
      url: url,
      headers: {
        Authorization: 'Bearer ' + auth
      },
      qs: qs,
      json: true
    });
  });
};
GME.prototype.post = function (url, body) {
  url = this.baseurl + url;
  var self = this;
  return this.auth().then(function (auth) {
    var opts = {
      url: url,
      headers: {
        Authorization: 'Bearer ' + auth
      },
      body: body,
      json: true,
      method: 'POST'
    };
    if (self.trace) {
      opts.qs = {
        trace: self.trace
      };
    }
    return self.request(opts);
  });
};
GME.prototype.info = function () {
  return this.get('tables/' + this.tableid);
};
GME.prototype.features = function (query) {
  var self = this;
  return new Promise(function (resolve, reject) {
    var out = {
      type: 'FeatureCollection',
      features: []
    };
    self.readStream(query).on('data', function (d) {
      out.features.push(d);
    }).on('finish', function () {
      resolve(out);
    }).on('error', reject);
  });
};
GME.prototype.featureStream = GME.prototype.readStream = function (query) {
  query = query || {};
  return new FeatureStream(this, query);
};
GME.prototype.feature = function (id) {
  return this.get('tables/' + this.tableid + '/features/' + id);
};
GME.prototype.create = function(arr) {
  if (this.verbose && arr.length) {
    console.log('create', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchInsert', {features: arr});
};
GME.prototype.update = function(arr) {
  if (this.verbose && arr.length) {
    console.log('update', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchPatch', {features: arr});
};
GME.prototype.remove = function(arr) {
  if (this.verbose && arr.length) {
    console.log('remove', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchDelete', {gx_ids: arr});
};

GME.prototype.bulk = function() {
  return new Bulk(this);
};
GME.prototype.writeStream = function() {
  return new Stream(this);
};<commit_msg>but give date<commit_after>'use strict';
var Promise = require('bluebird');
var auth = Promise.promisify(require('google-auth2-service-account').auth);
var scope = 'https://www.googleapis.com/auth/mapsengine';
var rawRequest = Promise.promisify(require('request'));
var Deque = require('double-ended-queue');
var fs = require('fs');
var Bulk = require('./bulk');
var limits = require('./limits.json');
var Stream = require('./stream');
var FeatureStream = require('./featureStream');
var readFile = Promise.promisify(fs.readFile, fs);
var TRUE = Promise.resolve(true);
module.exports = GME;
function GME(key, email, table, opts) {
  if (Buffer.isBuffer(key)) {
    this.key = Promise.resolve(key);
  } else {
    this.key = readFile(key);
  }
  if (typeof opts === 'string') {
    opts = {
      primary: opts
    };
  }
  this.iss = email;
  this.tableid = table;
  opts = opts || {};
  this.primary = opts.primary || 'gx_id';
  this.verbose = !!opts.verbose;
  this.nogeo = !!opts.nogeo;
  if (opts.trace) {
    this.trace = opts.trace;
  } else {
    this.trace = false;
  }
  this.baseurl = 'https://www.googleapis.com/mapsengine/v1/';
  this.queue = new Deque();
  this.inProgress = false;
  if (opts.limits && typeof opts.limits === 'object') {
    this.limits = opts.limits;
  } else if (typeof opts.limits === 'string') {
    if (limits[opts.limits]) {
      this.limits = limits[opts.limits];
    }
  }
}

GME.prototype._request = function (opts) {
  return rawRequest(opts).then(function (resp) {
    resp = resp[1];
    if (resp && resp.error) {
      throw resp.error;
    }
    return resp;
  });
};
GME.prototype.request = function (opts) {
  if (this.inProgress) {
    var resolver = Promise.defer();
    this.queue.push({
      resolver: resolver,
      opts: opts
    });
    return resolver.promise;
  }
  return this.createRequest(opts);
};
GME.prototype.createRequest = function (opts) {
  var self = this;
  this.inProgress = true;
  var tries = 1;
  function attemptDownload(){
    return self._request(opts).catch(function (err) {
      if (tries++ > 10 || err.code !== 403) {
        var error = new Error(err.message);
        error.code = err.code;
        error.errors = err.errors;
        if (self.verbose) {
          if (self.nogeo) {
            try {
              opts.body.features = opts.body.features.map(function (feature) {
                delete feature.geometry;
                return feature;
              });
            } catch(e) {}
          }
          console.log(new Date());
          console.log(JSON.stringify(opts, false, 4));
        }
        throw error;
      }
      return Promise.delay(tries * 1000).then(function () {
        return attemptDownload();
      });
    });
  }
  return attemptDownload().finally(function () {
    if (self.queue.length) {
      var next = self.queue.shift();
      next.resolver.resolve(self.createRequest(next.opts));
      return;
    }
    self.inProgress = false;
  });
};

GME.prototype.init = function () {
  var self = this;
  return this.key.then(function (key) {
    return auth(key, {
      iss: self.iss,
      scope: scope
    }).then(function (token) {
      if (self.timeout) {
        clearTimeout(self.timeout);
        self.timeout = null;
      }
      self.token = token;
      self.timeout = setTimeout(function () {
        self.token = null;
        self.timeout = null;
      }, 3600 * 1000);
      return token;
    });
  });
};
GME.prototype.auth = function () {
  if (this.token) {
    return Promise.resolve(this.token);
  } else {
    return this.init();
  }
};
GME.prototype.get = function (url, qs) {
  qs = qs || {};
  url = this.baseurl + url;
  var self = this;
  if (this.trace) {
    qs.trace = this.trace;
  }
  return this.auth().then(function (auth) {
    return self.request({
      url: url,
      headers: {
        Authorization: 'Bearer ' + auth
      },
      qs: qs,
      json: true
    });
  });
};
GME.prototype.post = function (url, body) {
  url = this.baseurl + url;
  var self = this;
  return this.auth().then(function (auth) {
    var opts = {
      url: url,
      headers: {
        Authorization: 'Bearer ' + auth
      },
      body: body,
      json: true,
      method: 'POST'
    };
    if (self.trace) {
      opts.qs = {
        trace: self.trace
      };
    }
    return self.request(opts);
  });
};
GME.prototype.info = function () {
  return this.get('tables/' + this.tableid);
};
GME.prototype.features = function (query) {
  var self = this;
  return new Promise(function (resolve, reject) {
    var out = {
      type: 'FeatureCollection',
      features: []
    };
    self.readStream(query).on('data', function (d) {
      out.features.push(d);
    }).on('finish', function () {
      resolve(out);
    }).on('error', reject);
  });
};
GME.prototype.featureStream = GME.prototype.readStream = function (query) {
  query = query || {};
  return new FeatureStream(this, query);
};
GME.prototype.feature = function (id) {
  return this.get('tables/' + this.tableid + '/features/' + id);
};
GME.prototype.create = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('create', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchInsert', {features: arr});
};
GME.prototype.update = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('update', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchPatch', {features: arr});
};
GME.prototype.remove = function(arr) {
  if (this.verbose && arr.length) {
    console.log(new Date());
    console.log('remove', arr.length);
  }
  if (!arr.length) {
    return TRUE;
  }
  return this.post('tables/' + this.tableid + '/features/batchDelete', {gx_ids: arr});
};

GME.prototype.bulk = function() {
  return new Bulk(this);
};
GME.prototype.writeStream = function() {
  return new Stream(this);
};<|endoftext|>


<commit_before>'use strict';

var Table = require('cli-table2'),
  chalk = require('chalk'),
  EOL = require("os").EOL;

function getNewTable() {
  return new Table({
    'style': {
      'head': ['blue'],
      'border': ['white']
    },
    colWidths: [20, 60, 10]
  });
}

function getColoredMessage(score, message) {
  let color = chalk.green;
  if (score >= 80 && score < 90) color = chalk.yellow;
  else if (score <= 70) color = chalk.red;
  return color(message ? message : score);
}

function getValue(value) {
  return (typeof value === 'object') ? JSON.stringify(value, null, ' ') : value;
}

class ResultTable {
  constructor(result, options) {
    this.result = result;
    this.options = options;
  }

  setupAdviceTable(type) {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse(type + ' advice')
    }]);

    let adviceList = this.result.coachAdvice.results[type].adviceList;
    let self = this;
    Object.keys(adviceList).forEach(function(advice) {
      let score = adviceList[advice].score;
      if (score < self.options.limit) {
        self.table.push([getColoredMessage(score, advice), adviceList[advice].advice.replace(/(.{57})/g, "$1" + EOL), getColoredMessage(score)]);
      }
    });
  }

  setupDefault() {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('The coach: Clear Eyes. Full Hearts. Can’t Lose.')
    }]);

    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse(this.result.coachAdvice.url)
    }]);

    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': this.options.browser + ' version'
    }]);
  }

  setupInfo() {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('INFORMATION')
    }]);
    let self = this;
    Object.keys(this.result.coachAdvice.results.info).forEach(function(key) {
      self.table.push([key, {
        'colSpan': 2,
        'content': getValue(self.result.coachAdvice.results.info[key])
      }]);
    });
  }

  setupTimings() {
    let self = this;
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('TIMINGS')
    }]);
    Object.keys(self.result.coachAdvice.results.timings).forEach(function(timing) {
      self.table.push([timing, {
        'colSpan': 2,
        'content': getValue(self.result.coachAdvice.results.timings[timing])
      }]);
    });
  }

  setupScoreTable() {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('Score per type')
    }]);
    this.table.push(['Advice', {
      'colSpan': 2,
      'content': 'Score'
    }]);
    this.table.push(
      ['Performance', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.performance.score)
      }], ['Best practice', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.bestpractice.score)
      }], ['Accessibility', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.accessibility.score)
      }], ['Average', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.score)
      }]
    );
  }

  generate() {
    this.table = getNewTable();
    this.setupDefault();
    this.setupInfo();
    this.setupTimings();
    this.setupAdviceTable('bestpractice');
    this.setupAdviceTable('accessibility');
    this.setupAdviceTable('performance');
    this.setupScoreTable();
    return this.table.toString();
  }

}

module.exports = ResultTable;
<commit_msg>lets make it yellow under 95<commit_after>'use strict';

var Table = require('cli-table2'),
  chalk = require('chalk'),
  EOL = require("os").EOL;

function getNewTable() {
  return new Table({
    'style': {
      'head': ['blue'],
      'border': ['white']
    },
    colWidths: [20, 60, 10]
  });
}

function getColoredMessage(score, message) {
  let color = chalk.green;
  if (score >= 80 && score <= 95) color = chalk.yellow;
  else if (score < 80) color = chalk.red;
  return color(message ? message : score);
}

function getValue(value) {
  return (typeof value === 'object') ? JSON.stringify(value, null, ' ') : value;
}

class ResultTable {
  constructor(result, options) {
    this.result = result;
    this.options = options;
  }

  setupAdviceTable(type) {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse(type + ' advice')
    }]);

    let adviceList = this.result.coachAdvice.results[type].adviceList;
    let self = this;
    Object.keys(adviceList).forEach(function(advice) {
      let score = adviceList[advice].score;
      if (score < self.options.limit) {
        self.table.push([getColoredMessage(score, advice), adviceList[advice].advice.replace(/(.{57})/g, "$1" + EOL), getColoredMessage(score)]);
      }
    });
  }

  setupDefault() {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('The coach: Clear Eyes. Full Hearts. Can’t Lose.')
    }]);

    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse(this.result.coachAdvice.url)
    }]);

    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': this.options.browser + ' version'
    }]);
  }

  setupInfo() {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('INFORMATION')
    }]);
    let self = this;
    Object.keys(this.result.coachAdvice.results.info).forEach(function(key) {
      self.table.push([key, {
        'colSpan': 2,
        'content': getValue(self.result.coachAdvice.results.info[key])
      }]);
    });
  }

  setupTimings() {
    let self = this;
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('TIMINGS')
    }]);
    Object.keys(self.result.coachAdvice.results.timings).forEach(function(timing) {
      self.table.push([timing, {
        'colSpan': 2,
        'content': getValue(self.result.coachAdvice.results.timings[timing])
      }]);
    });
  }

  setupScoreTable() {
    this.table.push([{
      'colSpan': 3,
      'hAlign': 'center',
      'content': chalk.inverse('Score per type')
    }]);
    this.table.push(['Advice', {
      'colSpan': 2,
      'content': 'Score'
    }]);
    this.table.push(
      ['Performance', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.performance.score)
      }], ['Best practice', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.bestpractice.score)
      }], ['Accessibility', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.accessibility.score)
      }], ['Average', {
        'colSpan': 2,
        'content': getColoredMessage(this.result.coachAdvice.results.score)
      }]
    );
  }

  generate() {
    this.table = getNewTable();
    this.setupDefault();
    this.setupInfo();
    this.setupTimings();
    this.setupAdviceTable('bestpractice');
    this.setupAdviceTable('accessibility');
    this.setupAdviceTable('performance');
    this.setupScoreTable();
    return this.table.toString();
  }

}

module.exports = ResultTable;
<|endoftext|>


<commit_before>/**
 * Class for handeling graph plotting of pulse data.
 * Still needs loads of work and is only a cut/paste from template for now
 */
var plot;
var GraphPlotter = {
    choiceContainer: null,
    datasets: null,
    backendUrl: null,
    max_hr: 200,
    markings: [],
    graphlabels: [],
    ranges: {},

    setRange: function(range) {
        this.ranges = range;
    },
    plot: function() {
        data = [];
        var that = this;
        var minIndex = null;
        var maxIndex = null;
        var min = null;
        var max = null;
        var xaxisattrs = {
            tickDecimals: 0,
            tickFormatter: this.xaxisformatter
        };
        if (this.ranges.xaxis != undefined) {
            min = this.ranges.xaxis.from;
            max = this.ranges.xaxis.to;
            xaxisattrs.min = min;
            xaxisattrs.max = max;

            for (dataset in this.datasets) {

                series = this.datasets[dataset]['data']

                for (k in series) {
                    if (min >= series[k][0]) {
                        minIndex = k;
                    }
                    if (max <= series[k][0]) {
                        maxIndex = k;
                        break;
                    }
                }
                break;
            }
        }
        $("#choices>button.active").each(function () {
            var key = $(this).attr("name");
            if (key && that.datasets[key]) {
                if (key == 'hr') {
                    //that.datasets[key]['constraints'] = that.hrconstraints;
                  that.datasets[key]['threshold']= that.newhrconstraints;
                }
                else if (key == 'altitude') {
                    //that.datasets[key]['constraints'] = segmentconstraints;
                }
                if (key != 'lon' && key != 'lat') { // lon and lat doesn't go in the graph
                    data.push(that.datasets[key]);
                }
            }
        });

        if (data.length > 0) {
            plot = $.plot($("#exercisegraph"), data, {
                series: {
                    lines: {
                        lineWidth: 2, // less cluttere
                    },
                    shadowSize: 3 //drawing is faster without shadoes
                },
                yaxes: [
                    { tickFormatter: axisformatters['speed'] },
                    { position: "right", min: 80, max: this.max_hr, tickFormatter: axisformatters['hr'] },
                    { position: "right", tickFormatter: axisformatters['power']},
                    { tickFormatter: axisformatters['altitude']} ,
                    { tickFormatter: axisformatters['temp']},
                    { tickFormatter: axisformatters['cadence']}
                    ],
                xaxis: xaxisattrs,
                legend: {
                    labelFormatter: function(label, series) {
                        // series is the series object for the label
                        return label;
                    },
                    position: 'se',
                    noColumns: 15
                },
                grid: {
                    hoverable: true,
                    clickable: true,
                    backgroundColor: { colors: ["#fff", "#eee"] },
                    markings: this.markings,
                    markingsLineWidth: 2,
                },
                crosshair: { mode: "x" },
                selection: { mode: "x" }
            });
        }

        if (minIndex != null && maxIndex != null) {
            if (typeof(Mapper) != "undefined")
                Mapper.loadGeoJSON(minIndex, maxIndex);

            var segment_link = $("#segment_add");
            segment_link.attr('href' , segment_link.attr('href')+ '&start=' + minIndex + '&stop=' + maxIndex);
            $("#segment_add").removeClass("hidden");

            //window.location.hash = 'graph-zoom-' + Math.round(min*10)/10 + '-' + Math.round(max*10)/10;
            window.location.hash = 'graph-zoom-' + min + '-' + max;

            $.getJSON(this.backendUrl, { start: minIndex, stop: maxIndex }, function (avgs) {
                var items = $("#averages ul .data");
                $("#averages h4").removeClass("hidden");

                $.each(items, function (i, elem) {
                    var classlist = elem.className.split(" ");
                    for (k in classlist) {
                        if (classlist[k] in avgs) {
                            var key = classlist[k];
                            var e = $(elem);
                            var val = avgs[key];
                            if (val) {
                                if (typeof(val) == 'number') {
                                    val = Math.round(val * 10) / 10;
                                }
                                e.text(val);
                                e.parents(".hidden").removeClass("hidden");
                                e.attr('title', key.replace(/_/g, ' '))
                                e.parents('li').attr('title', key.replace(/_/g, ' '))
                            }
                        }
                    }
                });
            });
        }
        else {
            if (typeof(Mapper) != "undefined")
                Mapper.loadGeoJSON(0, 0);
            $("#averages ul li").addClass("hidden");
            $("#averages h4").addClass("hidden");
        }

        this.drawGraphText();
    },
    updateLegend: function(pos) {
        this.updateLegendTimeout = null;
       // var pos = this.latestPosition;
        var axes = plot.getAxes();
        if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
            pos.y < axes.yaxis.min || pos.y > axes.yaxis.max)
            return;

        var i, j, dataset = plot.getData();
        for (i = 0; i < dataset.length; ++i) {
            var series = dataset[i];

            // find the nearest points, x-wise
            for (j = 0; j < series.data.length; ++j)
                if (series.data[j]) {
                    if (series.data[j][0] > pos.x)
                        break;
                }

            // now interpolate
            var y, p1 = series.data[j - 1], p2 = series.data[j];

            if (p1 == null)
                y = p2[1];
            else if (p2 == null)
                y = p1[1];
            else
                y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);

            this.legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
        }
    },
    showTooltip: function (x, y, contents) {
        $('
' + contents + '
').css( {
            position: 'absolute',
            display: 'none',
            top: y + 5,
            left: x + 5,
            padding: '10px',
            opacity: 0.9
        }).appendTo("body").delay(200).fadeIn(200);
    },
    addGraphText: function(x, text) {
        this.graphlabels.push([x,text] );
    },
    drawGraphText: function() {
        var ymax = plot.getAxes().yaxis.max;
        for (key in this.graphlabels) {
            var o= plot.pointOffset({ x: this.graphlabels[key][0], y: ymax});
            var placeholder = plot.getPlaceholder()
            placeholder.append('
' + this.graphlabels[key][1] + '
');
        }
    },
    init: function(args) {
        this.datasets = args.datasets;
        var backendUrl = args.backendUrl;
        this.max_hr = args.max_hr;
        this.markings = args.markings;
        this.xaxisformatter = axisformatters[args.xaxisformatter];
        this.posFeature = null;
        this.fullscreen = false;
        function evaluate(y,threshold) {
            return y < threshold;
        }
        this.hrconstraints = [{
                threshold: this.max_hr*0.6,
                color: colors[0],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.72,
                color: colors[1],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.82,
                color: colors[2],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.87,
                color: colors[3],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.92,
                color: colors[4],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.97,
                color: colors[5],
                evaluate : evaluate
        }]

        this.newhrconstraints = [{
                below: this.max_hr*0.6,
                color: colors[0]
            },
            {
                below: this.max_hr*0.72,
                color: colors[1]
            },
            {
                below: this.max_hr*0.82,
                color: colors[2]
            },
            {
                below: this.max_hr*0.87,
                color: colors[3]
            },
            {
                below: this.max_hr*0.92,
                color: colors[4]
            },
            {
                below: this.max_hr*0.97,
                color: colors[5]
        }]

        var that = this;
        this.backendUrl = backendUrl;
        this.choiceContainer = $("#choices");

        $.each(this.datasets, function(key, val) {
            var checked = 'checked=checked';
            if (key == 'cadence')
                checked = ''
            if (key == 'temp')
                checked = ''
            if (key == 'power')
                checked = ''

            if (key != 'lon' && key != 'lat') {

                that.choiceContainer.append('<button class="btn" name="' + key + '" ' + checked + ' id="chk_' + key + '">' + val.label + '</button>');
            }
        });
        $('#choices>button[checked]').button('toggle')
        $("#reset_zoom").bind("click", function(evt) {
            evt.preventDefault();
            $("#gtooltip").remove(); // tooltips messes up pos
            that.setRange({});
            that.plot();
            $('#reset_zoom').toggleClass('hide');
        });
        $(window).bind("keyup", function(evt) { if (evt.keyCode == 70) { $('#enlarge').click() } });
        $("#enlarge").bind("click", function(evt) {
            evt.preventDefault();
            $("#gtooltip").remove(); // tooltips messes up pos
            // Press escape to leave fullscreen
            $(window).bind("keyup", function(evt) { if (evt.keyCode == 27) { $('#enlarge').click() } });
            if(!that.fullscreen) {
                //$("#scrollhack").css("overflow", "scroll");
                //$("#exercisegraph").width( $("#exercisegraph").width()*4);
                $("#exercisegraph").appendTo('body');
                $("#exercisegraph").css("position", "absolute"); // Somehow needed, even though it's set in the css
                $("#exercisegraph").addClass('exercisegraphoverlay');
                $("#exercisegraph").removeClass('exercisegraph');
                $("#exercisegraph").css("overflow", "hidden");

                $("#averages").addClass('extraoverlay');
                $("#averages").appendTo('body');
                that.fullscreen = true;
            }
            else {
                $("#exercisegraph").removeClass('exercisegraphoverlay');
                $("#exercisegraph").addClass('exercisegraph');
                $("#exercisegraph").css("position", "relative"); // Somehow needed, even though it's set in the css
                $("#exercisegraph").appendTo($('#graphcontainer'));

                $("#averages").removeClass('extraoverlay');
                $("#averages").appendTo($('#graphcontainer'));
                that.fullscreen = false;
            }
        });
        this.choiceContainer.find("button").bind("click", function(evt) {
                evt.stopPropagation(); // Stop bootstrap event from firing
                $(this).toggleClass('active'); // Handle bootstrap event ourselves before we plot so class is set
                that.plot();
        });
        $('.legendColorBox > div').each(function(i){
            $(this).clone().prependTo(that.choiceContainer.find("li").eq(i));
        });

        this.legends = $(".legendLabel");
        this.legends.each(function () {
            // fix the widths so they don't jump around
            $(this).css('width', $(this).width());
        });

        var previousPoint = null;
        this.updateLegendTimeout = null;
        this.latestPosition = null;
        $("#exercisegraph").bind("plothover", function (event, pos, item) {
            that.latestPosition = pos;
  //          if (!that.updateLegendTimeout) that.updateLegendTimeout = setTimeout(that.updateLegend, 50);
            //that.updateLegend(pos);

            $("#gtooltip").remove();
            var axes = plot.getAxes();
            // Just retrn if we're hovering around outside the graph area
            if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
                pos.y < axes.yaxis.min || pos.y > axes.yaxis.max)
                return;

            var posIndex = 0;
            for (serieskey in that.datasets) { // Find first and best series that we got
                var series = that.datasets[serieskey]['data'];
                if (series == undefined) {
                    continue; // lon and lat dataseries does not have data object
                }
                console.log(serieskey);
                for (key in series) {
                    if (series[key][0] >= pos.x) {
                        var posIndex = key;
                        break;
                    }
                }
                break;
            }
            if (posIndex <= 0)  // No index found
                return;

            // Move marker to current pos
            if (Mapper.map != null || Mapper.posLayer != undefined) {
                var route_lon = that.datasets['lon'];
                var route_lat = that.datasets['lat'];
                if (route_lon.length >= posIndex) {
                    var x = route_lon[posIndex];
                    var y = route_lat[posIndex];
                    if (!this.posFeature) {
                        this.posFeature = Mapper.createFeature(Mapper.posLayer, x, y, 0);
                    }
                    // Mapper.updatePosMarker(x, y);
                    Mapper.moveFeature(this.posFeature, x, y, 0); // Hardcoded to 0 degrees
                }
            }

            var x = pos.x;
            var highlightedseries = null;
            if (item)
                highlightedseries = item.series.label;

            var tooltipHtml = '
'+ that.xaxisformatter(x, plot.getAxes().xaxis) + '

';
            for (skey in that.datasets) {
                if (skey == 'lon' || skey == 'lat')
                    continue;
                var series = that.datasets[skey];
                var label = series['label'];
                var tickFormatter = axisformatters[skey];
                if (skey == 'poweravg30s')
                    tickFormatter = axisformatters['power'];
                var val = series['data'][posIndex][1]; // Must fetch this from datasets rather thatn the graph data itself because of multiple treshold splits up the indexes
                if(tickFormatter != undefined)
                    val = tickFormatter(val, plot.getAxes().xaxis);
                var color = plot.getOptions().colors[series['color']];
                if(label == highlightedseries) // Highlight if item selected
                    skey += ' selected'
                tooltipHtml += '
' + label + ': ' + val + '
';
            }
            tooltipHtml += '
';
            that.showTooltip(pos.pageX, pos.pageY, tooltipHtml);

        });
        /*$("#exercisegraph").bind("plotclick", function (event, pos, item) {
            if (item) {
                plot.highlight(item.series, item.datapoint);
            }
            // reset zoom on click
            that.setRange({});
            that.plot();
        });*/

        $("#exercisegraph").bind("plotselected", function (event, ranges) {
            $('#reset_zoom.hide').toggleClass('hide');
            that.setRange(ranges);
            that.plot();
        });
    }
};

<commit_msg>remove debugging<commit_after>/**
 * Class for handeling graph plotting of pulse data.
 * Still needs loads of work and is only a cut/paste from template for now
 */
var plot;
var GraphPlotter = {
    choiceContainer: null,
    datasets: null,
    backendUrl: null,
    max_hr: 200,
    markings: [],
    graphlabels: [],
    ranges: {},

    setRange: function(range) {
        this.ranges = range;
    },
    plot: function() {
        data = [];
        var that = this;
        var minIndex = null;
        var maxIndex = null;
        var min = null;
        var max = null;
        var xaxisattrs = {
            tickDecimals: 0,
            tickFormatter: this.xaxisformatter
        };
        if (this.ranges.xaxis != undefined) {
            min = this.ranges.xaxis.from;
            max = this.ranges.xaxis.to;
            xaxisattrs.min = min;
            xaxisattrs.max = max;

            for (dataset in this.datasets) {

                series = this.datasets[dataset]['data']

                for (k in series) {
                    if (min >= series[k][0]) {
                        minIndex = k;
                    }
                    if (max <= series[k][0]) {
                        maxIndex = k;
                        break;
                    }
                }
                break;
            }
        }
        $("#choices>button.active").each(function () {
            var key = $(this).attr("name");
            if (key && that.datasets[key]) {
                if (key == 'hr') {
                    //that.datasets[key]['constraints'] = that.hrconstraints;
                  that.datasets[key]['threshold']= that.newhrconstraints;
                }
                else if (key == 'altitude') {
                    //that.datasets[key]['constraints'] = segmentconstraints;
                }
                if (key != 'lon' && key != 'lat') { // lon and lat doesn't go in the graph
                    data.push(that.datasets[key]);
                }
            }
        });

        if (data.length > 0) {
            plot = $.plot($("#exercisegraph"), data, {
                series: {
                    lines: {
                        lineWidth: 2, // less cluttere
                    },
                    shadowSize: 3 //drawing is faster without shadoes
                },
                yaxes: [
                    { tickFormatter: axisformatters['speed'] },
                    { position: "right", min: 80, max: this.max_hr, tickFormatter: axisformatters['hr'] },
                    { position: "right", tickFormatter: axisformatters['power']},
                    { tickFormatter: axisformatters['altitude']} ,
                    { tickFormatter: axisformatters['temp']},
                    { tickFormatter: axisformatters['cadence']}
                    ],
                xaxis: xaxisattrs,
                legend: {
                    labelFormatter: function(label, series) {
                        // series is the series object for the label
                        return label;
                    },
                    position: 'se',
                    noColumns: 15
                },
                grid: {
                    hoverable: true,
                    clickable: true,
                    backgroundColor: { colors: ["#fff", "#eee"] },
                    markings: this.markings,
                    markingsLineWidth: 2,
                },
                crosshair: { mode: "x" },
                selection: { mode: "x" }
            });
        }

        if (minIndex != null && maxIndex != null) {
            if (typeof(Mapper) != "undefined")
                Mapper.loadGeoJSON(minIndex, maxIndex);

            var segment_link = $("#segment_add");
            segment_link.attr('href' , segment_link.attr('href')+ '&start=' + minIndex + '&stop=' + maxIndex);
            $("#segment_add").removeClass("hidden");

            //window.location.hash = 'graph-zoom-' + Math.round(min*10)/10 + '-' + Math.round(max*10)/10;
            window.location.hash = 'graph-zoom-' + min + '-' + max;

            $.getJSON(this.backendUrl, { start: minIndex, stop: maxIndex }, function (avgs) {
                var items = $("#averages ul .data");
                $("#averages h4").removeClass("hidden");

                $.each(items, function (i, elem) {
                    var classlist = elem.className.split(" ");
                    for (k in classlist) {
                        if (classlist[k] in avgs) {
                            var key = classlist[k];
                            var e = $(elem);
                            var val = avgs[key];
                            if (val) {
                                if (typeof(val) == 'number') {
                                    val = Math.round(val * 10) / 10;
                                }
                                e.text(val);
                                e.parents(".hidden").removeClass("hidden");
                                e.attr('title', key.replace(/_/g, ' '))
                                e.parents('li').attr('title', key.replace(/_/g, ' '))
                            }
                        }
                    }
                });
            });
        }
        else {
            if (typeof(Mapper) != "undefined")
                Mapper.loadGeoJSON(0, 0);
            $("#averages ul li").addClass("hidden");
            $("#averages h4").addClass("hidden");
        }

        this.drawGraphText();
    },
    updateLegend: function(pos) {
        this.updateLegendTimeout = null;
       // var pos = this.latestPosition;
        var axes = plot.getAxes();
        if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
            pos.y < axes.yaxis.min || pos.y > axes.yaxis.max)
            return;

        var i, j, dataset = plot.getData();
        for (i = 0; i < dataset.length; ++i) {
            var series = dataset[i];

            // find the nearest points, x-wise
            for (j = 0; j < series.data.length; ++j)
                if (series.data[j]) {
                    if (series.data[j][0] > pos.x)
                        break;
                }

            // now interpolate
            var y, p1 = series.data[j - 1], p2 = series.data[j];

            if (p1 == null)
                y = p2[1];
            else if (p2 == null)
                y = p1[1];
            else
                y = p1[1] + (p2[1] - p1[1]) * (pos.x - p1[0]) / (p2[0] - p1[0]);

            this.legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
        }
    },
    showTooltip: function (x, y, contents) {
        $('
' + contents + '
').css( {
            position: 'absolute',
            display: 'none',
            top: y + 5,
            left: x + 5,
            padding: '10px',
            opacity: 0.9
        }).appendTo("body").delay(200).fadeIn(200);
    },
    addGraphText: function(x, text) {
        this.graphlabels.push([x,text] );
    },
    drawGraphText: function() {
        var ymax = plot.getAxes().yaxis.max;
        for (key in this.graphlabels) {
            var o= plot.pointOffset({ x: this.graphlabels[key][0], y: ymax});
            var placeholder = plot.getPlaceholder()
            placeholder.append('
' + this.graphlabels[key][1] + '
');
        }
    },
    init: function(args) {
        this.datasets = args.datasets;
        var backendUrl = args.backendUrl;
        this.max_hr = args.max_hr;
        this.markings = args.markings;
        this.xaxisformatter = axisformatters[args.xaxisformatter];
        this.posFeature = null;
        this.fullscreen = false;
        function evaluate(y,threshold) {
            return y < threshold;
        }
        this.hrconstraints = [{
                threshold: this.max_hr*0.6,
                color: colors[0],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.72,
                color: colors[1],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.82,
                color: colors[2],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.87,
                color: colors[3],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.92,
                color: colors[4],
                evaluate : evaluate
            },
            {
                threshold: this.max_hr*0.97,
                color: colors[5],
                evaluate : evaluate
        }]

        this.newhrconstraints = [{
                below: this.max_hr*0.6,
                color: colors[0]
            },
            {
                below: this.max_hr*0.72,
                color: colors[1]
            },
            {
                below: this.max_hr*0.82,
                color: colors[2]
            },
            {
                below: this.max_hr*0.87,
                color: colors[3]
            },
            {
                below: this.max_hr*0.92,
                color: colors[4]
            },
            {
                below: this.max_hr*0.97,
                color: colors[5]
        }]

        var that = this;
        this.backendUrl = backendUrl;
        this.choiceContainer = $("#choices");

        $.each(this.datasets, function(key, val) {
            var checked = 'checked=checked';
            if (key == 'cadence')
                checked = ''
            if (key == 'temp')
                checked = ''
            if (key == 'power')
                checked = ''

            if (key != 'lon' && key != 'lat') {

                that.choiceContainer.append('<button class="btn" name="' + key + '" ' + checked + ' id="chk_' + key + '">' + val.label + '</button>');
            }
        });
        $('#choices>button[checked]').button('toggle')
        $("#reset_zoom").bind("click", function(evt) {
            evt.preventDefault();
            $("#gtooltip").remove(); // tooltips messes up pos
            that.setRange({});
            that.plot();
            $('#reset_zoom').toggleClass('hide');
        });
        $(window).bind("keyup", function(evt) { if (evt.keyCode == 70) { $('#enlarge').click() } });
        $("#enlarge").bind("click", function(evt) {
            evt.preventDefault();
            $("#gtooltip").remove(); // tooltips messes up pos
            // Press escape to leave fullscreen
            $(window).bind("keyup", function(evt) { if (evt.keyCode == 27) { $('#enlarge').click() } });
            if(!that.fullscreen) {
                //$("#scrollhack").css("overflow", "scroll");
                //$("#exercisegraph").width( $("#exercisegraph").width()*4);
                $("#exercisegraph").appendTo('body');
                $("#exercisegraph").css("position", "absolute"); // Somehow needed, even though it's set in the css
                $("#exercisegraph").addClass('exercisegraphoverlay');
                $("#exercisegraph").removeClass('exercisegraph');
                $("#exercisegraph").css("overflow", "hidden");

                $("#averages").addClass('extraoverlay');
                $("#averages").appendTo('body');
                that.fullscreen = true;
            }
            else {
                $("#exercisegraph").removeClass('exercisegraphoverlay');
                $("#exercisegraph").addClass('exercisegraph');
                $("#exercisegraph").css("position", "relative"); // Somehow needed, even though it's set in the css
                $("#exercisegraph").appendTo($('#graphcontainer'));

                $("#averages").removeClass('extraoverlay');
                $("#averages").appendTo($('#graphcontainer'));
                that.fullscreen = false;
            }
        });
        this.choiceContainer.find("button").bind("click", function(evt) {
                evt.stopPropagation(); // Stop bootstrap event from firing
                $(this).toggleClass('active'); // Handle bootstrap event ourselves before we plot so class is set
                that.plot();
        });
        $('.legendColorBox > div').each(function(i){
            $(this).clone().prependTo(that.choiceContainer.find("li").eq(i));
        });

        this.legends = $(".legendLabel");
        this.legends.each(function () {
            // fix the widths so they don't jump around
            $(this).css('width', $(this).width());
        });

        var previousPoint = null;
        this.updateLegendTimeout = null;
        this.latestPosition = null;
        $("#exercisegraph").bind("plothover", function (event, pos, item) {
            that.latestPosition = pos;
  //          if (!that.updateLegendTimeout) that.updateLegendTimeout = setTimeout(that.updateLegend, 50);
            //that.updateLegend(pos);

            $("#gtooltip").remove();
            var axes = plot.getAxes();
            // Just retrn if we're hovering around outside the graph area
            if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max ||
                pos.y < axes.yaxis.min || pos.y > axes.yaxis.max)
                return;

            var posIndex = 0;
            for (serieskey in that.datasets) { // Find first and best series that we got
                var series = that.datasets[serieskey]['data'];
                if (series == undefined) {
                    continue; // lon and lat dataseries does not have data object
                }
                for (key in series) {
                    if (series[key][0] >= pos.x) {
                        var posIndex = key;
                        break;
                    }
                }
                break;
            }
            if (posIndex <= 0)  // No index found
                return;

            // Move marker to current pos
            if (Mapper.map != null || Mapper.posLayer != undefined) {
                var route_lon = that.datasets['lon'];
                var route_lat = that.datasets['lat'];
                if (route_lon.length >= posIndex) {
                    var x = route_lon[posIndex];
                    var y = route_lat[posIndex];
                    if (!this.posFeature) {
                        this.posFeature = Mapper.createFeature(Mapper.posLayer, x, y, 0);
                    }
                    // Mapper.updatePosMarker(x, y);
                    Mapper.moveFeature(this.posFeature, x, y, 0); // Hardcoded to 0 degrees
                }
            }

            var x = pos.x;
            var highlightedseries = null;
            if (item)
                highlightedseries = item.series.label;

            var tooltipHtml = '
'+ that.xaxisformatter(x, plot.getAxes().xaxis) + '

';
            for (skey in that.datasets) {
                if (skey == 'lon' || skey == 'lat')
                    continue;
                var series = that.datasets[skey];
                var label = series['label'];
                var tickFormatter = axisformatters[skey];
                if (skey == 'poweravg30s')
                    tickFormatter = axisformatters['power'];
                var val = series['data'][posIndex][1]; // Must fetch this from datasets rather thatn the graph data itself because of multiple treshold splits up the indexes
                if(tickFormatter != undefined)
                    val = tickFormatter(val, plot.getAxes().xaxis);
                var color = plot.getOptions().colors[series['color']];
                if(label == highlightedseries) // Highlight if item selected
                    skey += ' selected'
                tooltipHtml += '
' + label + ': ' + val + '
';
            }
            tooltipHtml += '
';
            that.showTooltip(pos.pageX, pos.pageY, tooltipHtml);

        });
        /*$("#exercisegraph").bind("plotclick", function (event, pos, item) {
            if (item) {
                plot.highlight(item.series, item.datapoint);
            }
            // reset zoom on click
            that.setRange({});
            that.plot();
        });*/

        $("#exercisegraph").bind("plotselected", function (event, ranges) {
            $('#reset_zoom.hide').toggleClass('hide');
            that.setRange(ranges);
            that.plot();
        });
    }
};

<|endoftext|>


<commit_before>/**
 * @flow
 */

import React, {Component} from 'react'
import {StyleSheet, Text, TouchableHighlight, LayoutAnimation} from 'react-native'
import reactMixin from 'react-mixin'
import TimerMixin from 'react-timer-mixin'

const DEMO_CLICK_DELAY = 1000
const DEMO_CLICK_INTERVAL = 700

export default class ToggleButton extends Component {
  static get defaultProps () {
    return {
      value: '-',
      color: '#a5d9e5',
      disabled: true,
      demo: false,
      demoPressSec: 0,
      onUp: function () {},
      onDown: function () {}
    }
  }

  static get propTypes () {
    return {
      value: React.PropTypes.string,
      color: React.PropTypes.string,
      disabled: React.PropTypes.bool,
      demo: React.PropTypes.bool,
      demoPressSec: React.PropTypes.number,
      onUp: React.PropTypes.func,
      onDown: React.PropTypes.func
    }
  }

  constructor (props) {
    super(props)

    this.state = {
      pressed: false
    }
  }

  componentDidMount () {
    if (this.props.demoPressSec > 0) {
      this.setTimeout(
        this.onPressButton.bind(this, true),
        DEMO_CLICK_DELAY + this.props.demoPressSec * DEMO_CLICK_INTERVAL
      )
    }
  }

  onPressButton (force) {
    if (this.props.demo && !force) {
      return
    }

    const pressed = !this.state.pressed
    LayoutAnimation.configureNext({
      duration: 200,
      update: {
        type: LayoutAnimation.Types.spring,
        property: LayoutAnimation.Properties.scaleXY,
        springDamping: 0.5
      }
    })

    this.setState({
      pressed: pressed
    })

    if (pressed) {
      this.props.onDown(this.props.value)
    } else {
      this.props.onUp(this.props.value)
    }
  }

  componentDidUpdate (prevProps, prevState) {
    if (prevState.pressed && prevProps.disabled && !this.props.disabled) {
      this.setState({
        pressed: false
      })
    }
  }

  render () {
    const {color, disabled} = this.props

    const styleText = [styles.text]
    let styleContainer = [styles.container]
    if (disabled) {
      styleText.push(styles.textDisabled)
      styleContainer.push(styles.containerDisabled)
    } else if (this.state.pressed) {
      styleContainer.push(styles.containerPressed)
    } else {
      styleContainer = {
        ...StyleSheet.flatten(styleContainer),
        ...{backgroundColor: color}
      }
    }

    return (
      <TouchableHighlight
        style={styleContainer}
        onPress={this.onPressButton.bind(this, false)}
        disabled={this.props.disabled}
        underlayColor={'gold'}>
        <Text style={styleText} allowFontScaling>
          {this.props.value}
        </Text>
      </TouchableHighlight>
    )
  }
}

reactMixin.onClass(ToggleButton, TimerMixin)

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#a5d9e5',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 7,
    padding: 5,
    paddingBottom: 9,
    margin: 15
  },
  containerPressed: {
    backgroundColor: '#FFDA31',
    borderRadius: 10,
    padding: 10,
    paddingBottom: 14,
    margin: 10
  },
  containerDisabled: {
    backgroundColor: 'lightgray',
    borderRadius: 3,
    margin: 25
  },
  text: {
    fontSize: 60,
    color: 'darkslategray'
  },
  textDisabled: {
    fontSize: 1,
    color: 'lightgray'
  }
})
<commit_msg>Adjustable buttons font size<commit_after>/**
 * @flow
 */

import React, {Component} from 'react'
import {StyleSheet, Text, TouchableHighlight, LayoutAnimation} from 'react-native'
import reactMixin from 'react-mixin'
import TimerMixin from 'react-timer-mixin'

const DEMO_CLICK_DELAY = 1000
const DEMO_CLICK_INTERVAL = 700

export default class ToggleButton extends Component {
  static get defaultProps () {
    return {
      value: '-',
      color: '#a5d9e5',
      disabled: true,
      demo: false,
      demoPressSec: 0,
      onUp: function () {},
      onDown: function () {}
    }
  }

  static get propTypes () {
    return {
      value: React.PropTypes.string,
      color: React.PropTypes.string,
      disabled: React.PropTypes.bool,
      demo: React.PropTypes.bool,
      demoPressSec: React.PropTypes.number,
      onUp: React.PropTypes.func,
      onDown: React.PropTypes.func
    }
  }

  constructor (props) {
    super(props)

    this.state = {
      pressed: false
    }
  }

  componentDidMount () {
    if (this.props.demoPressSec > 0) {
      this.setTimeout(
        this.onPressButton.bind(this, true),
        DEMO_CLICK_DELAY + this.props.demoPressSec * DEMO_CLICK_INTERVAL
      )
    }
  }

  onPressButton (force) {
    if (this.props.demo && !force) {
      return
    }

    const pressed = !this.state.pressed
    LayoutAnimation.configureNext({
      duration: 200,
      update: {
        type: LayoutAnimation.Types.spring,
        property: LayoutAnimation.Properties.scaleXY,
        springDamping: 0.5
      }
    })

    this.setState({
      pressed: pressed
    })

    if (pressed) {
      this.props.onDown(this.props.value)
    } else {
      this.props.onUp(this.props.value)
    }
  }

  componentDidUpdate (prevProps, prevState) {
    if (prevState.pressed && prevProps.disabled && !this.props.disabled) {
      this.setState({
        pressed: false
      })
    }
  }

  render () {
    const {color, disabled, value} = this.props

    let styleContainer = [styles.container]
    if (disabled) {
      styleContainer.push(styles.containerDisabled)
    } else if (this.state.pressed) {
      styleContainer.push(styles.containerPressed)
    } else {
      styleContainer = {
        ...StyleSheet.flatten(styleContainer),
        ...{backgroundColor: color}
      }
    }

    let styleText = [styles.text]
    if (disabled) {
      styleText.push(styles.textDisabled)
    } else {
      styleText = {
        ...StyleSheet.flatten(styleText),
        ...{fontSize: (120 / Math.max(value.length, 2))}
      }
    }

    return (
      <TouchableHighlight
        style={styleContainer}
        onPress={this.onPressButton.bind(this, false)}
        disabled={this.props.disabled}
        underlayColor={'gold'}>
        <Text style={styleText} allowFontScaling>
          {this.props.value}
        </Text>
      </TouchableHighlight>
    )
  }
}

reactMixin.onClass(ToggleButton, TimerMixin)

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#a5d9e5',
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 7,
    padding: 5,
    paddingBottom: 9,
    margin: 15
  },
  containerPressed: {
    backgroundColor: '#FFDA31',
    borderRadius: 10,
    padding: 10,
    paddingBottom: 14,
    margin: 10
  },
  containerDisabled: {
    backgroundColor: 'lightgray',
    borderRadius: 3,
    margin: 25
  },
  text: {
    color: 'darkslategray'
  },
  textDisabled: {
    fontSize: 1,
    color: 'lightgray'
  }
})
<|endoftext|>


<commit_before>(function (exports) {
    function urlsToAbsolute(nodeList) {
        if (!nodeList.length) {
            return [];
        }
        var attrName = 'href';
        if (nodeList[0].__proto__ === HTMLImageElement.prototype || nodeList[0].__proto__ === HTMLScriptElement.prototype) {
            attrName = 'src';
        }
        nodeList = [].map.call(nodeList, function (el, i) {
            var attr = el.getAttribute(attrName);
            if (!attr) {
                return;
            }
            var absURL = /^(https?|data):/i.test(attr);
            if (absURL) {
                return el;
            } else {
                return el;
            }
        });
        return nodeList;
    }

    function screenshotPage() {
        urlsToAbsolute(document.images);
        urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
        var screenshot = document.documentElement.cloneNode(true);
        var b = document.createElement('base');
        b.href = document.location.protocol + '//' + location.host;
        var head = screenshot.querySelector('head');
        head.insertBefore(b, head.firstChild);
        screenshot.style.pointerEvents = 'none';
        screenshot.style.overflow = 'hidden';
        screenshot.style.webkitUserSelect = 'none';
        screenshot.style.mozUserSelect = 'none';
        screenshot.style.msUserSelect = 'none';
        screenshot.style.oUserSelect = 'none';
        screenshot.style.userSelect = 'none';
        screenshot.dataset.scrollX = window.scrollX;
        screenshot.dataset.scrollY = window.scrollY;
        var script = document.createElement('script');
        script.textContent = '(' + addOnPageLoad_.toString() + ')();';
        screenshot.querySelector('body').appendChild(script);
        var blob = new Blob([screenshot.outerHTML], {
            type: 'jpeg/png/gif'
        });
        return blob;
    }

    function addOnPageLoad_() {
        window.addEventListener('DOMContentLoaded', function (e) {
            var scrollX = document.documentElement.dataset.scrollX || 0;
            var scrollY = document.documentElement.dataset.scrollY || 0;
            window.scrollTo(scrollX, scrollY);
        });
    }

    function generate() {
        window.URL = window.URL || window.webkitURL;
        window.open(window.URL.createObjectURL(screenshotPage()));
    }
    exports.screenshotPage = screenshotPage;
    exports.generate = generate;
})(window);
<commit_msg>adding snapshot functionalty<commit_after><|endoftext|>


<commit_before>/**
 * Created by Zsolt on 5/16/2014.
 *
 * http://localhost:8888/rest/external/executor/
 * http://localhost:8888/rest/external/executor/create/[validhash]
 *
 * THIS IS A THROW AWAY CODE AND IMPLEMENTATION.
 *
 * TEMPORARY CODE AND IMPLEMENTATION.
 *
 */

define(['logManager',
    'blob/BlobRunPluginClient',
    'blob/BlobFSBackend',
    'blob/BlobMetadata',
    'fs',
    'path',
    'unzip',
    'child_process',
    'minimatch'],
    function (logManager, BlobRunPluginClient, BlobFSBackend, BlobMetadata, fs, path, unzip, child_process, minimatch) {

    var walk = function(dir, done) {
        var results = [];
        fs.readdir(dir, function(err, list) {
            if (err) return done(err);
            var i = 0;
            (function next() {
                var file = list[i++];
                if (!file) return done(null, results);
                file = dir + '/' + file;
                fs.stat(file, function(err, stat) {
                    if (stat && stat.isDirectory()) {
                        walk(file, function(err, res) {
                            results = results.concat(res);
                            next();
                        });
                    } else {
                        results.push(file);
                        next();
                    }
                });
            })();
        });
    };

    var deleteFolderRecursive = function(path) {
        if( fs.existsSync(path) ) {
            fs.readdirSync(path).forEach(function(file,index){
                var curPath = path + "/" + file;
                if(fs.lstatSync(curPath).isDirectory()) { // recurse
                    deleteFolderRecursive(curPath);
                } else { // delete file
                    fs.unlinkSync(curPath);
                }
            });
            fs.rmdirSync(path);
        }
    };

    //here you can define global variables for your middleware
    var logger = logManager.create('REST-External-Executor'); //how to define your own logger which will use the global settings

    var executorBackend = null;

    var ExecutorBackend = function (parameters) {
        this.jobList = {};

        // FIXME: should we use HTTP here?
        var blobBackend = new BlobFSBackend();
        this.blobClient = new BlobRunPluginClient(blobBackend);

        this.sourceFilename = 'source.zip';
        this.executorConfigFilename = 'executor_config.json';

        this.workingDirectory = 'executor-temp';

        if (!fs.existsSync(this.workingDirectory)) {
            fs.mkdirSync(this.workingDirectory);
        }
    };

    ExecutorBackend.prototype.startJob = function (jobInfo) {
        // FIXME: This is the server side implementation of a dumb executor.

        var self = this;

        // TODO: create job
        // TODO: what if job is already running?

        this.jobList[jobInfo.hash] = jobInfo;

        // get metadata for hash
        self.blobClient.getMetadata(jobInfo.hash, function (err, metadata) {
            if (err) {
                logger.error(err);
                jobInfo.status = 'FAILED_TO_GET_SOURCE_METADATA';
                return;
            }

            if (metadata.contentType !== BlobMetadata.CONTENT_TYPES.COMPLEX) {
                jobInfo.status = 'FAILED_SOURCE_IS_NOT_COMPLEX';
                return;
            }

//            if (metadata.content.hasOwnProperty(self.executorConfigFilename)) {
//
//            } else {
//                jobInfo.status = 'FAILED_EXECUTOR_CONFIG_DOES_NOT_EXIST';
//                return;
//            }

            // download artifacts
            self.blobClient.getObject(jobInfo.hash, function (err, content) {
                if (err) {
                    logger.error('Failed obtaining job source content, err: ' + err.toString());
                    jobInfo.status = 'FAILED_SOURCE_COULD_NOT_BE_OBTAINED';
                    return;
                }

                var jobDir = path.normalize(path.join(self.workingDirectory, jobInfo.hash));

                if (!fs.existsSync(jobDir)) {
                    fs.mkdirSync(jobDir);
                }

                var zipPath = path.join(jobDir, self.sourceFilename);

                fs.writeFile(zipPath, content, function (err) {
                    if (err) {
                        logger.error('Failed creating source zip-file, err: ' + err.toString());
                        jobInfo.status = 'FAILED_CREATING_SOURCE_ZIP';
                        return;
                    }

                    // unzip downloaded file

                    var extract = unzip.Extract({ path: jobDir });
                    fs.createReadStream(zipPath).pipe(extract);

                    extract.on('close', function(err) {
                        if (err) {
                            logger.error(err);
                            jobInfo.status = 'FAILED_UNZIP';
                            return;
                        }

                        // delete downloaded file
                        fs.unlinkSync(zipPath);

                        var exec = child_process.exec;

                        jobInfo.startTime = new Date().toISOString();

                        // get cmd file dynamically from the this.executorConfigFilename file
                        fs.readFile(path.join(jobDir, self.executorConfigFilename), 'utf8', function (err, data) {
                            if (err) {
                                logger.error('Could not read ' + self.executorConfigFilename + ' err:' + err);
                                jobInfo.status = 'FAILED_EXECUTOR_CONFIG';
                                return;
                            }

                            var executorConfig = JSON.parse(data);
                            var cmd = executorConfig.cmd;

                            logger.debug('working directory: ' + jobDir + ' executing: ' + cmd);

                            var child = exec(cmd, {cwd: jobDir},
                                function (error, stdout, stderr) {

                                    jobInfo.finishTime = new Date().toISOString();

                                    logger.debug('stdout: ' + stdout);

                                    if (stderr) {
                                        logger.error('stderr: ' + stderr);
                                    }

                                    if (error !== null) {
                                        logger.error('exec error: ' + error);
                                        jobInfo.status = 'FAILED';
                                    }

                                    // TODO: save stderr and stdout to files.

                                    self.saveJobResults(jobInfo, jobDir, executorConfig);
                                });
                        });
                    });
                });
            });
        });
    };

    ExecutorBackend.prototype.saveJobResults = function (jobInfo, directory, executorConfig) {
        var self = this,
            i,
            jointArtifact = self.blobClient.createArtifact('jobInfo_resultSuperSetHash'),
            resultsArtifacts = [],
            error = '',
            counter,
            pendingStatus = 'SUCCESS',
            archiveFile,
            afterAllFilesVisited,
            addObjectHashesAndSaveArtifact;
        jobInfo.resultHashes = {};

        for (i = 0; i < executorConfig.resultArtifacts.length; i += 1) {
            resultsArtifacts.push(
                {
                    name: executorConfig.resultArtifacts[i].name,
                    artifact: self.blobClient.createArtifact(executorConfig.resultArtifacts[i].name),
                    patterns: executorConfig.resultArtifacts[i].resultPatterns instanceof Array ?
                        executorConfig.resultArtifacts[i].resultPatterns : [],
                    files: {}
                }
            );
        }
        counter = executorConfig.resultArtifacts.length;

        archiveFile = function (filename, filePath, callback) {
            jointArtifact.addFileAsSoftLink(filename, fs.createReadStream(filePath), function (err, hash) {
                var i;
                if (err) {
                    logger.error(err);
                    //console.log('Was error :' + err);
                    callback(err);
                } else {
                    // Add the file-hash to the results artifacts containing the filename.
                    //console.log('Filename added : ' + filename);
                    for (i = 0; i < resultsArtifacts.length; i += 1) {
                        if (resultsArtifacts[i].files[filename] === true) {
                            resultsArtifacts[i].files[filename] = hash;
                            //console.log('Replaced! filename: "' + filename + '", artifact "' + resultsArtifacts[i].name
                            //    + '" with hash: ' + hash);
                        }
                    }
                    callback(null);
                }
            });
        };

        addObjectHashesAndSaveArtifact = function (resultArtifact) {
            resultArtifact.artifact.addMetadataHashes(resultArtifact.files, function (err, hashes) {
                if (err) {
                    error += err;
                    pendingStatus = jobInfo.status = 'FAILED_TO_ADD_OBJECT_HASHES';
                    counter -= 1;
                    if (counter == 0) {
                        logger.error(error);
                        jobInfo.status = pendingStatus;
                    }
                    return;
                }
                resultArtifact.artifact.save(function (err, resultHash) {
                    if (err) {
                        error += err;
                        pendingStatus = 'FAILED_TO_SAVE_ARTIFACT';
                        counter -= 1;
                        if (counter == 0) {
                            logger.error(error);
                            jobInfo.status = pendingStatus;
                        }
                        return;
                    }
                    jobInfo.resultHashes[resultArtifact.name] = resultHash;
                    counter -= 1;
                    if (counter === 0) {
                        if (error) {
                            logger.error(error);
                        }
                        if (jobInfo.status === 'CREATED') {
                            jobInfo.status = pendingStatus;
                        }
                    }
                });
            });
        };

        afterAllFilesVisited = function () {
            jointArtifact.save(function (err, resultHash) {
                var i;
                if (err) {
                    logger.error(err);
                    jobInfo.status = 'FAILED_TO_SAVE_JOINT_ARTIFACT';
                } else {
                    // FIXME: This is synchronous
                    deleteFolderRecursive(directory);
                    jobInfo.resultSuperSetHash = resultHash;
                    for (i = 0; i < resultsArtifacts.length; i += 1) {
                        addObjectHashesAndSaveArtifact(resultsArtifacts[i]);
                    }
                }
            });
        };

        walk(directory, function (err, results) {
            var i, j, a,
                remaining = results.length,
                archive,
                filename,
                matched;
            //console.log('Walking the walk..');
            for (i = 0; i < results.length; i++) {
                archive = false;
                filename = path.relative(directory, results[i]).replace(/\\/g,'/');

                for (a = 0; a < resultsArtifacts.length; a += 1) {
                    if (resultsArtifacts[a].patterns.length === 0) {
                        //console.log('Matched! filename: "' + filename + '", artifact "' + resultsArtifacts[a].name + '"');
                        resultsArtifacts[a].files[filename] = true;
                        archive = true;
                    } else {
                        for (j = 0; j < resultsArtifacts[a].patterns.length; j += 1) {
                            matched = minimatch(filename, resultsArtifacts[a].patterns[j]);
                            if (matched) {
                                //console.log('Matched! filename: "' + filename + '", artifact "' + resultsArtifacts[a].name + '"');
                                resultsArtifacts[a].files[filename] = true;
                                archive = true;
                                break;
                            }
                        }
                    }
                }

                if (archive) {
                    archiveFile(filename, results[i], function (err) {
                        remaining -= 1;
                        if (remaining === 0) {
                            afterAllFilesVisited();
                        }
                    })
                } else {
                    // skip it
                    remaining -= 1;
                    //console.log('Archive was false - Remaining ' + remaining.toString());
                    if (remaining === 0) {
                        afterAllFilesVisited();
                    }
                }
            }
        });
    };

    ExecutorBackend.prototype.cancelJob = function () {

    };

    // this should be in a database
    var jobList = {};

    var JobInfo = function (parameters) {
        this.hash = parameters.hash;
        this.resultSuperSetHash = null;
        this.resultHashes = null;
        this.userId = [];
        this.status = 'CREATED'; // TODO: define a constant for this
        this.startTime = null;
        this.finishTime = null;
    };

    var ExecutorREST = function(req,res,next){
        //global config is accessible via webGMEGlobal.getConfig()
        var config = webGMEGlobal.getConfig();
        logger.debug('Executor request');

        var url = req.url.split('/');

        if (url.length === 2) {
            //next should be always called / the response should be sent otherwise this thread will stop without and end

            res.send(jobList);

            // TODO: send status
            // FIXME: this path will not be safe
//            res.sendfile(path.join('src', 'rest', 'executor', 'index2.html'), function (err) {
//                if (err) {
//                    logger.error(err);
//                    res.send(500);
//                }
//            });

        } else {

            switch (url[1]) {
                case "create":
                    ExecutorRESTCreate(req, res, next);
                    break;
                case "cancel":
                    ExecutorRESTCancel(req, res, next);
                    break;
                case "info":
                    ExecutorRESTInfo(req, res, next);
                    break;
                default:
                    res.send(500);
            }

        }

    };

    var ExecutorRESTCreate = function(req, res, next) {
        // TODO: accept only POST

        var url = req.url.split('/');

        if (url.length < 3 || !url[2]) {
            res.send(500);
        }

        var hash = url[2];
        var jobInfo = new JobInfo({hash:hash});

        // TODO: check if hash ok
        jobList[hash] = jobInfo;

        // TODO: get job description

        // TODO: schedule job

        if (!executorBackend) {
            executorBackend = new ExecutorBackend();
        }

        executorBackend.startJob(jobInfo);

        res.send(jobInfo);
    };

    var ExecutorRESTCancel = function(req, res, next) {
        // TODO: accept only POST
        var url = req.url.split('/');

        if (url.length < 3 || !url[2]) {
            res.send(500);
        }

        var hash = url[2];

        if (!executorBackend) {
            res.send(500);
        } else {

            executorBackend.cancelJob(hash);

            res.send(200);
        }
    };

    var ExecutorRESTInfo = function(req, res, next) {
        // TODO: accept only GET

        var url = req.url.split('/');

        if (url.length < 3 || !url[2]) {
            res.send(500);
        }

        var hash = url[2];

        if (hash) {
            if (jobList.hasOwnProperty(hash)) {
                res.send(jobList[hash]);
            } else {
                res.send(500);
            }
        } else {
            res.send(jobList);
        }
    };

    var setup = function() { //it has to be done this way, but this is probably a placeholder for later option parameters...
        return ExecutorREST;
    };
    return setup();
});<commit_msg>Cleaner implementation of saving Jobs in executor. Works but needs to be tested on bigger models.<commit_after>/**
 * Created by Zsolt on 5/16/2014.
 *
 * http://localhost:8888/rest/external/executor/
 * http://localhost:8888/rest/external/executor/create/[validhash]
 *
 * THIS IS A THROW AWAY CODE AND IMPLEMENTATION.
 *
 * TEMPORARY CODE AND IMPLEMENTATION.
 *
 */

define(['logManager',
    'blob/BlobRunPluginClient',
    'blob/BlobFSBackend',
    'blob/BlobMetadata',
    'fs',
    'path',
    'unzip',
    'child_process',
    'minimatch'],
    function (logManager, BlobRunPluginClient, BlobFSBackend, BlobMetadata, fs, path, unzip, child_process, minimatch) {

    var walk = function(dir, done) {
        var results = [];
        fs.readdir(dir, function(err, list) {
            if (err) return done(err);
            var i = 0;
            (function next() {
                var file = list[i++];
                if (!file) return done(null, results);
                file = dir + '/' + file;
                fs.stat(file, function(err, stat) {
                    if (stat && stat.isDirectory()) {
                        walk(file, function(err, res) {
                            results = results.concat(res);
                            next();
                        });
                    } else {
                        results.push(file);
                        next();
                    }
                });
            })();
        });
    };

    var deleteFolderRecursive = function(path) {
        if( fs.existsSync(path) ) {
            fs.readdirSync(path).forEach(function(file,index){
                var curPath = path + "/" + file;
                if(fs.lstatSync(curPath).isDirectory()) { // recurse
                    deleteFolderRecursive(curPath);
                } else { // delete file
                    fs.unlinkSync(curPath);
                }
            });
            fs.rmdirSync(path);
        }
    };

    //here you can define global variables for your middleware
    var logger = logManager.create('REST-External-Executor'); //how to define your own logger which will use the global settings

    var executorBackend = null;

    var ExecutorBackend = function (parameters) {
        this.jobList = {};

        // FIXME: should we use HTTP here?
        var blobBackend = new BlobFSBackend();
        this.blobClient = new BlobRunPluginClient(blobBackend);

        this.sourceFilename = 'source.zip';
        this.executorConfigFilename = 'executor_config.json';

        this.workingDirectory = 'executor-temp';

        if (!fs.existsSync(this.workingDirectory)) {
            fs.mkdirSync(this.workingDirectory);
        }
    };

    ExecutorBackend.prototype.startJob = function (jobInfo) {
        // FIXME: This is the server side implementation of a dumb executor.

        var self = this;

        // TODO: create job
        // TODO: what if job is already running?

        this.jobList[jobInfo.hash] = jobInfo;

        // get metadata for hash
        self.blobClient.getMetadata(jobInfo.hash, function (err, metadata) {
            if (err) {
                logger.error(err);
                jobInfo.status = 'FAILED_TO_GET_SOURCE_METADATA';
                return;
            }

            if (metadata.contentType !== BlobMetadata.CONTENT_TYPES.COMPLEX) {
                jobInfo.status = 'FAILED_SOURCE_IS_NOT_COMPLEX';
                return;
            }

//            if (metadata.content.hasOwnProperty(self.executorConfigFilename)) {
//
//            } else {
//                jobInfo.status = 'FAILED_EXECUTOR_CONFIG_DOES_NOT_EXIST';
//                return;
//            }

            // download artifacts
            self.blobClient.getObject(jobInfo.hash, function (err, content) {
                if (err) {
                    logger.error('Failed obtaining job source content, err: ' + err.toString());
                    jobInfo.status = 'FAILED_SOURCE_COULD_NOT_BE_OBTAINED';
                    return;
                }

                var jobDir = path.normalize(path.join(self.workingDirectory, jobInfo.hash));

                if (!fs.existsSync(jobDir)) {
                    fs.mkdirSync(jobDir);
                }

                var zipPath = path.join(jobDir, self.sourceFilename);

                fs.writeFile(zipPath, content, function (err) {
                    if (err) {
                        logger.error('Failed creating source zip-file, err: ' + err.toString());
                        jobInfo.status = 'FAILED_CREATING_SOURCE_ZIP';
                        return;
                    }

                    // unzip downloaded file

                    var extract = unzip.Extract({ path: jobDir });
                    fs.createReadStream(zipPath).pipe(extract);

                    extract.on('close', function(err) {
                        if (err) {
                            logger.error(err);
                            jobInfo.status = 'FAILED_UNZIP';
                            return;
                        }

                        // delete downloaded file
                        fs.unlinkSync(zipPath);

                        var exec = child_process.exec;

                        jobInfo.startTime = new Date().toISOString();

                        // get cmd file dynamically from the this.executorConfigFilename file
                        fs.readFile(path.join(jobDir, self.executorConfigFilename), 'utf8', function (err, data) {
                            if (err) {
                                logger.error('Could not read ' + self.executorConfigFilename + ' err:' + err);
                                jobInfo.status = 'FAILED_EXECUTOR_CONFIG';
                                return;
                            }

                            var executorConfig = JSON.parse(data);
                            var cmd = executorConfig.cmd;

                            logger.debug('working directory: ' + jobDir + ' executing: ' + cmd);

                            var child = exec(cmd, {cwd: jobDir},
                                function (error, stdout, stderr) {

                                    jobInfo.finishTime = new Date().toISOString();

                                    logger.debug('stdout: ' + stdout);

                                    if (stderr) {
                                        logger.error('stderr: ' + stderr);
                                    }

                                    if (error !== null) {
                                        logger.error('exec error: ' + error);
                                        jobInfo.status = 'FAILED';
                                    }

                                    // TODO: save stderr and stdout to files.

                                    self.saveJobResults(jobInfo, jobDir, executorConfig);
                                });
                        });
                    });
                });
            });
        });
    };

    ExecutorBackend.prototype.saveJobResults = function (jobInfo, directory, executorConfig) {
        var self = this,
            i,
            jointArtifact = self.blobClient.createArtifact('jobInfo_resultSuperSetHash'),
            resultsArtifacts = [],
            afterWalk,
            archiveFile,
            afterAllFilesArchived,
            addObjectHashesAndSaveArtifact;
        jobInfo.resultHashes = {};

        for (i = 0; i < executorConfig.resultArtifacts.length; i += 1) {
            resultsArtifacts.push(
                {
                    name: executorConfig.resultArtifacts[i].name,
                    artifact: self.blobClient.createArtifact(executorConfig.resultArtifacts[i].name),
                    patterns: executorConfig.resultArtifacts[i].resultPatterns instanceof Array ?
                        executorConfig.resultArtifacts[i].resultPatterns : [],
                    files: {}
                }
            );
        }

        afterWalk = function (filesToArchive) {
            var counter,
                pendingStatus,
                i,
                counterCallback = function (err) {
                    if (err) {
                        pendingStatus = err;
                    }
                    counter -= 1;
                    if (counter <= 0) {
                        if (pendingStatus) {
                            jobInfo.status = pendingStatus;
                        } else {
                            afterAllFilesArchived();
                        }

                    }
                };
            counter = filesToArchive.length;
            if (counter === 0) {
                counterCallback(null);
            }
            for (i = 0; i < filesToArchive.length; i += 1) {
                archiveFile(filesToArchive[i].filename, filesToArchive[i].filePath, counterCallback);
            }
        };

        archiveFile = function (filename, filePath, callback) {

            jointArtifact.addFileAsSoftLink(filename, fs.createReadStream(filePath), function (err, hash) {
                var j;
                if (err) {
                    logger.error(err);
                    callback('FAILED_TO_ARCHIVE_FILE');
                } else {
                    // Add the file-hash to the results artifacts containing the filename.
                    //console.log('Filename added : ' + filename);
                    for (j = 0; j < resultsArtifacts.length; j += 1) {
                        if (resultsArtifacts[j].files[filename] === true) {
                            resultsArtifacts[j].files[filename] = hash;
                            //console.log('Replaced! filename: "' + filename + '", artifact "' + resultsArtifacts[j].name
                            //    + '" with hash: ' + hash);
                        }
                    }
                    callback(null);
                }
            });
        };

        afterAllFilesArchived = function () {
            jointArtifact.save(function (err, resultHash) {
                var counter,
                    pendingStatus,
                    i,
                    counterCallback;
                if (err) {
                    logger.error(err);
                    jobInfo.status = 'FAILED_TO_SAVE_JOINT_ARTIFACT';
                } else {
                    counterCallback = function (err) {
                        if (err) {
                            pendingStatus = err;
                        }
                        counter -= 1;
                        if (counter <= 0) {
                            if (pendingStatus) {
                                jobInfo.status = pendingStatus;
                            } else {
                                jobInfo.status = 'SUCCESS';
                            }
                        }
                    };
                    counter = resultsArtifacts.length;
                    if (counter === 0) {
                        counterCallback(null);
                    }
                    // FIXME: This is synchronous
                    deleteFolderRecursive(directory);
                    jobInfo.resultSuperSetHash = resultHash;
                    for (i = 0; i < resultsArtifacts.length; i += 1) {
                        addObjectHashesAndSaveArtifact(resultsArtifacts[i], counterCallback);
                    }
                }
            });
        };

        addObjectHashesAndSaveArtifact = function (resultArtifact, callback) {
            resultArtifact.artifact.addMetadataHashes(resultArtifact.files, function (err, hashes) {
                if (err) {
                    logger.error(err);
                    return callback('FAILED_TO_ADD_OBJECT_HASHES');
                }
                resultArtifact.artifact.save(function (err, resultHash) {
                    if (err) {
                        logger.error(err);
                        return callback('FAILED_TO_SAVE_ARTIFACT');
                    }
                    jobInfo.resultHashes[resultArtifact.name] = resultHash;
                    callback(null);
                });
            });
        };

        walk(directory, function (err, results) {
            var i, j, a,
                filesToArchive = [],
                archive,
                filename,
                matched;
            //console.log('Walking the walk..');
            for (i = 0; i < results.length; i++) {
                filename = path.relative(directory, results[i]).replace(/\\/g,'/');
                archive = false;
                for (a = 0; a < resultsArtifacts.length; a += 1) {
                    if (resultsArtifacts[a].patterns.length === 0) {
                        //console.log('Matched! filename: "' + filename + '", artifact "' + resultsArtifacts[a].name + '"');
                        resultsArtifacts[a].files[filename] = true;
                        archive = true;
                    } else {
                        for (j = 0; j < resultsArtifacts[a].patterns.length; j += 1) {
                            matched = minimatch(filename, resultsArtifacts[a].patterns[j]);
                            if (matched) {
                                //console.log('Matched! filename: "' + filename + '", artifact "' + resultsArtifacts[a].name + '"');
                                resultsArtifacts[a].files[filename] = true;
                                archive = true;
                                break;
                            }
                        }
                    }
                }
                if (archive) {
                    filesToArchive.push({ filename: filename, filePath: results[i]});
                }
            }
            afterWalk(filesToArchive);
        });
    };

    ExecutorBackend.prototype.cancelJob = function () {

    };

    // this should be in a database
    var jobList = {};

    var JobInfo = function (parameters) {
        this.hash = parameters.hash;
        this.resultSuperSetHash = null;
        this.resultHashes = null;
        this.userId = [];
        this.status = 'CREATED'; // TODO: define a constant for this
        this.startTime = null;
        this.finishTime = null;
    };

    var ExecutorREST = function(req,res,next){
        //global config is accessible via webGMEGlobal.getConfig()
        var config = webGMEGlobal.getConfig();
        logger.debug('Executor request');

        var url = req.url.split('/');

        if (url.length === 2) {
            //next should be always called / the response should be sent otherwise this thread will stop without and end

            res.send(jobList);

            // TODO: send status
            // FIXME: this path will not be safe
//            res.sendfile(path.join('src', 'rest', 'executor', 'index2.html'), function (err) {
//                if (err) {
//                    logger.error(err);
//                    res.send(500);
//                }
//            });

        } else {

            switch (url[1]) {
                case "create":
                    ExecutorRESTCreate(req, res, next);
                    break;
                case "cancel":
                    ExecutorRESTCancel(req, res, next);
                    break;
                case "info":
                    ExecutorRESTInfo(req, res, next);
                    break;
                default:
                    res.send(500);
            }

        }

    };

    var ExecutorRESTCreate = function(req, res, next) {
        // TODO: accept only POST

        var url = req.url.split('/');

        if (url.length < 3 || !url[2]) {
            res.send(500);
        }

        var hash = url[2];
        var jobInfo = new JobInfo({hash:hash});

        // TODO: check if hash ok
        jobList[hash] = jobInfo;

        // TODO: get job description

        // TODO: schedule job

        if (!executorBackend) {
            executorBackend = new ExecutorBackend();
        }

        executorBackend.startJob(jobInfo);

        res.send(jobInfo);
    };

    var ExecutorRESTCancel = function(req, res, next) {
        // TODO: accept only POST
        var url = req.url.split('/');

        if (url.length < 3 || !url[2]) {
            res.send(500);
        }

        var hash = url[2];

        if (!executorBackend) {
            res.send(500);
        } else {

            executorBackend.cancelJob(hash);

            res.send(200);
        }
    };

    var ExecutorRESTInfo = function(req, res, next) {
        // TODO: accept only GET

        var url = req.url.split('/');

        if (url.length < 3 || !url[2]) {
            res.send(500);
        }

        var hash = url[2];

        if (hash) {
            if (jobList.hasOwnProperty(hash)) {
                res.send(jobList[hash]);
            } else {
                res.send(500);
            }
        } else {
            res.send(jobList);
        }
    };

    var setup = function() { //it has to be done this way, but this is probably a placeholder for later option parameters...
        return ExecutorREST;
    };
    return setup();
});<|endoftext|>