<commit_before>// A Solver keeps track of context for a depth-first search.
function Solver(heuristic, moves, cb, deadline, depth) {
  this.heuristic = heuristic;
  this.moves = moves;
  this.cb = cb;
  this.deadline = deadline;
  this.depth = depth;

  // this.basis caches 18 Move objects to avoid allocation.
  this.basis = [];
  for (var i = 0; i < 18; ++i) {
    this.basis[i] = new Move(i);
  }

  // this.preAllocCubes caches a cube per level of depth in the search.
  this.preAllocCubes = [];
  for (var i = 0; i < depth; ++i) {
    this.preAllocCubes[i] = new Phase1Cube();
  }

  // this.solution is used to track the current solution; using this allows us
  // to avoid memory allocation in some browsers.
  this.solution = [];
  for (var i = 0; i < depth; ++i) {
    this.solution[i] = new Move(0);
  }
}

// deepen increases the depth of this solver by 1.
Solver.prototype.deepen = function() {
  var i = this.depth;
  this.depth++;
  this.preAllocCubes[i] = new Phase1Cube();
  this.solution[i] = new Move(0);
};

// solve finds solutions for the cube.
Solver.prototype.solve = function(cube) {
  return this._search(cube, 0, 0);
}

Solver.prototype._expired = function() {
  return new Date().getTime() > this.deadline;
};

Solver.prototype._search = function(cube, depth, lastFace) {
  if (depth === this.depth) {
    if (cube.anySolved()) {
      if (this._expired()) {
        return false;
      }
      return this.cb(this.solution.slice(), cube.copy());
    }
    return true;
  } else if (this.heuristic.lowerBound(cube) > this.depth - depth) {
    return true;
  }

  var newCube = this.preAllocCubes[depth];
  for (var i = 0; i < 18; ++i) {
    var face = (i % 6) + 1;
    if (face === lastFace) {
      continue;
    }

    // Get the cube which results from applying the given move.
    var move = this.basis[i];
    newCube.set(cube);
    newCube.move(move, this.moves);

    // Recurse one level deeper, setting the move in the solution buffer.
    this.solution[depth] = move;
    if (!this._search(newCube, depth+1, face)) {
      return false;
    }

    // this._expired allocates memory and thus consumes time. Luckily, short
    // circuit evaluation makes this a three-liner.
    if (this._depth - depth >= 7 && this._expired()) {
      return false;
    }
  }

  return true;
};

// solvePhase1 uses iterative deepening to solve the cube.
// The cb argument is a callback which receives two arguments, a solution and a
// Phase1Cube, for each solution. If the callback returns true, the search will
// continue. If it returns false, the search will stop.
// The timeout argument represents the number of milliseconds after which the
// solver should stop. This is 1000000 by default.
function solvePhase1(cube, heuristic, moves, cb, timeout) {
  var deadline = new Date().getTime() + (timeout || 1000000);
  var depth = 0;
  var solver = new Solver(heuristic, moves, cb, deadline, 0);
  while (true) {
    if (!solver.solve(cube)) {
      return;
    }
    solver.deepen();
    ++depth;
  }
}

exports.solvePhase1 = solvePhase1;
<commit_msg>avoid some redundant phase-1 solutions<commit_after>// A Solver keeps track of context for a depth-first search.
function Solver(heuristic, moves, cb, deadline, depth) {
  this.heuristic = heuristic;
  this.moves = moves;
  this.cb = cb;
  this.deadline = deadline;
  this.depth = depth;

  // this.basis caches 18 Move objects to avoid allocation.
  this.basis = [];
  for (var i = 0; i < 18; ++i) {
    this.basis[i] = new Move(i);
  }

  // this.preAllocCubes caches a cube per level of depth in the search.
  this.preAllocCubes = [];
  for (var i = 0; i < depth; ++i) {
    this.preAllocCubes[i] = new Phase1Cube();
  }

  // this.solution is used to track the current solution; using this allows us
  // to avoid memory allocation in some browsers.
  this.solution = [];
  for (var i = 0; i < depth; ++i) {
    this.solution[i] = new Move(0);
  }
}

// deepen increases the depth of this solver by 1.
Solver.prototype.deepen = function() {
  var i = this.depth;
  this.depth++;
  this.preAllocCubes[i] = new Phase1Cube();
  this.solution[i] = new Move(0);
};

// solve finds solutions for the cube.
Solver.prototype.solve = function(cube) {
  return this._search(cube, 0, 0, -1);
}

Solver.prototype._expired = function() {
  return new Date().getTime() > this.deadline;
};

Solver.prototype._search = function(cube, depth, lastFace, lastAxis) {
  if (depth === this.depth) {
    if (cube.anySolved()) {
      if (this._expired()) {
        return false;
      }
      return this.cb(this.solution.slice(), cube.copy());
    }
    return true;
  } else if (this.heuristic.lowerBound(cube) > this.depth - depth) {
    return true;
  }

  var newCube = this.preAllocCubes[depth];
  for (var i = 0; i < 18; ++i) {
    var face = (i % 6) + 1;
    var axis = (face - 1) >>> 1;
    if (face === lastFace) {
      continue;
    } else if (axis === lastAxis && face >= lastFace) {
      // Avoid redundancies like L R L' and enforce an ordering (e.g. there can
      // be a solution with L R, but not one with R L).
      continue;
    }

    // Get the cube which results from applying the given move.
    var move = this.basis[i];
    newCube.set(cube);
    newCube.move(move, this.moves);

    // Recurse one level deeper, setting the move in the solution buffer.
    this.solution[depth] = move;
    if (!this._search(newCube, depth+1, face, axis)) {
      return false;
    }

    // this._expired allocates memory and thus consumes time. Luckily, short
    // circuit evaluation makes this a three-liner.
    if (this._depth - depth >= 7 && this._expired()) {
      return false;
    }
  }

  return true;
};

// solvePhase1 uses iterative deepening to solve the cube.
// The cb argument is a callback which receives two arguments, a solution and a
// Phase1Cube, for each solution. If the callback returns true, the search will
// continue. If it returns false, the search will stop.
// The timeout argument represents the number of milliseconds after which the
// solver should stop. This is 1000000 by default.
function solvePhase1(cube, heuristic, moves, cb, timeout) {
  var deadline = new Date().getTime() + (timeout || 1000000);
  var depth = 0;
  var solver = new Solver(heuristic, moves, cb, deadline, 0);
  while (true) {
    if (!solver.solve(cube)) {
      return;
    }
    solver.deepen();
    ++depth;
  }
}

exports.solvePhase1 = solvePhase1;
<|endoftext|>


<commit_before>
var myLoadedMetrics = [];
var myMatchedMetrics = [];
var mySearchIntervalId = null;

// Apply 'selected' class to metrics in our array
// need this for when we refresh
var selectActiveMetrics = function() {
  var selectedMetrics = Object.keys(myMetrics);
  for (var id in selectedMetrics) {
    $('.metrics div#' + selectedMetrics[id]).removeClass('default').addClass('selected');
  }
};

// Populate our preview graph with selected metrics
var renderPreviewGraph = function() {
  if (Object.keys(myMetrics).length > 0) {
    var selectedMetrics = Object.keys(myMetrics);
    var targets = [];
    for (var id in selectedMetrics) {
      targets.push(myMetrics[selectedMetrics[id]]);
    }
    myGraph = {
      targets: targets,
      options: '&hideLegend=false'
    };
    $('span#metrics_graph_preview').html('');
  } else {
    $('span#metrics_graph_preview').html('Click any metrics below');
  }
};

// Add the options toolbar to each metric
var addMetricsToolbar = function(metric) {
  $('.metrics .rickshaw_graph#' + metric).append('

');
  $('.metrics .rickshaw_graph#' + metric + ' .options').
    html('12');
};

// Grab configuration blob and construct our graph urls
var renderGraphs = function() {
  // Load index.json at first load and cache as myLoadedMetrics
  // Use cached myLoadedMetrics for infinite scrolling
  if (myLoadedMetrics.length > 0) {
    // Use cached myLoadedMetrics
    renderSparklines();
  } else {
    // Load index.json and cache in myLoadedMetrics
    return $.ajax({
      accepts: {json: 'application/json'},
      cache: false,
      dataType: 'json',
      error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
      url: graphiteUrl + '/metrics/index.json'
    }).done(function(d) {
      if (d.length === 0) {
        console.log('No metrics found');
      } else {
        myLoadedMetrics = d;
        renderSparklines();
      }
    });
  }
};

var renderSparklines = function() {
  // Show our loading div
  $('div.loading').removeClass('hidden');

  if (myMatchedMetrics.length === 0) {
    myMatchedMetrics = myLoadedMetrics;
  }

  for (var target = ((myPageIndex - 1) * myMetricsPerPage); target < (myPageIndex * myMetricsPerPage); target += 1) {
    // Only run if we have an actual target, ignore end of page undefs
    if (myMatchedMetrics[target] !== undefined) {
      hash = CryptoJS.SHA256(myMatchedMetrics[target]).toString(CryptoJS.enc.Hex);
      $('div.metrics').append('

');
      $.ajax({
        accepts: {jason: 'application/json'},
        cache: false,
        hash: hash,
        target: myMatchedMetrics[target],
        dataType: 'jsonp',
        jsonp: 'jsonp',
        error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
        url: graphiteUrl + '/render?target=' + myMatchedMetrics[target] + '&from=-' + myInterval + '&format=json'
      }).done(function(output) {
        if (output.length === 1) {
          var data = [];
          for (var i in output[0].datapoints) {
            data[i] = { x: output[0].datapoints[i][1], y: output[0].datapoints[i][0] || 0 };
          }
          var hash = this.hash;
          var graph = new Rickshaw.Graph({
            element: document.getElementById(hash),
            height: 25,
            width: 400,
            renderer: 'area',
            series: [{ data: data }]
          });
          graph.render();
          $('.metrics div#' + hash).append('');
          $('.metrics div#' + hash + ' span.target').text(this.target);
          addMetricsToolbar(hash);

          // Restore our visual state
          selectActiveColumnButton();
          selectActiveIntervalButton();
          selectActiveMetrics();
          renderPreviewGraph();

          // Hide our loading div
          $('div.loading').addClass('hidden');

          // Clear search timer and bind infinite-scroll pagination
          clearInterval(mySearchIntervalId);
          $(window).on('scroll', scrollNextPage);

        } else {
          console.log("no data returned");
        }
      });
    }
  }
  // Disable infinite scrolling
  if (myPageIndex * myMetricsPerPage > myMatchedMetrics.length) {
    myPageIndex = 0;
  }
};

// Invert metrics_graph button when opened
$('div.well').on('click', 'a.metrics_graph.btn.closed', function() {
  $(this).text('Cancel Graph').removeClass('closed').addClass('open').addClass('btn-inverse');
  // Populate graph selector
  // XXX TODO - load existing graph configuration into preview
  // XXX TODO - allow appending new metrics to existing graph
  /* return $.ajax({
    accepts: {json: 'application/json'},
    cache: false,
    dataType: 'json',
    error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
    url: '/graphs'
  }).done(function(d) {
    for (var graph in d) {
      $('select[name=metrics_graph_select]').append('<option value="' + d[graph]['uuid'] + '">' + d[graph]['name'] + '</option>');
    }
  }); */
});

// Revert metrics_graph button when closed
$('div.well').on('click', 'a.metrics_graph.btn.open', function() {
  $(this).text('Add to Graph').removeClass('open').addClass('closed').removeClass('btn-inverse');
});

// Real-time filter renders sparklines when keystroke timer exceeds 500ms
$(document).on('keyup', 'input.search-ahead', function() {
  clearInterval(mySearchIntervalId);
  clearGraphs();
  var mySearchString = $(this).val();
  myMatchedMetrics = [];
  for (var i in myLoadedMetrics) {
    if (myLoadedMetrics[i].match(mySearchString)) {
      myMatchedMetrics.push(myLoadedMetrics[i]);
    }
  }
  if (myMatchedMetrics.length === 0) {
    console.log("no matches found");
  } else {
    mySearchIntervalId = setInterval(renderSparklines, 500);
  }
});

// Add metrics to array of items to graph
$('.metrics').on('click', 'div.rickshaw_graph.default', function() {
  var metric = {
    name: $(this).children('span.target').attr('id'),
    id: $(this).attr('id'),
    axis: 1
  };
  myMetrics[metric.id] = metric;
  $(this).removeClass('default').addClass('selected');
  $('button.metrics_graph_submit').removeClass('disabled');
  $('input.new-graph-name').removeAttr('disabled');
  renderPreviewGraph();
  return false;
});

// Set per-metric axis
$('.metrics').on('click', '.rickshaw_graph.selected .options a.axis', function(e) {
  e.stopImmediatePropagation();
  axis = $(this).html();
  id = $(this).parent('div.options').parent('div.rickshaw_graph.selected').attr('id');
  myMetrics[id].axis = parseInt(axis);
  $('.metrics .rickshaw_graph.selected#' + id + ' .options a.axis').removeClass('selected');
  $($('.metrics .rickshaw_graph.selected#' + id + ' .options a.axis')[axis - 1]).addClass('selected');
  renderPreviewGraph();
  return false;
});

// Remove metrics from array of items to graph
$('.metrics').on('click', 'div.rickshaw_graph.selected', function() {
  delete myMetrics[$(this).attr('id')];
  $(this).find('.options a.axis').removeClass('selected');
  $(this).find('.options a.axis.left').addClass('selected');
  $(this).removeClass('selected').addClass('default');
  if (myMetrics.length === undefined) {
    $('button.metrics_graph_submit').addClass('disabled');
    $('input.new-graph-name').attr('disabled', true);
  }
  renderPreviewGraph();
  return false;
});

// Create new graph on submit
$('#metrics_graph_select ul li').on('click', 'button.metrics_graph_submit', function() {
  var name = $(this).parent('li').parent('ul').find('input.new-graph-name').val();
  var url = $('span.graph#metrics_graph_preview img').attr('src');
  submitGraph({node: url, name: name}, function(graph) {
    window.location.href = '/graphs/' + graph.uuid
  });
  return false;
});

// Create new graph on enter
$('#metrics_graph_select ul li').on('keypress', 'input.new-graph-name', function(e) {
  if (e.which === 13) {
    var name = $(this).parent('li').parent('ul').find('input.new-graph-name').val();
    var url = $('span.graph#metrics_graph_preview img').attr('src');
    submitGraph({node: url, name: name}, function(graph) {
      window.location.href = '/graphs/' + graph.uuid
    });
  }
});

// Paginate when we hit the bottom of the page
var scrollNextPage = function() {
  // Do not paginate under the following conditions:
  //   * we're already at the end of the document (myPageIndex === 0)
  //   * we're searching on tags (myTags.length > 0)
  if ((myPageIndex > 0) && (myTags.length === 0)) {
    if ($(window).scrollTop() + $(window).height() == $(document).height()) {
      var now = +(new Date);
      var threshold = 2000;
      var delta = now - this.lastCall;
      if (!this.lastCall || (delta > threshold)) {
        this.lastCall = now;
        myPageIndex += 1;
        console.log("loading more metrics, myPageIndex is now " + myPageIndex);
        renderGraphs();
      } else {
        console.log("throttling");
      }
    }
  }
};

<commit_msg>add stroke to metric sparklines<commit_after>
var myLoadedMetrics = [];
var myMatchedMetrics = [];
var mySearchIntervalId = null;

// Apply 'selected' class to metrics in our array
// need this for when we refresh
var selectActiveMetrics = function() {
  var selectedMetrics = Object.keys(myMetrics);
  for (var id in selectedMetrics) {
    $('.metrics div#' + selectedMetrics[id]).removeClass('default').addClass('selected');
  }
};

// Populate our preview graph with selected metrics
var renderPreviewGraph = function() {
  if (Object.keys(myMetrics).length > 0) {
    var selectedMetrics = Object.keys(myMetrics);
    var targets = [];
    for (var id in selectedMetrics) {
      targets.push(myMetrics[selectedMetrics[id]]);
    }
    myGraph = {
      targets: targets,
      options: '&hideLegend=false'
    };
    $('span#metrics_graph_preview').html('');
  } else {
    $('span#metrics_graph_preview').html('Click any metrics below');
  }
};

// Add the options toolbar to each metric
var addMetricsToolbar = function(metric) {
  $('.metrics .rickshaw_graph#' + metric).append('

');
  $('.metrics .rickshaw_graph#' + metric + ' .options').
    html('12');
};

// Grab configuration blob and construct our graph urls
var renderGraphs = function() {
  // Load index.json at first load and cache as myLoadedMetrics
  // Use cached myLoadedMetrics for infinite scrolling
  if (myLoadedMetrics.length > 0) {
    // Use cached myLoadedMetrics
    renderSparklines();
  } else {
    // Load index.json and cache in myLoadedMetrics
    return $.ajax({
      accepts: {json: 'application/json'},
      cache: false,
      dataType: 'json',
      error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
      url: graphiteUrl + '/metrics/index.json'
    }).done(function(d) {
      if (d.length === 0) {
        console.log('No metrics found');
      } else {
        myLoadedMetrics = d;
        renderSparklines();
      }
    });
  }
};

var renderSparklines = function() {
  // Show our loading div
  $('div.loading').removeClass('hidden');

  if (myMatchedMetrics.length === 0) {
    myMatchedMetrics = myLoadedMetrics;
  }

  for (var target = ((myPageIndex - 1) * myMetricsPerPage); target < (myPageIndex * myMetricsPerPage); target += 1) {
    // Only run if we have an actual target, ignore end of page undefs
    if (myMatchedMetrics[target] !== undefined) {
      hash = CryptoJS.SHA256(myMatchedMetrics[target]).toString(CryptoJS.enc.Hex);
      $('div.metrics').append('

');
      $.ajax({
        accepts: {jason: 'application/json'},
        cache: false,
        hash: hash,
        target: myMatchedMetrics[target],
        dataType: 'jsonp',
        jsonp: 'jsonp',
        error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
        url: graphiteUrl + '/render?target=' + myMatchedMetrics[target] + '&from=-' + myInterval + '&format=json'
      }).done(function(output) {
        if (output.length === 1) {
          var data = [];
          for (var i in output[0].datapoints) {
            data[i] = { x: output[0].datapoints[i][1], y: output[0].datapoints[i][0] || 0 };
          }
          var hash = this.hash;
          var graph = new Rickshaw.Graph({
            element: document.getElementById(hash),
            height: 25,
            width: 400,
            renderer: 'area',
            stroke: true,
            series: [{ data: data, stroke: '#546c84' }]
          });
          graph.render();
          $('.metrics div#' + hash).append('');
          $('.metrics div#' + hash + ' span.target').text(this.target);
          addMetricsToolbar(hash);

          // Restore our visual state
          selectActiveColumnButton();
          selectActiveIntervalButton();
          selectActiveMetrics();
          renderPreviewGraph();

          // Hide our loading div
          $('div.loading').addClass('hidden');

          // Clear search timer and bind infinite-scroll pagination
          clearInterval(mySearchIntervalId);
          $(window).on('scroll', scrollNextPage);

        } else {
          console.log("no data returned");
        }
      });
    }
  }
  // Disable infinite scrolling
  if (myPageIndex * myMetricsPerPage > myMatchedMetrics.length) {
    myPageIndex = 0;
  }
};

// Invert metrics_graph button when opened
$('div.well').on('click', 'a.metrics_graph.btn.closed', function() {
  $(this).text('Cancel Graph').removeClass('closed').addClass('open').addClass('btn-inverse');
  // Populate graph selector
  // XXX TODO - load existing graph configuration into preview
  // XXX TODO - allow appending new metrics to existing graph
  /* return $.ajax({
    accepts: {json: 'application/json'},
    cache: false,
    dataType: 'json',
    error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
    url: '/graphs'
  }).done(function(d) {
    for (var graph in d) {
      $('select[name=metrics_graph_select]').append('<option value="' + d[graph]['uuid'] + '">' + d[graph]['name'] + '</option>');
    }
  }); */
});

// Revert metrics_graph button when closed
$('div.well').on('click', 'a.metrics_graph.btn.open', function() {
  $(this).text('Add to Graph').removeClass('open').addClass('closed').removeClass('btn-inverse');
});

// Real-time filter renders sparklines when keystroke timer exceeds 500ms
$(document).on('keyup', 'input.search-ahead', function() {
  clearInterval(mySearchIntervalId);
  clearGraphs();
  var mySearchString = $(this).val();
  myMatchedMetrics = [];
  for (var i in myLoadedMetrics) {
    if (myLoadedMetrics[i].match(mySearchString)) {
      myMatchedMetrics.push(myLoadedMetrics[i]);
    }
  }
  if (myMatchedMetrics.length === 0) {
    console.log("no matches found");
  } else {
    mySearchIntervalId = setInterval(renderSparklines, 500);
  }
});

// Add metrics to array of items to graph
$('.metrics').on('click', 'div.rickshaw_graph.default', function() {
  var metric = {
    name: $(this).children('span.target').attr('id'),
    id: $(this).attr('id'),
    axis: 1
  };
  myMetrics[metric.id] = metric;
  $(this).removeClass('default').addClass('selected');
  $('button.metrics_graph_submit').removeClass('disabled');
  $('input.new-graph-name').removeAttr('disabled');
  renderPreviewGraph();
  return false;
});

// Set per-metric axis
$('.metrics').on('click', '.rickshaw_graph.selected .options a.axis', function(e) {
  e.stopImmediatePropagation();
  axis = $(this).html();
  id = $(this).parent('div.options').parent('div.rickshaw_graph.selected').attr('id');
  myMetrics[id].axis = parseInt(axis);
  $('.metrics .rickshaw_graph.selected#' + id + ' .options a.axis').removeClass('selected');
  $($('.metrics .rickshaw_graph.selected#' + id + ' .options a.axis')[axis - 1]).addClass('selected');
  renderPreviewGraph();
  return false;
});

// Remove metrics from array of items to graph
$('.metrics').on('click', 'div.rickshaw_graph.selected', function() {
  delete myMetrics[$(this).attr('id')];
  $(this).find('.options a.axis').removeClass('selected');
  $(this).find('.options a.axis.left').addClass('selected');
  $(this).removeClass('selected').addClass('default');
  if (myMetrics.length === undefined) {
    $('button.metrics_graph_submit').addClass('disabled');
    $('input.new-graph-name').attr('disabled', true);
  }
  renderPreviewGraph();
  return false;
});

// Create new graph on submit
$('#metrics_graph_select ul li').on('click', 'button.metrics_graph_submit', function() {
  var name = $(this).parent('li').parent('ul').find('input.new-graph-name').val();
  var url = $('span.graph#metrics_graph_preview img').attr('src');
  submitGraph({node: url, name: name}, function(graph) {
    window.location.href = '/graphs/' + graph.uuid
  });
  return false;
});

// Create new graph on enter
$('#metrics_graph_select ul li').on('keypress', 'input.new-graph-name', function(e) {
  if (e.which === 13) {
    var name = $(this).parent('li').parent('ul').find('input.new-graph-name').val();
    var url = $('span.graph#metrics_graph_preview img').attr('src');
    submitGraph({node: url, name: name}, function(graph) {
      window.location.href = '/graphs/' + graph.uuid
    });
  }
});

// Paginate when we hit the bottom of the page
var scrollNextPage = function() {
  // Do not paginate under the following conditions:
  //   * we're already at the end of the document (myPageIndex === 0)
  //   * we're searching on tags (myTags.length > 0)
  if ((myPageIndex > 0) && (myTags.length === 0)) {
    if ($(window).scrollTop() + $(window).height() == $(document).height()) {
      var now = +(new Date);
      var threshold = 2000;
      var delta = now - this.lastCall;
      if (!this.lastCall || (delta > threshold)) {
        this.lastCall = now;
        myPageIndex += 1;
        console.log("loading more metrics, myPageIndex is now " + myPageIndex);
        renderGraphs();
      } else {
        console.log("throttling");
      }
    }
  }
};

<|endoftext|>


<commit_before>var HueLight = function (hueConnect, index, data) {

    this.rawdata = data;
    this.hueConnect = hueConnect;

    this.lightIndex = index;
    this.name = data.name;

    this.hue = data.state.hue;
    this.sat = data.state.sat;
    this.bri = data.state.bri;
    this.on = data.state.on;
    this.reachable = data.state.reachable;

    this.getName = function () {
        return this.name;
    };

    this.getIndex = function() {
        return this.lightIndex;
    };

    this.getOn = function() {
        if (!this.reachable) return false;
        return this.on;
    };

    this.getCurrentColorAsHSB = function () {
        var hsb = {h: 0, s: 0, b: 0};
        hsb.h = Math.round(this.hue / 65535 * 360);
        hsb.s = Math.round(this.sat / 255 * 100);
        hsb.b = Math.round(this.bri / 255 * 100);
        return hsb;
    };

    this.getCurrentColorAsRGB = function () {
        var hsb = this.getCurrentColorAsHSB();
        var rgb = this.hsb2rgb(hsb);
        return rgb;
    };

    this.setOn = function(on) {
        var response = this.hueConnect.api('lights/' + this.lightIndex + "/state", {'on': on});
        this.updateResponseData(response);
        $(window).trigger('hue-change', this);
    };

    this.setColorRGB = function (r, g, b) {
        console.log('Set Lamp '+this.getIndex()+' "'+this.getName()+'" to rgb('+r+','+g+','+b+')');
        var hsb = this.rgb2hsb({r: r, g: g, b: b})
        this.setColorHSB(hsb.h, hsb.s, hsb.b);
        return this;
    };

    this.setColorHSB = function (h, s, b) {
        console.log('Set Lamp '+this.getIndex()+' "'+this.getName()+'" to hsb('+h+','+s+','+b+')');
        // dim the values around 180° (because they are too bright!)
        b = this.correctBrightness(h, b, {centerHue: 150, amp: 75, width: 140});
        console.log('- correct brigtness to '+b);

        var data = {
            'on': true,
            'hue': Math.round(h / 360 * 65535),
            'bri': Math.round(b / 100 * 255),
            'sat': Math.round(s / 100 * 255)
        };

        var response = this.hueConnect.api('lights/' + this.lightIndex + "/state", data);
        this.updateResponseData(response);
        $(window).trigger('hue-change', this);
        return this;
    };

    this.updateResponseData = function (response) {
        var that = this;
        var updateVars = ['hue', 'sat', 'bri', 'on'];

        $.each(response, function (k, v) {
            if (v.success) {
                var pName = Object.keys(v.success)[0];
                var pValue = v.success[pName];
                var pNameShort = pName.split('/').pop();

                if (updateVars.indexOf(pNameShort) != -1) {
                    that[pNameShort] = pValue;
                }
            }
        });
        return this;
    };

    this.logColor = function (rgbObject) {
        var hsb = this.rgb2hsb(rgbObject);
        var color = 'rgb(' + rgbObject.r + ', ' + rgbObject.g + ', ' + rgbObject.b + ')';
        console.log('logColor', color, rgbObject, hsb);
        $('body').append('
H: ' + Math.round(hsb.h) + '
B: ' + Math.round(hsb.b) + '

');
    };

    this.correctBrightness = function (currentHue, currentBrigtness, damperObject) {
        var damperValue = 100;

        if (currentHue > damperObject.centerHue - damperObject.width / 2 &&
            currentHue < damperObject.centerHue + damperObject.width / 2) {

            var rest = damperObject.centerHue % damperObject.width;
            var filterOffset = rest / damperObject.width; // rest
            var offset = 360 - (filterOffset * 360);

            var position = (360 / damperObject.width * currentHue) + offset; // in grad!

            damperValue = (damperObject.amp * (Math.cos(position * Math.PI / 180) + 1) / 2);
            damperValue = 100 - damperValue;
            if (damperValue == 0) {
                damperValue = 1;
            }
        }

        currentBrigtness = currentBrigtness / 100 * damperValue;
        return currentBrigtness;
    };

    this.rgb2hsb = function (rgb) {
        var hsb = {h: 0, s: 0, b: 0};
        var min = Math.min(rgb.r, rgb.g, rgb.b);
        var max = Math.max(rgb.r, rgb.g, rgb.b);
        var delta = max - min;
        hsb.b = max;
        hsb.s = max !== 0 ? 255 * delta / max : 0;
        if (hsb.s !== 0) {
            if (rgb.r === max) {
                hsb.h = (rgb.g - rgb.b) / delta;
            } else if (rgb.g === max) {
                hsb.h = 2 + (rgb.b - rgb.r) / delta;
            } else {
                hsb.h = 4 + (rgb.r - rgb.g) / delta;
            }
        } else {
            hsb.h = -1;
        }
        hsb.h *= 60;
        if (hsb.h < 0) {
            hsb.h += 360;
        }
        hsb.s *= 100 / 255;
        hsb.b *= 100 / 255;

        hsb.h = Math.round(hsb.h);
        hsb.s = Math.round(hsb.s);
        hsb.b = Math.round(hsb.b);
        return hsb;
    }

    this.hsb2rgb = function (hsb) {
        var rgb = {};

        var h = Math.round(hsb.h);
        var s = Math.round(hsb.s * 255 / 100);
        var v = Math.round(hsb.b * 255 / 100);
        if (s === 0) {
            rgb.r = rgb.g = rgb.b = v;
        } else {
            var t1 = v;
            var t2 = (255 - s) * v / 255;
            var t3 = (t1 - t2) * (h % 60) / 60;
            if (h === 360) h = 0;
            if (h < 60) {
                rgb.r = t1;
                rgb.b = t2;
                rgb.g = t2 + t3;
            }
            else if (h < 120) {
                rgb.g = t1;
                rgb.b = t2;
                rgb.r = t1 - t3;
            }
            else if (h < 180) {
                rgb.g = t1;
                rgb.r = t2;
                rgb.b = t2 + t3;
            }
            else if (h < 240) {
                rgb.b = t1;
                rgb.r = t2;
                rgb.g = t1 - t3;
            }
            else if (h < 300) {
                rgb.b = t1;
                rgb.g = t2;
                rgb.r = t2 + t3;
            }
            else if (h < 360) {
                rgb.r = t1;
                rgb.g = t2;
                rgb.b = t1 - t3;
            }
            else {
                rgb.r = 0;
                rgb.g = 0;
                rgb.b = 0;
            }
        }
        return {
            r: Math.round(rgb.r),
            g: Math.round(rgb.g),
            b: Math.round(rgb.b)
        };
    }

    return this;
};<commit_msg>Added Transitiontime<commit_after>var HueLight = function (hueConnect, index, data) {

    this.rawdata = data;
    this.hueConnect = hueConnect;

    this.lightIndex = index;
    this.name = data.name;

    this.hue = data.state.hue;
    this.sat = data.state.sat;
    this.bri = data.state.bri;
    this.on = data.state.on;
    this.reachable = data.state.reachable;

    this.getName = function () {
        return this.name;
    };

    this.getIndex = function() {
        return this.lightIndex;
    };

    this.getOn = function() {
        if (!this.reachable) return false;
        return this.on;
    };

    this.getCurrentColorAsHSB = function () {
        var hsb = {h: 0, s: 0, b: 0};
        hsb.h = Math.round(this.hue / 65535 * 360);
        hsb.s = Math.round(this.sat / 255 * 100);
        hsb.b = Math.round(this.bri / 255 * 100);
        return hsb;
    };

    this.getCurrentColorAsRGB = function () {
        var hsb = this.getCurrentColorAsHSB();
        var rgb = this.hsb2rgb(hsb);
        return rgb;
    };

    this.setOn = function(on) {
        var response = this.hueConnect.api('lights/' + this.lightIndex + "/state", {'on': on, 'transitiontime': 0});
        this.updateResponseData(response);
        $(window).trigger('hue-change', this);
    };

    this.setColorRGB = function (r, g, b) {
        console.log('Set Lamp '+this.getIndex()+' "'+this.getName()+'" to rgb('+r+','+g+','+b+')');
        var hsb = this.rgb2hsb({r: r, g: g, b: b})
        this.setColorHSB(hsb.h, hsb.s, hsb.b);
        return this;
    };

    this.setColorHSB = function (h, s, b) {
        console.log('Set Lamp '+this.getIndex()+' "'+this.getName()+'" to hsb('+h+','+s+','+b+')');
        // dim the values around 180° (because they are too bright!)
        b = this.correctBrightness(h, b, {centerHue: 150, amp: 75, width: 140});
        console.log('- correct brigtness to '+b);

        var data = {
            'on': true,
            'hue': Math.round(h / 360 * 65535),
            'bri': Math.round(b / 100 * 255),
            'sat': Math.round(s / 100 * 255),
            'transitiontime': 0
        };

        var response = this.hueConnect.api('lights/' + this.lightIndex + "/state", data);
        this.updateResponseData(response);
        $(window).trigger('hue-change', this);
        return this;
    };

    this.updateResponseData = function (response) {
        var that = this;
        var updateVars = ['hue', 'sat', 'bri', 'on'];

        $.each(response, function (k, v) {
            if (v.success) {
                var pName = Object.keys(v.success)[0];
                var pValue = v.success[pName];
                var pNameShort = pName.split('/').pop();

                if (updateVars.indexOf(pNameShort) != -1) {
                    that[pNameShort] = pValue;
                }
            }
        });
        return this;
    };

    this.logColor = function (rgbObject) {
        var hsb = this.rgb2hsb(rgbObject);
        var color = 'rgb(' + rgbObject.r + ', ' + rgbObject.g + ', ' + rgbObject.b + ')';
        console.log('logColor', color, rgbObject, hsb);
        $('body').append('
H: ' + Math.round(hsb.h) + '
B: ' + Math.round(hsb.b) + '

');
    };

    this.correctBrightness = function (currentHue, currentBrigtness, damperObject) {
        var damperValue = 100;

        if (currentHue > damperObject.centerHue - damperObject.width / 2 &&
            currentHue < damperObject.centerHue + damperObject.width / 2) {

            var rest = damperObject.centerHue % damperObject.width;
            var filterOffset = rest / damperObject.width; // rest
            var offset = 360 - (filterOffset * 360);

            var position = (360 / damperObject.width * currentHue) + offset; // in grad!

            damperValue = (damperObject.amp * (Math.cos(position * Math.PI / 180) + 1) / 2);
            damperValue = 100 - damperValue;
            if (damperValue == 0) {
                damperValue = 1;
            }
        }

        currentBrigtness = currentBrigtness / 100 * damperValue;
        return currentBrigtness;
    };

    this.rgb2hsb = function (rgb) {
        var hsb = {h: 0, s: 0, b: 0};
        var min = Math.min(rgb.r, rgb.g, rgb.b);
        var max = Math.max(rgb.r, rgb.g, rgb.b);
        var delta = max - min;
        hsb.b = max;
        hsb.s = max !== 0 ? 255 * delta / max : 0;
        if (hsb.s !== 0) {
            if (rgb.r === max) {
                hsb.h = (rgb.g - rgb.b) / delta;
            } else if (rgb.g === max) {
                hsb.h = 2 + (rgb.b - rgb.r) / delta;
            } else {
                hsb.h = 4 + (rgb.r - rgb.g) / delta;
            }
        } else {
            hsb.h = -1;
        }
        hsb.h *= 60;
        if (hsb.h < 0) {
            hsb.h += 360;
        }
        hsb.s *= 100 / 255;
        hsb.b *= 100 / 255;

        hsb.h = Math.round(hsb.h);
        hsb.s = Math.round(hsb.s);
        hsb.b = Math.round(hsb.b);
        return hsb;
    }

    this.hsb2rgb = function (hsb) {
        var rgb = {};

        var h = Math.round(hsb.h);
        var s = Math.round(hsb.s * 255 / 100);
        var v = Math.round(hsb.b * 255 / 100);
        if (s === 0) {
            rgb.r = rgb.g = rgb.b = v;
        } else {
            var t1 = v;
            var t2 = (255 - s) * v / 255;
            var t3 = (t1 - t2) * (h % 60) / 60;
            if (h === 360) h = 0;
            if (h < 60) {
                rgb.r = t1;
                rgb.b = t2;
                rgb.g = t2 + t3;
            }
            else if (h < 120) {
                rgb.g = t1;
                rgb.b = t2;
                rgb.r = t1 - t3;
            }
            else if (h < 180) {
                rgb.g = t1;
                rgb.r = t2;
                rgb.b = t2 + t3;
            }
            else if (h < 240) {
                rgb.b = t1;
                rgb.r = t2;
                rgb.g = t1 - t3;
            }
            else if (h < 300) {
                rgb.b = t1;
                rgb.g = t2;
                rgb.r = t2 + t3;
            }
            else if (h < 360) {
                rgb.r = t1;
                rgb.g = t2;
                rgb.b = t1 - t3;
            }
            else {
                rgb.r = 0;
                rgb.g = 0;
                rgb.b = 0;
            }
        }
        return {
            r: Math.round(rgb.r),
            g: Math.round(rgb.g),
            b: Math.round(rgb.b)
        };
    }

    return this;
};<|endoftext|>


<commit_before>//
// JMVC Controller for the SMArt bootstrap
//

RecordController = MVC.Controller.extend('record', {
  // NOT called automatically on init! Called from main controller setup
	// (workaround for IE issues
  // with include ordering -- ACCOUNT not being defined when this is called
	// automatically)
  setup: function(params) {
    var _this = this;

    ACCOUNT.get_recent_records(function(record_list) {
	    RecordController.set_recent_records(record_list);
	    });

    $('#current_patient').change(function() {
		$("LI.app").removeClass("greyed_out");
    	RecordController.RECORD_ID = $('SELECT#current_patient OPTION:selected').attr("value");
    	_this._load_record();
    	return false;
    });},

  _load_record: function() {
      var _this = this;
      var record_id = RecordController.RECORD_ID;

	  var exists = $('SELECT#current_patient OPTION[value=\''+record_id+'\']');

	  // if the 'new' selection ain't new, we're done here.
	  if (	exists.attr("selected") &&  record_id == RecordController.CURRENT_RECORD.record_id ) {
		  return;
	  }

	  var after_record_obtained = function(record) {
		  $("#header").flash( $("#header").css("color"), 500 );
    	  _this.record = record;
    	  RecordController.CURRENT_RECORD = record;

    	  $("#select_patient_warning").html(" patient: "+record.label+"");

    	  if (exists.length === 1)
    	  {
    		  // detach doesn't exist in jQuery 1.3 :-(
    		  // exists.detach();
    		  // $('SELECT#current_patient').prepend(exists);
        	  exists.remove()
    	  }
		  exists = $("<option value='"+record.record_id+"'>"+record.label+"</option>");
		  $('SELECT#current_patient').prepend(exists);

    	  $('SELECT#current_patient OPTION:selected').each(function() {$(this).removeAttr("selected");});
		  exists.attr('selected', 'selected');

          // If there was an app open on the old record, open it automatically
			// on the new one.
          if (RecordController.APP_ID) {
        	  $('[href=\'#'+RecordController.APP_ID.replace(/@/, '_at_').replace(/\./g,'_')+'\']').click();
          }
          $('#current_patient').change();

	  };

	  var already_obtained = RecordController.RECENT_RECORDS[record_id];
	  if (already_obtained)
		  after_record_obtained(already_obtained);
      else
    	  Record.get(record_id,after_record_obtained);
  },

});

RecordController.set_recent_records= function(rlist) {
    RecordController.recent_records_list = rlist;
    RecordController.RECENT_RECORDS = {};
    $(rlist).each(function(i, record) {
    RecordController.RECENT_RECORDS[record.record_id] = record;
    });
};<commit_msg>Reload apps when patient changes<commit_after>//
// JMVC Controller for the SMArt bootstrap
//

RecordController = MVC.Controller.extend('record', {
  // NOT called automatically on init! Called from main controller setup
	// (workaround for IE issues
  // with include ordering -- ACCOUNT not being defined when this is called
	// automatically)
  setup: function(params) {
    var _this = this;

    ACCOUNT.get_recent_records(function(record_list) {
	    RecordController.set_recent_records(record_list);
	    });

    $('#current_patient').change(function() {
		$("LI.app").removeClass("greyed_out");
    	RecordController.RECORD_ID = $('SELECT#current_patient OPTION:selected').attr("value");
    	_this._load_record();
    	return false;
    });},

  _load_record: function() {
      var _this = this;
      var record_id = RecordController.RECORD_ID;

	  var exists = $('SELECT#current_patient OPTION[value=\''+record_id+'\']');

	  // if the 'new' selection ain't new, we're done here.
	  if (	exists.attr("selected") &&  record_id == RecordController.CURRENT_RECORD.record_id ) {
		  return;
	  }

	  var after_record_obtained = function(record) {
		  $("#header").flash( $("#header").css("color"), 500 );
    	  _this.record = record;
    	  RecordController.CURRENT_RECORD = record;

    	  $("#select_patient_warning").html(" patient: "+record.label+"");

    	  if (exists.length === 1)
    	  {
    		  // detach doesn't exist in jQuery 1.3 :-(
    		  // exists.detach();
    		  // $('SELECT#current_patient').prepend(exists);
        	  exists.remove()
    	  }
		  exists = $("<option value='"+record.record_id+"'>"+record.label+"</option>");
		  $('SELECT#current_patient').prepend(exists);

    	  $('SELECT#current_patient OPTION:selected').each(function() {$(this).removeAttr("selected");});
		  exists.attr('selected', 'selected');

          // If there was an app open on the old record, open it automatically
			// on the new one.
          if (RecordController.APP_ID) {
        		var app = $.grep(PHAController.phas, function(pha) {return (pha.id === RecordController.APP_ID);})[0];
        		PHAController.dispatch('launch_app', app);
          }
          $('#current_patient').change();

	  };

	  var already_obtained = RecordController.RECENT_RECORDS[record_id];
	  if (already_obtained)
		  after_record_obtained(already_obtained);
      else
    	  Record.get(record_id,after_record_obtained);
  },

});

RecordController.set_recent_records= function(rlist) {
    RecordController.recent_records_list = rlist;
    RecordController.RECENT_RECORDS = {};
    $(rlist).each(function(i, record) {
    RecordController.RECENT_RECORDS[record.record_id] = record;
    });
};<|endoftext|>


<commit_before>// Requires
var gulp = require('gulp');
var autoprefixer = require('autoprefixer');
var browserSync = require('browser-sync').create();
var changed = require('gulp-changed');
var concat = require('gulp-concat');
var cssnano = require('cssnano');
var data = require('gulp-data');
var del = require('del');
var fs = require('fs');
var imagemin = require('gulp-imagemin');
var plumber = require('gulp-plumber');
var postcss = require('gulp-postcss');
var rename = require('gulp-rename');
var sass = require('gulp-sass');
var svgSprite = require('gulp-svg-sprite');
var twig = require('gulp-twig');
var uglify = require('gulp-uglify');

// BrowserSync reload
function reload(cb) {
  browserSync.reload();
  cb();
}

// Get data from JSON
function getData() {
  var data = JSON.parse(fs.readFileSync('./src/data.json', 'utf8'));
  return data;
};

// CSS Task
gulp.task('css', function() {
  var processors = [
    autoprefixer({
      browsers: [
        '> 1%',
        'last 2 versions',
        'firefox ESR',
        'ie >= 10',
        'edge >= 12',
        'safari >= 7',
        'ios >= 7',
        'android >= 4.4',
        'ie_mob >= 10'
      ]
    }),
    cssnano({
      autoprefixer: false,
      safe: true
    })
  ];

  return gulp.src('src/css/**/*.scss')
    .pipe(plumber())
    .pipe(sass().on('error', sass.logError))
    .pipe(postcss(processors))
    .pipe(rename({ suffix: '.min' }))
    .pipe(gulp.dest('dist/css'))
    .pipe(browserSync.stream());
});

// JS Task
gulp.task('js', function() {
  return gulp.src([
      'node_modules/svg4everybody/dist/svg4everybody.min.js',
      'node_modules/jquery/dist/jquery.slim.min.js',
      'src/js/**/*.js'
    ])
    .pipe(plumber())
    .pipe(concat('main.min.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/js'));
});

// HTML Task
gulp.task('html', function() {
  return gulp.src('src/*.html')
    .pipe(plumber())
    .pipe(data(getData()))
    .pipe(twig())
    .pipe(gulp.dest('dist'));
});

// Images Task
gulp.task('images', function() {
  return gulp.src('src/img/**/*')
    .pipe(changed('dist/img'))
    .pipe(imagemin({
      progressive: true,
      interlaced: true
    }))
    .pipe(gulp.dest('dist/img'));
});

// Sprites Task
gulp.task('sprites', function() {
  return gulp.src('src/icons/**/*.svg')
    .pipe(svgSprite({
      mode: {
        symbol: {
          dest: '.',
          sprite: 'icons.svg'
        }
      }
    }))
    .pipe(gulp.dest('dist/sprites'));
});

// Fonts Task
gulp.task('fonts', function() {
  return gulp.src('src/fonts/**/*')
    .pipe(changed('dist/fonts'))
    .pipe(gulp.dest('dist/fonts'));
});

// Clean Task
gulp.task('clean', function() {
  return del(['dist']);
});

// Serve task
gulp.task('serve', function() {
  browserSync.init({
    notify: false,
    server: 'dist'
  });
});

// Watch task
gulp.task('watch', function() {
	gulp.watch('src/css/**/*.scss', gulp.series('css'));
	gulp.watch('src/js/**/*.js', gulp.series('js', reload));
	gulp.watch(['src/**/*.html', 'src/**/*.json'], gulp.series('html', reload));
	gulp.watch('src/img/**/*', gulp.series('images'));
  gulp.watch('src/sprites/**/*.svg', gulp.series('sprites'));
	gulp.watch('src/fonts/**/*', gulp.series('fonts'));
});

// Default Task
gulp.task('default',
  gulp.parallel('css', 'js', 'html', 'images', 'sprites', 'fonts')
);

// Dev task : build, serve and watch
gulp.task('dev',
  gulp.series(
    'default',
    gulp.parallel('serve', 'watch')
  )
);

// Build task : clean and build
gulp.task('build',
  gulp.series('clean', 'default')
);
<commit_msg>Fix sprites path<commit_after>// Requires
var gulp = require('gulp');
var autoprefixer = require('autoprefixer');
var browserSync = require('browser-sync').create();
var changed = require('gulp-changed');
var concat = require('gulp-concat');
var cssnano = require('cssnano');
var data = require('gulp-data');
var del = require('del');
var fs = require('fs');
var imagemin = require('gulp-imagemin');
var plumber = require('gulp-plumber');
var postcss = require('gulp-postcss');
var rename = require('gulp-rename');
var sass = require('gulp-sass');
var svgSprite = require('gulp-svg-sprite');
var twig = require('gulp-twig');
var uglify = require('gulp-uglify');

// BrowserSync reload
function reload(cb) {
  browserSync.reload();
  cb();
}

// Get data from JSON
function getData() {
  var data = JSON.parse(fs.readFileSync('./src/data.json', 'utf8'));
  return data;
};

// CSS Task
gulp.task('css', function() {
  var processors = [
    autoprefixer({
      browsers: [
        '> 1%',
        'last 2 versions',
        'firefox ESR',
        'ie >= 10',
        'edge >= 12',
        'safari >= 7',
        'ios >= 7',
        'android >= 4.4',
        'ie_mob >= 10'
      ]
    }),
    cssnano({
      autoprefixer: false,
      safe: true
    })
  ];

  return gulp.src('src/css/**/*.scss')
    .pipe(plumber())
    .pipe(sass().on('error', sass.logError))
    .pipe(postcss(processors))
    .pipe(rename({ suffix: '.min' }))
    .pipe(gulp.dest('dist/css'))
    .pipe(browserSync.stream());
});

// JS Task
gulp.task('js', function() {
  return gulp.src([
      'node_modules/svg4everybody/dist/svg4everybody.min.js',
      'node_modules/jquery/dist/jquery.slim.min.js',
      'src/js/**/*.js'
    ])
    .pipe(plumber())
    .pipe(concat('main.min.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/js'));
});

// HTML Task
gulp.task('html', function() {
  return gulp.src('src/*.html')
    .pipe(plumber())
    .pipe(data(getData()))
    .pipe(twig())
    .pipe(gulp.dest('dist'));
});

// Images Task
gulp.task('images', function() {
  return gulp.src('src/img/**/*')
    .pipe(changed('dist/img'))
    .pipe(imagemin({
      progressive: true,
      interlaced: true
    }))
    .pipe(gulp.dest('dist/img'));
});

// Sprites Task
gulp.task('sprites', function() {
  return gulp.src('src/icons/**/*.svg')
    .pipe(svgSprite({
      mode: {
        symbol: {
          dest: '.',
          sprite: 'icons.svg'
        }
      }
    }))
    .pipe(gulp.dest('dist/sprites'));
});

// Fonts Task
gulp.task('fonts', function() {
  return gulp.src('src/fonts/**/*')
    .pipe(changed('dist/fonts'))
    .pipe(gulp.dest('dist/fonts'));
});

// Clean Task
gulp.task('clean', function() {
  return del(['dist']);
});

// Serve task
gulp.task('serve', function() {
  browserSync.init({
    notify: false,
    server: 'dist'
  });
});

// Watch task
gulp.task('watch', function() {
	gulp.watch('src/css/**/*.scss', gulp.series('css'));
	gulp.watch('src/js/**/*.js', gulp.series('js', reload));
	gulp.watch(['src/**/*.html', 'src/**/*.json'], gulp.series('html', reload));
	gulp.watch('src/img/**/*', gulp.series('images'));
  gulp.watch('src/icons/**/*.svg', gulp.series('sprites'));
	gulp.watch('src/fonts/**/*', gulp.series('fonts'));
});

// Default Task
gulp.task('default',
  gulp.parallel('css', 'js', 'html', 'images', 'sprites', 'fonts')
);

// Dev task : build, serve and watch
gulp.task('dev',
  gulp.series(
    'default',
    gulp.parallel('serve', 'watch')
  )
);

// Build task : clean and build
gulp.task('build',
  gulp.series('clean', 'default')
);
<|endoftext|>


<commit_before>const { Collection } = require('../structures/all.js');
const { defaults } = require('./templates.js');

class Table {
  constructor(table) {
    this.remote = table;
    this.name = table.getTableName();
    this.entries = new Collection();
    this.sync();
  }

  async sync() {
    await this.remote.sync();
    const entries = await this.remote.all();
    for (const entry of entries) this.entries.set(entry.id, entry);
  }

  get(id) {
    return this.entries.get(id) || this.set(id);
  }

  set(id, obj = {}) {
    const entry = this.entries.get(id) || this.create(id);
    entry.set(obj);
    this.entries.set(id, entry);
    entry.save().catch(e => e);
    return entry;
  }

  create(id) {
    const obj = Object.assign({ id }, defaults[this.name]);
    return this.remote.build(obj);
  }

  delete(id) {
    if (this.entries.has(id)) {
      this.entries.get(id).destroy();
      this.entries.delete(id);
    }
  }
}

module.exports = Table;
<commit_msg>Remove Table.js<commit_after><|endoftext|>


<commit_before>/**
 * Created by AlexanderC on 5/25/15.
 */

'use strict';

/**
 * Request received by the lambda context
 */
export class Request {
  /**
   * @param {*} data
   */
  constructor(data) {
    this._data = data || {};

    this._registerDataAsParams();
  }

  /**
   * @private
   */
  _registerDataAsParams() {
    for (let key in this._data) {
      if (!this._data.hasOwnProperty(key)) {
        continue;
      }

      Object.defineProperty(this, key, {
        value: this._data[key],
        writable: false,
        configurable: false,
        enumerable: true
      });
    }
  }

  /**
   * @returns {*}
   */
  get data() {
    return this._data;
  }

  /**
   * @param {String} name
   * @param {String|Object|null} defaultValue
   *
   * @returns {String|Object|null}
   */
  getParam(name, defaultValue = undefined) {
    return this._data.hasOwnProperty(name) ? this._data[name] : defaultValue;
  }
}
<commit_msg>Avoid _data key listing on Object.keys(request)<commit_after>/**
 * Created by AlexanderC on 5/25/15.
 */

'use strict';

/**
 * Request received by the lambda context
 */
export class Request {
  /**
   * @param {*} data
   */
  constructor(data) {
    this._data = data || {};

    this._registerDataAsParams();
  }

  /**
   * @private
   */
  _registerDataAsParams() {
    for (let key in this._data) {
      if (!this._data.hasOwnProperty(key)) {
        continue;
      }

      Object.defineProperty(this, key, {
        value: this._data[key],
        writable: false,
        configurable: false,
        enumerable: true,
      });
    }

    // Avoid _data key listing on Object.keys(request)
    Object.defineProperty(this, '_data', {
      configurable: false,
      enumerable: true,
    });
  }

  /**
   * @returns {*}
   */
  get data() {
    return this._data;
  }

  /**
   * @param {String} name
   * @param {String|Object|null} defaultValue
   *
   * @returns {String|Object|null}
   */
  getParam(name, defaultValue = undefined) {
    return this._data.hasOwnProperty(name) ? this._data[name] : defaultValue;
  }
}
<|endoftext|>


<commit_before>import Global from './global';

// TODO want to handle default slot
// FIXME issue with the recreating elements

var Component = function () {
	this.data = {};
};

Component.prototype.renderSlot = function (element, component) {
	var slots = component.fragment.querySelectorAll('slot');
	for (var i = 0, l = slots.length; i < l; i++) {
		var name = slots[i].getAttribute('name');
		var slot = element.querySelector('[slot="'+ name + '"]');
		if (slot) slots[i].parentElement.replaceChild(slot, slots[i]);
	}
};

Component.prototype.renderStyle = function (element, component, callback) {
	var self = this;

	if (!component.style || component.styleReady) {
		return callback ? callback() : undefined;
	}

	if (typeof component.style === 'function') {
		return component.style(function (s) {
			component.style = s;
			self.renderStyle(element, component, callback);
		});
	}

	if (typeof component.style === 'string') {
		var style = component.style;

		if (!window.CSS || !window.CSS.supports) {

			if (!window.CSS.supports('(--t: black)')) {
				var matches = data.match(/--\w+(?:-+\w+)*:\s*.*?;/g);
				matches.forEach(function (match) {
					var rule = match.match(/(--\w+(?:-+\w+)*):\s*(.*?);/);
					var pattern = new RegExp('var\\('+rule[1]+'\\)', 'g');
					style = style.replace(rule[0], '');
					style = style.replace(pattern, rule[2]);
				});
			}

			if (!window.CSS.supports(':scope')) {
				style = style.replace(/\:scope/g, '[o-scope="' + element.scope + '"]');
			}

		}

		var estyle = document.createElement('style');
		var nstyle = document.createTextNode(style);
		estyle.appendChild(nstyle);
		component.fragment.appendChild(estyle);
	} else if (typeof component.style === 'object') {
		component.fragment.appendChild(component.style);
	}

	component.styleReady = true;

	return callback ? callback() : undefined;
};

Component.prototype.renderTemplate = function (element, component, callback) {
	var self = this;

	console.log(element.scope);
	console.log(!component.template || component.templateReady);
	console.log('\n');

	if (!component.template || component.templateReady) {
		return callback ? callback() : undefined;
	}

	if (typeof component.template === 'function') {
		return component.template(function (t) {
			component.template = t;
			self.renderTemplate(element, component, callback);
		});
	}

	if (typeof component.template === 'string') {
		var template = document.createElement('div');
		template.innerHTML = component.template;
		while (template.firstChild) {
			component.fragment.appendChild(template.firstChild);
		}
	} else if (typeof component.template === 'object') {
		component.fragment.appendChild(component.template);
	}

	component.templateReady = true;

	return callback ? callback() : undefined;
};

Component.prototype.created = function (element, component) {
	var self = this;
	var scope = component.name + '-' + component.count++;

	Object.defineProperty(element, 'scope', {
		enumerable: true,
		value: scope
	});

	element.setAttribute('o-scope', scope);
	Global.model.set(scope, component.model || {});
	Global.methods.data[scope] = component.methods;

	self.renderStyle(element, component, function () {
		self.renderTemplate(element, component, function () {

			if (component.shadow && 'attachShadow' in document.body) {
				element.attachShadow({ mode: 'open' }).appendChild(component.fragment.cloneNode(true));
			} else if (component.shadow && 'createShadowRoot' in document.body) {
				element.createShadowRoot().appendChild(component.fragment.cloneNode(true));
			} else {
				// self.renderSlot(element, component);
				// while (element.firstChild) element.removeChild(element.firstChild);
				element.appendChild(component.fragment.cloneNode(true));
			}

			if (component.created) {
				component.created.call(element);
			}

		});
	});

};

Component.prototype.define = function (options) {
	var self = this;

	if (!options.name) {
		throw new Error('Oxe.component.define - Requires name');
	}

	if (options.name in self.data) {
		throw new Error('Oxe.component.define - Component already defined');
	}

	self.data[options.name] = options;

	options.count = 0;
	options.ready = false;
	options.model = options.model || {};
	options.shadow = options.shadow || false;
	options.template = options.template || '';
	options.properties = options.properties || {};
	options.fragment = document.createDocumentFragment();

	options.properties.scope = {
		enumerable: true,
		configurable: true
	};

	options.properties.model = {
		enumerable: true,
		configurable: true,
		get: function () {
			return Global.model.get(this.scope);
		},
		set: function (data) {
			data = data && typeof data === 'object' ? data : {};
			return Global.model.set(this.scope, data);
		}
	};

	options.properties.methods = {
		enumerable: true,
		get: function () {
			return Global.methods.data[this.scope];
		}
	};

	options.proto = Object.create(HTMLElement.prototype, options.properties);

	options.proto.attachedCallback = options.attached;
	options.proto.detachedCallback = options.detached;
	options.proto.attributeChangedCallback = options.attributed;

	options.proto.createdCallback = function () {
		self.created(this, options);
	};

	document.registerElement(options.name, {
		prototype: options.proto
	});

};

export default Component;
<commit_msg>fixed model not ready<commit_after>import Global from './global';

// TODO want to handle default slot
// FIXME issue with the recreating elements

var Component = function () {
	this.data = {};
};

Component.prototype.renderSlot = function (element, component) {
	var slots = component.fragment.querySelectorAll('slot');

	for (var i = 0, l = slots.length; i < l; i++) {
		var name = slots[i].getAttribute('name');
		var slot = element.querySelector('[slot="'+ name + '"]');

		if (slot) {
			slots[i].parentElement.replaceChild(slot, slots[i]);
		}

	}
};

Component.prototype.renderStyle = function (element, component, callback) {
	var self = this;

	if (!component.style || component.styleReady) {
		return callback ? callback() : undefined;
	}

	if (typeof component.style === 'function') {
		return component.style(function (s) {
			component.style = s;
			self.renderStyle(element, component, callback);
		});
	}

	if (typeof component.style === 'string') {
		var style = component.style;

		if (!window.CSS || !window.CSS.supports) {

			if (!window.CSS.supports('(--t: black)')) {
				var matches = data.match(/--\w+(?:-+\w+)*:\s*.*?;/g);
				matches.forEach(function (match) {
					var rule = match.match(/(--\w+(?:-+\w+)*):\s*(.*?);/);
					var pattern = new RegExp('var\\('+rule[1]+'\\)', 'g');
					style = style.replace(rule[0], '');
					style = style.replace(pattern, rule[2]);
				});
			}

			if (!window.CSS.supports(':scope')) {
				style = style.replace(/\:scope/g, '[o-scope="' + element.scope + '"]');
			}

		}

		var estyle = document.createElement('style');
		var nstyle = document.createTextNode(style);

		estyle.appendChild(nstyle);
		component.fragment.appendChild(estyle);
	} else if (typeof component.style === 'object') {
		component.fragment.appendChild(component.style);
	}

	component.styleReady = true;

	return callback ? callback() : undefined;
};

Component.prototype.renderTemplate = function (element, component, callback) {
	var self = this;

	if (component.templateReady) {
		console.log('ready');
		return;
	}

	if (!component.template) {
	// if (!component.template || component.templateReady) {
		return callback ? callback() : undefined;
	}

	if (typeof component.template === 'function') {
		return component.template(function (t) {
			component.template = t;
			self.renderTemplate(element, component, callback);
		});
	}

	if (typeof component.template === 'string') {
		var template = document.createElement('div');
		template.innerHTML = component.template;
		while (template.firstChild) {
			component.fragment.appendChild(template.firstChild);
		}
	} else if (typeof component.template === 'object') {
		component.fragment.appendChild(component.template);
	}

	component.templateReady = true;

	return callback ? callback() : undefined;
};

Component.prototype.created = function (element, component) {
	var self = this;
	var scope = component.name + '-' + component.count++;

	Object.defineProperty(element, 'scope', {
		enumerable: true,
		value: scope
	});

	element.setAttribute('o-scope', scope);

	Global.model.ready(function () {

		Global.model.set(scope, component.model || {});
		Global.methods.data[scope] = component.methods;

		// self.renderStyle(element, component, function () {
			self.renderTemplate(element, component, function () {

				if (component.shadow && 'attachShadow' in document.body) {
					element.attachShadow({ mode: 'open' }).appendChild(component.fragment.cloneNode(true));
				} else if (component.shadow && 'createShadowRoot' in document.body) {
					element.createShadowRoot().appendChild(component.fragment.cloneNode(true));
				} else {
					self.renderSlot(element, component);
					while (element.firstChild) element.removeChild(element.firstChild);
					element.appendChild(component.fragment.cloneNode(true));
				}

				if (component.created) {
					component.created.call(element);
				}

			});
		// });
	});
};

Component.prototype.define = function (options) {
	var self = this;

	if (!options.name) {
		throw new Error('Oxe.component.define - Requires name');
	}

	if (options.name in self.data) {
		throw new Error('Oxe.component.define - Component already defined');
	}

	self.data[options.name] = options;

	options.count = 0;
	options.ready = false;
	options.model = options.model || {};
	options.shadow = options.shadow || false;
	options.template = options.template || '';
	options.properties = options.properties || {};
	options.fragment = document.createDocumentFragment();

	options.properties.scope = {
		enumerable: true,
		configurable: true
	};

	options.properties.model = {
		enumerable: true,
		configurable: true,
		get: function () {
			return Global.model.get(this.scope);
		},
		set: function (data) {
			data = data && typeof data === 'object' ? data : {};
			return Global.model.set(this.scope, data);
		}
	};

	options.properties.methods = {
		enumerable: true,
		get: function () {
			return Global.methods.data[this.scope];
		}
	};

	options.proto = Object.create(HTMLElement.prototype, options.properties);

	options.proto.attachedCallback = options.attached;
	options.proto.detachedCallback = options.detached;
	options.proto.attributeChangedCallback = options.attributed;

	options.proto.createdCallback = function () {
		self.created(this, options);
	};

	document.registerElement(options.name, {
		prototype: options.proto
	});

};

export default Component;
<|endoftext|>


<commit_before>import MethodDescriptor from 'src/method-descriptor'
import Request from 'src/request'
import EncodeJsonMiddleware, { CONTENT_TYPE_JSON } from 'src/middlewares/encode-json'

describe('Middleware / EncodeJson', () => {
  let methodDescriptor, request, body, middleware

  beforeEach(() => {
    body = { nice: 'object' }
    methodDescriptor = new MethodDescriptor({ method: 'post' })
    request = new Request(methodDescriptor, { body })
    middleware = EncodeJsonMiddleware()
  })

  it('exposes name', () => {
    expect(EncodeJsonMiddleware.name).toEqual('EncodeJsonMiddleware')
  })

  it('stringify the body and add "content-type" application/json', async () => {
    const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
    expect(newRequest.body()).toEqual(JSON.stringify(body))
    expect(newRequest.headers()['content-type']).toEqual(CONTENT_TYPE_JSON)
  })

  describe('when the request does not have a body', () => {
    it('returns the original request', async () => {
      request = new Request(new MethodDescriptor({ method: 'get' }))
      expect(request.body()).toBeUndefined()

      const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
      expect(newRequest.body()).toBeUndefined()
      expect(newRequest.headers()['content-type']).toBeUndefined()
    })
  })

  describe('when body is an invalid JSON', () => {
    beforeEach(() => {
      const data = {}
      data.attr = data
      body = data
      request = new Request(methodDescriptor, { body })
    })

    it('keeps the original request', async () => {
      const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
      expect(newRequest.body()).toEqual(body)
      expect(newRequest.headers()).toEqual({})
    })
  })

  describe('when there is already a content-type header set', () => {
    let headers

    beforeEach(() => {
      body = { something: 'strange' }
      headers = { 'content-type': 'application/java-archive' }
      methodDescriptor = new MethodDescriptor({ method: 'post' })
      request = new Request(methodDescriptor, { body, headers })
    })

    it('returns the original request', async () => {
      const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
      expect(newRequest.body()).toEqual(body)
      expect(newRequest.headers()).toEqual(headers)
    })

    describe('when the content-type is application/json', () => {
      let headers

      beforeEach(() => {
        headers = {'content-type': 'application/json'}
      })

      describe('and the body is already encoded', () => {
        let body

        beforeEach(() => {
          body = JSON.stringify({ foo: 'bar' })
          methodDescriptor = new MethodDescriptor({ method: 'post' })
          request = new Request(methodDescriptor, { body, headers })
        })

        it('returns the original request', async () => {
          const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
          expect(newRequest.body()).toEqual(body)
          expect(newRequest.headers()).toEqual(headers)
        })
      })

      describe('and the body is not encoded', () => {
        let body

        beforeEach(() => {
          body = { foo: 'bar' }
          methodDescriptor = new MethodDescriptor({ method: 'post' })
          request = new Request(methodDescriptor, { body, headers })
        })

        it('encodes the body but keeps the original header', async () => {
          const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
          expect(newRequest.body()).toEqual(JSON.stringify(body))
          expect(newRequest.headers()).toEqual(headers)
        })
      })
    })

    describe('when the content-type is application/json but with a different charset', () => {
      let headers, body

      beforeEach(() => {
        headers = { 'content-type': 'application/json;charset=utf-7' }
        body = 'strange-stuff'
        methodDescriptor = new MethodDescriptor({ method: 'post' })
        request = new Request(methodDescriptor, { body, headers })
      })

      it('keeps the original request', async () => {
        const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
        expect(newRequest.body()).toEqual(body)
        expect(newRequest.headers()).toEqual(headers)
      })
    })
  })
})
<commit_msg>fix lint<commit_after>import MethodDescriptor from 'src/method-descriptor'
import Request from 'src/request'
import EncodeJsonMiddleware, { CONTENT_TYPE_JSON } from 'src/middlewares/encode-json'

describe('Middleware / EncodeJson', () => {
  let methodDescriptor, request, body, middleware

  beforeEach(() => {
    body = { nice: 'object' }
    methodDescriptor = new MethodDescriptor({ method: 'post' })
    request = new Request(methodDescriptor, { body })
    middleware = EncodeJsonMiddleware()
  })

  it('exposes name', () => {
    expect(EncodeJsonMiddleware.name).toEqual('EncodeJsonMiddleware')
  })

  it('stringify the body and add "content-type" application/json', async () => {
    const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
    expect(newRequest.body()).toEqual(JSON.stringify(body))
    expect(newRequest.headers()['content-type']).toEqual(CONTENT_TYPE_JSON)
  })

  describe('when the request does not have a body', () => {
    it('returns the original request', async () => {
      request = new Request(new MethodDescriptor({ method: 'get' }))
      expect(request.body()).toBeUndefined()

      const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
      expect(newRequest.body()).toBeUndefined()
      expect(newRequest.headers()['content-type']).toBeUndefined()
    })
  })

  describe('when body is an invalid JSON', () => {
    beforeEach(() => {
      const data = {}
      data.attr = data
      body = data
      request = new Request(methodDescriptor, { body })
    })

    it('keeps the original request', async () => {
      const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
      expect(newRequest.body()).toEqual(body)
      expect(newRequest.headers()).toEqual({})
    })
  })

  describe('when there is already a content-type header set', () => {
    let headers

    beforeEach(() => {
      body = { something: 'strange' }
      headers = { 'content-type': 'application/java-archive' }
      methodDescriptor = new MethodDescriptor({ method: 'post' })
      request = new Request(methodDescriptor, { body, headers })
    })

    it('returns the original request', async () => {
      const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
      expect(newRequest.body()).toEqual(body)
      expect(newRequest.headers()).toEqual(headers)
    })

    describe('when the content-type is application/json', () => {
      let headers

      beforeEach(() => {
        headers = { 'content-type': 'application/json' }
      })

      describe('and the body is already encoded', () => {
        let body

        beforeEach(() => {
          body = JSON.stringify({ foo: 'bar' })
          methodDescriptor = new MethodDescriptor({ method: 'post' })
          request = new Request(methodDescriptor, { body, headers })
        })

        it('returns the original request', async () => {
          const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
          expect(newRequest.body()).toEqual(body)
          expect(newRequest.headers()).toEqual(headers)
        })
      })

      describe('and the body is not encoded', () => {
        let body

        beforeEach(() => {
          body = { foo: 'bar' }
          methodDescriptor = new MethodDescriptor({ method: 'post' })
          request = new Request(methodDescriptor, { body, headers })
        })

        it('encodes the body but keeps the original header', async () => {
          const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
          expect(newRequest.body()).toEqual(JSON.stringify(body))
          expect(newRequest.headers()).toEqual(headers)
        })
      })
    })

    describe('when the content-type is application/json but with a different charset', () => {
      let headers, body

      beforeEach(() => {
        headers = { 'content-type': 'application/json;charset=utf-7' }
        body = 'strange-stuff'
        methodDescriptor = new MethodDescriptor({ method: 'post' })
        request = new Request(methodDescriptor, { body, headers })
      })

      it('keeps the original request', async () => {
        const newRequest = await middleware.prepareRequest(() => Promise.resolve(request))
        expect(newRequest.body()).toEqual(body)
        expect(newRequest.headers()).toEqual(headers)
      })
    })
  })
})
<|endoftext|>


<commit_before>function setupTabViewsForTagPalette() {
	var currentTagGroupTabIndex = "0";

    $tab = $( "#tag-palette-panel" ).tabs({
         activate : function (e, ui) {
            currentTagGroupTabIndex = ui.newTab.index().toString();
            sessionStorage.setItem('tag-palette-tab-index', currentTagGroupTabIndex);
         }
    });

    if (sessionStorage.getItem('tag-palette-tab-index')) {
        currentTagGroupTabIndex = sessionStorage.getItem('tag-palette-tab-index');
        $tab.tabs('option', 'active', currentTagGroupTabIndex);
    }
}

function onExpandComplete() {
	setContextMenuForTags();
	setupDraggableForTags();
}

function showTagEditorForCreating() {
    var saveBtn = PF("tagEditorSaveBtn");
    saveBtn.disable();

    var inplace = PF("tagNameInplace");
    inplace.hide();
    inplace.jq.find(".ui-inplace-display").text(MSG.name_required);

    var dlg = PF("tagEditor");
    dlg.jq.off(".te")
    .on("keyup.te", ".ui-inplace-content input", function(e) {
        if ($(this).val().trim())
            saveBtn.enable();
        else
            saveBtn.disable();
    })
    .find("input[name=isNewTag]").val(true);
    dlg.show();
}

function showTagEditorForEditing() {
	var dlg = PF("tagEditor");

	var menu = $("#tag-context-menu");
	var target = menu.data("target-tag");
	var tagName = target.find(".each-tag-name").text() || target.text();
	if (tagName == "") {
		tagName = "???";
	}

	setupPfInplaceText(
	        PF("tagNameInplace")
	        , tagName
	        , dlg.jq.find("#tag-palette-form\\:tag-name-panel")
	);

	dlg.jq.find("input[name=isNewTag]").val(false);
	dlg.show();
}

function showTagInfo() {
    var dlg = PF("tagInfoDlg");
    dlg.show();

    var menu = $("#tag-context-menu");
    var target = menu.data("target-tag");
    var tagName = target.find(".each-tag-name").text() || target.text();
    if (tagName == "") {
        tagName = "???";
    }

    dlg.jq.find("._tag-name").text(" "+tagName);
}
<commit_msg>[bug fix] heirarchy information of another tag appeared on the tag creating editor;<commit_after>function setupTabViewsForTagPalette() {
	var currentTagGroupTabIndex = "0";

    $tab = $( "#tag-palette-panel" ).tabs({
         activate : function (e, ui) {
            currentTagGroupTabIndex = ui.newTab.index().toString();
            sessionStorage.setItem('tag-palette-tab-index', currentTagGroupTabIndex);
         }
    });

    if (sessionStorage.getItem('tag-palette-tab-index')) {
        currentTagGroupTabIndex = sessionStorage.getItem('tag-palette-tab-index');
        $tab.tabs('option', 'active', currentTagGroupTabIndex);
    }
}

function onExpandComplete() {
	setContextMenuForTags();
	setupDraggableForTags();
}

function showTagEditorForCreating() {
    var saveBtn = PF("tagEditorSaveBtn");
    saveBtn.disable();

    var inplace = PF("tagNameInplace");
    inplace.hide();
    inplace.jq.find(".ui-inplace-display").text(MSG.name_required);

    var dlg = PF("tagEditor");
    dlg.jq.off(".te")
    .on("keyup.te", ".ui-inplace-content input", function(e) {
        if ($(this).val().trim())
            saveBtn.enable();
        else
            saveBtn.disable();
    })
    .find("input[name=isNewTag]").val(true);
    $("#tag-palette-form\\:parent-tags").empty();
    $("#tag-palette-form\\:child-tags").empty();
    dlg.show();
}

function showTagEditorForEditing() {
	var dlg = PF("tagEditor");

	var menu = $("#tag-context-menu");
	var target = menu.data("target-tag");
	var tagName = target.find(".each-tag-name").text() || target.text();
	if (tagName == "") {
		tagName = "???";
	}

	setupPfInplaceText(
	        PF("tagNameInplace")
	        , tagName
	        , dlg.jq.find("#tag-palette-form\\:tag-name-panel")
	);

	dlg.jq.find("input[name=isNewTag]").val(false);
	dlg.show();
}

function showTagInfo() {
    var dlg = PF("tagInfoDlg");
    dlg.show();

    var menu = $("#tag-context-menu");
    var target = menu.data("target-tag");
    var tagName = target.find(".each-tag-name").text() || target.text();
    if (tagName == "") {
        tagName = "???";
    }

    dlg.jq.find("._tag-name").text(" "+tagName);
}
<|endoftext|>


<commit_before>/* eslint-env jasmine */
import gitGraphAst from './gitGraphAst';
import { parser } from './parser/gitGraph';

describe('when parsing a gitGraph', function() {
  beforeEach(function() {
    parser.yy = gitGraphAst;
    parser.yy.clear();
  });
  it('should handle a gitGraph defintion', function() {
    const str = 'gitGraph:\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should handle a gitGraph defintion with empty options', function() {
    const str = 'gitGraph:\n' + 'options\n' + 'end\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(parser.yy.getOptions()).toEqual({});
    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should handle a gitGraph defintion with valid options', function() {
    const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"}\n' + 'end\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();
    expect(parser.yy.getOptions()['key']).toBe('value');
    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should not fail on a gitGraph with malformed json', function() {
    const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"\n' + 'end\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should handle set direction', function() {
    const str = 'gitGraph BT:\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('BT');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should checkout a branch', function() {
    const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(0);
    expect(parser.yy.getCurrentBranch()).toBe('new');
  });

  it('should add commits to checked out branch', function() {
    const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(2);
    expect(parser.yy.getCurrentBranch()).toBe('new');
    const branchCommit = parser.yy.getBranches()['new'];
    expect(branchCommit).not.toBeNull();
    expect(commits[branchCommit].parent).not.toBeNull();
  });
  it('should handle commit with args', function() {
    const str = 'gitGraph:\n' + 'commit "a commit"\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(1);
    const key = Object.keys(commits)[0];
    expect(commits[key].message).toBe('a commit');
    expect(parser.yy.getCurrentBranch()).toBe('master');
  });

  it('it should reset a branch', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'reset master\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
  });

  it('reset can take an argument', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'reset master^\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    const master = commits[parser.yy.getBranches()['master']];
    expect(parser.yy.getHead().id).toEqual(master.parent);
  });

  it('it should handle fast forwardable merges', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'checkout master\n' +
      'merge newbranch\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
  });

  it('it should handle cases when merge is a noop', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'merge master\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
  });

  it('it should handle merge with 2 parents', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'checkout master\n' +
      'commit\n' +
      'merge newbranch\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(5);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
  });

  it('it should handle ff merge when history walk has two parents (merge commit)', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'checkout master\n' +
      'commit\n' +
      'merge newbranch\n' +
      'commit\n' +
      'checkout newbranch\n' +
      'merge master\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(6);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);

    parser.yy.prettyPrint();
  });
});
<commit_msg>#848 - Add Unit Test<commit_after>/* eslint-env jasmine */
import gitGraphAst from './gitGraphAst';
import { parser } from './parser/gitGraph';
import randomString from 'crypto-random-string';
import cryptoRandomString from 'crypto-random-string';

jest.mock('crypto-random-string');

describe('when parsing a gitGraph', function() {
  let i = 0;
  beforeEach(function() {
    parser.yy = gitGraphAst;
    parser.yy.clear();
    cryptoRandomString.mockImplementation(() => {
      i = i + 1;
      return String(i);
    });
  });
  it('should handle a gitGraph defintion', function() {
    const str = 'gitGraph:\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should handle a gitGraph defintion with empty options', function() {
    const str = 'gitGraph:\n' + 'options\n' + 'end\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(parser.yy.getOptions()).toEqual({});
    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should handle a gitGraph defintion with valid options', function() {
    const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"}\n' + 'end\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();
    expect(parser.yy.getOptions()['key']).toBe('value');
    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should not fail on a gitGraph with malformed json', function() {
    const str = 'gitGraph:\n' + 'options\n' + '{"key": "value"\n' + 'end\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('LR');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should handle set direction', function() {
    const str = 'gitGraph BT:\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(1);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getDirection()).toBe('BT');
    expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
  });

  it('should checkout a branch', function() {
    const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(0);
    expect(parser.yy.getCurrentBranch()).toBe('new');
  });

  it('should add commits to checked out branch', function() {
    const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n' + 'commit\n' + 'commit\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(2);
    expect(parser.yy.getCurrentBranch()).toBe('new');
    const branchCommit = parser.yy.getBranches()['new'];
    expect(branchCommit).not.toBeNull();
    expect(commits[branchCommit].parent).not.toBeNull();
  });
  it('should handle commit with args', function() {
    const str = 'gitGraph:\n' + 'commit "a commit"\n';

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits).length).toBe(1);
    const key = Object.keys(commits)[0];
    expect(commits[key].message).toBe('a commit');
    expect(parser.yy.getCurrentBranch()).toBe('master');
  });

  it('it should reset a branch', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'reset master\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
  });

  it('reset can take an argument', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'reset master^\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    const master = commits[parser.yy.getBranches()['master']];
    expect(parser.yy.getHead().id).toEqual(master.parent);
  });

  it('it should handle fast forwardable merges', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'checkout master\n' +
      'merge newbranch\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
  });

  it('it should handle cases when merge is a noop', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'merge master\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(3);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['newbranch']);
  });

  it('it should handle merge with 2 parents', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'checkout master\n' +
      'commit\n' +
      'merge newbranch\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(5);
    expect(parser.yy.getCurrentBranch()).toBe('master');
    expect(parser.yy.getBranches()['newbranch']).not.toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);
  });

  it('it should handle ff merge when history walk has two parents (merge commit)', function() {
    const str =
      'gitGraph:\n' +
      'commit\n' +
      'branch newbranch\n' +
      'checkout newbranch\n' +
      'commit\n' +
      'commit\n' +
      'checkout master\n' +
      'commit\n' +
      'merge newbranch\n' +
      'commit\n' +
      'checkout newbranch\n' +
      'merge master\n';

    parser.parse(str);

    const commits = parser.yy.getCommits();
    expect(Object.keys(commits).length).toBe(6);
    expect(parser.yy.getCurrentBranch()).toBe('newbranch');
    expect(parser.yy.getBranches()['newbranch']).toEqual(parser.yy.getBranches()['master']);
    expect(parser.yy.getHead().id).toEqual(parser.yy.getBranches()['master']);

    parser.yy.prettyPrint();
  });

  it('it should generate a secure random ID for commits', () => {
    const str = 'gitGraph:\n' + 'commit\n' + 'commit\n';
    const EXPECTED_LENGTH = 7;
    const EXPECTED_CHARACTERS = '0123456789abcdef';

    let idCount = 0;
    randomString.mockImplementation(options => {
      if (
        options.length === EXPECTED_LENGTH &&
        options.characters === EXPECTED_CHARACTERS &&
        Object.keys(options).length === 2
      ) {
        const id = `abcdef${idCount}`;
        idCount += 1;
        return id;
      }
      return 'unexpected-ID';
    });

    parser.parse(str);
    const commits = parser.yy.getCommits();

    expect(Object.keys(commits)).toEqual(['abcdef0', 'abcdef1']);
    Object.keys(commits).forEach(key => {
      expect(commits[key].id).toEqual(key);
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Create markdown link with proper escaping
 */
export function markdownLink(title, url) {
	// Escape special characters
	title = title.replace(/[\[\]\*\`_\\]/g, c => '\\' + c).replace(/</g, '<');
	url = url.replace(/[\(\)]/g, escape);
	return '[' + title + '](' + url + ')';
}<commit_msg>Never produce empty Markdown links<commit_after>/**
 * Create markdown link with proper escaping
 */
export function markdownLink(title, url) {
	if (title === '') {
		title = '(untitled link)';
	}
	// Escape special characters
	title = title.replace(/[\[\]\*\`_\\]/g, c => '\\' + c).replace(/</g, '<');
	url = url.replace(/[\(\)]/g, escape);
	return '[' + title + '](' + url + ')';
}<|endoftext|>